Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH] _pkg_str: add _db attribute (bug 640318)
Date: Sat, 07 Apr 2018 06:36:55
Message-Id: 20180407063354.29021-1-zmedico@gentoo.org
1 In order to propagate information from dbapi to Package instance,
2 it's useful for each _pkg_str instance to have a _db attribute
3 which references the dbapi instance that instantiated it. Use a new
4 dbapi._iuse_implicit_cnstr method to delegate implicit IUSE logic to
5 the dbapi instance, in order to make the behavior customizable at the
6 dbapi level for the purposes of bug 640318. This patch consists only
7 of refactoring, with no behavioral changes.
8
9 Bug: https://bugs.gentoo.org/640318
10 ---
11 pym/_emerge/Package.py | 53 +++++++++---------------------
12 pym/_emerge/depgraph.py | 8 ++++-
13 pym/_emerge/resolver/DbapiProvidesIndex.py | 3 +-
14 pym/portage/dbapi/__init__.py | 44 +++++++++++++++++++++----
15 pym/portage/dbapi/bintree.py | 15 +++++----
16 pym/portage/dbapi/porttree.py | 4 +--
17 pym/portage/dbapi/vartree.py | 5 +--
18 pym/portage/dbapi/virtual.py | 4 +--
19 pym/portage/versions.py | 6 ++--
20 9 files changed, 81 insertions(+), 61 deletions(-)
21
22 diff --git a/pym/_emerge/Package.py b/pym/_emerge/Package.py
23 index 791a35612..a7ce00bc9 100644
24 --- a/pym/_emerge/Package.py
25 +++ b/pym/_emerge/Package.py
26 @@ -67,8 +67,19 @@ class Package(Task):
27 if not self.built:
28 self._metadata['CHOST'] = self.root_config.settings.get('CHOST', '')
29 eapi_attrs = _get_eapi_attrs(self.eapi)
30 +
31 + try:
32 + db = self.cpv._db
33 + except AttributeError:
34 + if self.built:
35 + # For independence from the source ebuild repository and
36 + # profile implicit IUSE state, require the _db attribute
37 + # for built packages.
38 + raise
39 + db = self.root_config.trees['porttree'].dbapi
40 +
41 self.cpv = _pkg_str(self.cpv, metadata=self._metadata,
42 - settings=self.root_config.settings)
43 + settings=self.root_config.settings, db=db)
44 if hasattr(self.cpv, 'slot_invalid'):
45 self._invalid_metadata('SLOT.invalid',
46 "SLOT: invalid value: '%s'" % self._metadata["SLOT"])
47 @@ -82,17 +93,10 @@ class Package(Task):
48 # sync metadata with validated repo (may be UNKNOWN_REPO)
49 self._metadata['repository'] = self.cpv.repo
50
51 - if eapi_attrs.iuse_effective:
52 - implicit_match = self.root_config.settings._iuse_effective_match
53 - if self.built:
54 - implicit_match = functools.partial(
55 - self._built_iuse_effective_match,
56 - implicit_match, frozenset(self._metadata['USE'].split()))
57 - else:
58 - implicit_match = self.root_config.settings._iuse_implicit_match
59 + implicit_match = db._iuse_implicit_cnstr(self.cpv, self._metadata)
60 usealiases = self.root_config.settings._use_manager.getUseAliases(self)
61 - self.iuse = self._iuse(self, self._metadata["IUSE"].split(), implicit_match,
62 - usealiases, self.eapi)
63 + self.iuse = self._iuse(self, self._metadata["IUSE"].split(),
64 + implicit_match, usealiases, self.eapi)
65
66 if (self.iuse.enabled or self.iuse.disabled) and \
67 not eapi_attrs.iuse_defaults:
68 @@ -115,33 +119,6 @@ class Package(Task):
69 type_name=self.type_name)
70 self._hash_value = hash(self._hash_key)
71
72 - @staticmethod
73 - def _built_iuse_effective_match(prof_effective_match, built_use, flag):
74 - """
75 - For built packages, it is desirable for the built USE setting to be
76 - independent of the profile's current IUSE_IMPLICIT state, since the
77 - profile's IUSE_IMPLICT setting may have diverged. Therefore, any
78 - member of the built USE setting is considered to be a valid member of
79 - IUSE_EFFECTIVE. Note that the binary package may be remote, so it's
80 - only possible to rely on metadata that is available in the remote
81 - Packages file, and the IUSE_IMPLICIT header in the Packages file is
82 - vulnerable to mutation (see bug 640318).
83 -
84 - This function is only used for EAPIs that support IUSE_EFFECTIVE,
85 - since built USE settings for earlier EAPIs may contain a large
86 - number of irrelevant flags.
87 -
88 - @param prof_effective_match: function to match IUSE_EFFECTIVE
89 - values for the current profile
90 - @type prof_effective_match: callable
91 - @param built_use: built USE setting
92 - @type built_use: frozenset
93 - @return: True if flag is a valid USE value which may
94 - be specified in USE dependencies, False otherwise.
95 - @rtype: bool
96 - """
97 - return flag in built_use or prof_effective_match(flag)
98 -
99 @property
100 def eapi(self):
101 return self._metadata["EAPI"]
102 diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
103 index 963bf25f4..c435bc0b5 100644
104 --- a/pym/_emerge/depgraph.py
105 +++ b/pym/_emerge/depgraph.py
106 @@ -50,7 +50,7 @@ from portage.util.digraph import digraph
107 from portage.util._async.TaskScheduler import TaskScheduler
108 from portage.util._eventloop.EventLoop import EventLoop
109 from portage.util._eventloop.global_event_loop import global_event_loop
110 -from portage.versions import catpkgsplit
111 +from portage.versions import _pkg_str, catpkgsplit
112
113 from _emerge.AtomArg import AtomArg
114 from _emerge.Blocker import Blocker
115 @@ -6975,6 +6975,12 @@ class depgraph(object):
116 except KeyError:
117 raise portage.exception.PackageNotFound(cpv)
118
119 + # Ensure that this cpv is linked to the correct db, since the
120 + # caller might have passed in a cpv from a different db, in
121 + # order get an instance from this db with the same cpv.
122 + if getattr(cpv, '_db', None) is not db:
123 + cpv = _pkg_str(cpv, db=db)
124 +
125 pkg = Package(built=(type_name != "ebuild"), cpv=cpv,
126 installed=installed, metadata=metadata, onlydeps=onlydeps,
127 root_config=root_config, type_name=type_name)
128 diff --git a/pym/_emerge/resolver/DbapiProvidesIndex.py b/pym/_emerge/resolver/DbapiProvidesIndex.py
129 index 59ae71941..1650edd4e 100644
130 --- a/pym/_emerge/resolver/DbapiProvidesIndex.py
131 +++ b/pym/_emerge/resolver/DbapiProvidesIndex.py
132 @@ -24,7 +24,8 @@ class DbapiProvidesIndex(object):
133 _copy_attrs = ('aux_get', 'aux_update', 'categories', 'cpv_all',
134 'cpv_exists', 'cp_all', 'cp_list', 'getfetchsizes',
135 'settings', '_aux_cache_keys', '_clear_cache',
136 - '_cpv_sort_ascending', '_pkg_str', '_pkg_str_aux_keys')
137 + '_cpv_sort_ascending', '_iuse_implicit_cnstr', '_pkg_str',
138 + '_pkg_str_aux_keys')
139
140 def __init__(self, db):
141 self._db = db
142 diff --git a/pym/portage/dbapi/__init__.py b/pym/portage/dbapi/__init__.py
143 index c1b5d967d..d320cc75f 100644
144 --- a/pym/portage/dbapi/__init__.py
145 +++ b/pym/portage/dbapi/__init__.py
146 @@ -166,7 +166,7 @@ class dbapi(object):
147 metadata = dict(zip(self._pkg_str_aux_keys,
148 self.aux_get(cpv, self._pkg_str_aux_keys, myrepo=repo)))
149
150 - return _pkg_str(cpv, metadata=metadata, settings=self.settings)
151 + return _pkg_str(cpv, metadata=metadata, settings=self.settings, db=self)
152
153 def _iter_match_repo(self, atom, cpv_iter):
154 for cpv in cpv_iter:
155 @@ -216,16 +216,48 @@ class dbapi(object):
156
157 yield cpv
158
159 - def _match_use(self, atom, pkg, metadata, ignore_profile=False):
160 + def _iuse_implicit_cnstr(self, pkg, metadata):
161 + """
162 + Construct a callable that checks if a given USE flag should
163 + be considered to be a member of the implicit IUSE for the
164 + given package.
165 +
166 + @param pkg: package
167 + @type pkg: _pkg_str
168 + @param metadata: package metadata
169 + @type metadata: Mapping
170 + @return: a callable that accepts a single USE flag argument,
171 + and returns True only if the USE flag should be considered
172 + to be a member of the implicit IUSE for the given package.
173 + @rtype: callable
174 + """
175 eapi_attrs = _get_eapi_attrs(metadata["EAPI"])
176 if eapi_attrs.iuse_effective:
177 iuse_implicit_match = self.settings._iuse_effective_match
178 - if not self._use_mutable:
179 - iuse_implicit_match = functools.partial(
180 - Package._built_iuse_effective_match,
181 - iuse_implicit_match, frozenset(metadata["USE"].split()))
182 else:
183 iuse_implicit_match = self.settings._iuse_implicit_match
184 +
185 + if not self._use_mutable and eapi_attrs.iuse_effective:
186 + # For built packages, it is desirable for the built USE setting to
187 + # be independent of the profile's current IUSE_IMPLICIT state, since
188 + # the profile's IUSE_IMPLICT setting may have diverged. Therefore,
189 + # any member of the built USE setting is considered to be a valid
190 + # member of IUSE_EFFECTIVE. Note that the binary package may be
191 + # remote, so it's only possible to rely on metadata that is available
192 + # in the remote Packages file, and the IUSE_IMPLICIT header in the
193 + # Packages file is vulnerable to mutation (see bug 640318).
194 + #
195 + # This behavior is only used for EAPIs that support IUSE_EFFECTIVE,
196 + # since built USE settings for earlier EAPIs may contain a large
197 + # number of irrelevant flags.
198 + prof_iuse = iuse_implicit_match
199 + enabled = frozenset(metadata["USE"].split()).__contains__
200 + iuse_implicit_match = lambda flag: prof_iuse(flag) or enabled(flag)
201 +
202 + return iuse_implicit_match
203 +
204 + def _match_use(self, atom, pkg, metadata, ignore_profile=False):
205 + iuse_implicit_match = self._iuse_implicit_cnstr(pkg, metadata)
206 usealiases = self.settings._use_manager.getUseAliases(pkg)
207 iuse = Package._iuse(None, metadata["IUSE"].split(), iuse_implicit_match, usealiases, metadata["EAPI"])
208
209 diff --git a/pym/portage/dbapi/bintree.py b/pym/portage/dbapi/bintree.py
210 index fc48a6902..42d334d24 100644
211 --- a/pym/portage/dbapi/bintree.py
212 +++ b/pym/portage/dbapi/bintree.py
213 @@ -458,7 +458,7 @@ class binarytree(object):
214 if v is not None:
215 v = _unicode_decode(v)
216 metadata[k] = " ".join(v.split())
217 - mynewcpv = _pkg_str(mynewcpv, metadata=metadata)
218 + mynewcpv = _pkg_str(mynewcpv, metadata=metadata, db=self.dbapi)
219 new_path = self.getname(mynewcpv)
220 self._pkg_paths[
221 self.dbapi._instance_key(mynewcpv)] = new_path[len(self.pkgdir)+1:]
222 @@ -589,7 +589,7 @@ class binarytree(object):
223 basename_index = {}
224 for d in pkgindex.packages:
225 cpv = _pkg_str(d["CPV"], metadata=d,
226 - settings=self.settings)
227 + settings=self.settings, db=self.dbapi)
228 d["CPV"] = cpv
229 metadata[_instance_key(cpv)] = d
230 path = d.get("PATH")
231 @@ -755,8 +755,8 @@ class binarytree(object):
232 pkg_metadata.pop("CATEGORY")
233 pkg_metadata.pop("PF")
234 mycpv = _pkg_str(mycpv,
235 - metadata=self.dbapi._aux_cache_slot_dict(
236 - pkg_metadata))
237 + metadata=self.dbapi._aux_cache_slot_dict(pkg_metadata),
238 + db=self.dbapi)
239 pkg_paths[_instance_key(mycpv)] = mypath
240 self.dbapi.cpv_inject(mycpv)
241 update_pkgindex = True
242 @@ -1028,7 +1028,7 @@ class binarytree(object):
243 remote_base_uri = pkgindex.header.get("URI", base_url)
244 for d in pkgindex.packages:
245 cpv = _pkg_str(d["CPV"], metadata=d,
246 - settings=self.settings)
247 + settings=self.settings, db=self.dbapi)
248 # Local package instances override remote instances
249 # with the same instance_key.
250 if self.dbapi.cpv_exists(cpv):
251 @@ -1097,7 +1097,8 @@ class binarytree(object):
252 if self._remotepkgs is not None:
253 fetched = self._remotepkgs.pop(instance_key, None)
254
255 - cpv = _pkg_str(cpv, metadata=metadata, settings=self.settings)
256 + cpv = _pkg_str(cpv, metadata=metadata, settings=self.settings,
257 + db=self.dbapi)
258
259 # Reread the Packages index (in case it's been changed by another
260 # process) and then updated it, all while holding a lock.
261 @@ -1128,7 +1129,7 @@ class binarytree(object):
262 build_id = self._parse_build_id(basename)
263 metadata["BUILD_ID"] = _unicode(build_id)
264 cpv = _pkg_str(cpv, metadata=metadata,
265 - settings=self.settings)
266 + settings=self.settings, db=self.dbapi)
267 binpkg = portage.xpak.tbz2(full_path)
268 binary_data = binpkg.get_data()
269 binary_data[b"BUILD_ID"] = _unicode_encode(
270 diff --git a/pym/portage/dbapi/porttree.py b/pym/portage/dbapi/porttree.py
271 index 69e14dbcd..910e90e6f 100644
272 --- a/pym/portage/dbapi/porttree.py
273 +++ b/pym/portage/dbapi/porttree.py
274 @@ -945,7 +945,7 @@ class portdbapi(dbapi):
275 writemsg(_("\nInvalid ebuild version: %s\n") % \
276 os.path.join(oroot, mycp, x), noiselevel=-1)
277 continue
278 - d[_pkg_str(mysplit[0]+"/"+pf)] = None
279 + d[_pkg_str(mysplit[0]+"/"+pf, db=self)] = None
280 if invalid_category and d:
281 writemsg(_("\n!!! '%s' has a category that is not listed in " \
282 "%setc/portage/categories\n") % \
283 @@ -1070,7 +1070,7 @@ class portdbapi(dbapi):
284
285 try:
286 pkg_str = _pkg_str(cpv, metadata=metadata,
287 - settings=self.settings)
288 + settings=self.settings, db=self)
289 except InvalidData:
290 continue
291
292 diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py
293 index a136c38f1..c274248e3 100644
294 --- a/pym/portage/dbapi/vartree.py
295 +++ b/pym/portage/dbapi/vartree.py
296 @@ -466,7 +466,8 @@ class vardbapi(dbapi):
297 cpv = "%s/%s" % (mysplit[0], x)
298 metadata = dict(zip(self._aux_cache_keys,
299 self.aux_get(cpv, self._aux_cache_keys)))
300 - returnme.append(_pkg_str(cpv, metadata=metadata))
301 + returnme.append(_pkg_str(cpv, metadata=metadata,
302 + settings=self.settings, db=self))
303 self._cpv_sort_ascending(returnme)
304 if use_cache:
305 self.cpcache[mycp] = [mystat, returnme[:]]
306 @@ -520,7 +521,7 @@ class vardbapi(dbapi):
307 subpath = x + "/" + y
308 # -MERGING- should never be a cpv, nor should files.
309 try:
310 - subpath = _pkg_str(subpath)
311 + subpath = _pkg_str(subpath, db=self)
312 except InvalidData:
313 self.invalidentry(self.getpath(subpath))
314 continue
315 diff --git a/pym/portage/dbapi/virtual.py b/pym/portage/dbapi/virtual.py
316 index f2e841fcd..3f7e6c221 100644
317 --- a/pym/portage/dbapi/virtual.py
318 +++ b/pym/portage/dbapi/virtual.py
319 @@ -151,10 +151,10 @@ class fakedbapi(dbapi):
320 if mycp is None or \
321 (myslot is None and metadata is not None and metadata.get('SLOT')):
322 if metadata is None:
323 - mycpv = _pkg_str(mycpv)
324 + mycpv = _pkg_str(mycpv, db=self)
325 else:
326 mycpv = _pkg_str(mycpv, metadata=metadata,
327 - settings=self.settings)
328 + settings=self.settings, db=self)
329
330 mycp = mycpv.cp
331 try:
332 diff --git a/pym/portage/versions.py b/pym/portage/versions.py
333 index 7b6a57673..0c21373cc 100644
334 --- a/pym/portage/versions.py
335 +++ b/pym/portage/versions.py
336 @@ -363,12 +363,12 @@ class _pkg_str(_unicode):
337
338 def __new__(cls, cpv, metadata=None, settings=None, eapi=None,
339 repo=None, slot=None, build_time=None, build_id=None,
340 - file_size=None, mtime=None):
341 + file_size=None, mtime=None, db=None):
342 return _unicode.__new__(cls, cpv)
343
344 def __init__(self, cpv, metadata=None, settings=None, eapi=None,
345 repo=None, slot=None, build_time=None, build_id=None,
346 - file_size=None, mtime=None):
347 + file_size=None, mtime=None, db=None):
348 if not isinstance(cpv, _unicode):
349 # Avoid TypeError from _unicode.__init__ with PyPy.
350 cpv = _unicode_decode(cpv)
351 @@ -384,6 +384,8 @@ class _pkg_str(_unicode):
352 mtime = metadata.get('_mtime_', mtime)
353 if settings is not None:
354 self.__dict__['_settings'] = settings
355 + if db is not None:
356 + self.__dict__['_db'] = db
357 if eapi is not None:
358 self.__dict__['eapi'] = eapi
359
360 --
361 2.13.6