repoman: rename most *DEPEND.* to dependency.*
[portage.git] / pym / _emerge / Package.py
1 # Copyright 1999-2012 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3
4 import sys
5 from itertools import chain
6 import portage
7 from portage import _encodings, _unicode_decode, _unicode_encode
8 from portage.cache.mappings import slot_dict_class
9 from portage.const import EBUILD_PHASES
10 from portage.dep import Atom, check_required_use, use_reduce, \
11         paren_enclose, _slot_separator, _repo_separator
12 from portage.versions import _pkg_str, _unknown_repo
13 from portage.eapi import _get_eapi_attrs
14 from portage.exception import InvalidDependString
15 from portage.localization import _
16 from _emerge.Task import Task
17
18 if sys.hexversion >= 0x3000000:
19         basestring = str
20         long = int
21         _unicode = str
22 else:
23         _unicode = unicode
24
25 class Package(Task):
26
27         __hash__ = Task.__hash__
28         __slots__ = ("built", "cpv", "depth",
29                 "installed", "metadata", "onlydeps", "operation",
30                 "root_config", "type_name",
31                 "category", "counter", "cp", "cpv_split",
32                 "inherited", "iuse", "mtime",
33                 "pf", "root", "slot", "sub_slot", "slot_atom", "version") + \
34                 ("_invalid", "_raw_metadata", "_masks", "_use",
35                 "_validated_atoms", "_visible")
36
37         metadata_keys = [
38                 "BUILD_TIME", "CHOST", "COUNTER", "DEPEND", "EAPI",
39                 "HDEPEND", "INHERITED", "IUSE", "KEYWORDS",
40                 "LICENSE", "PDEPEND", "PROVIDE", "RDEPEND",
41                 "repository", "PROPERTIES", "RESTRICT", "SLOT", "USE",
42                 "_mtime_", "DEFINED_PHASES", "REQUIRED_USE"]
43
44         _dep_keys = ('DEPEND', 'HDEPEND' 'PDEPEND', 'RDEPEND',)
45         _use_conditional_misc_keys = ('LICENSE', 'PROPERTIES', 'RESTRICT')
46         UNKNOWN_REPO = _unknown_repo
47
48         def __init__(self, **kwargs):
49                 Task.__init__(self, **kwargs)
50                 # the SlotObject constructor assigns self.root_config from keyword args
51                 # and is an instance of a '_emerge.RootConfig.RootConfig class
52                 self.root = self.root_config.root
53                 self._raw_metadata = _PackageMetadataWrapperBase(self.metadata)
54                 self.metadata = _PackageMetadataWrapper(self, self._raw_metadata)
55                 if not self.built:
56                         self.metadata['CHOST'] = self.root_config.settings.get('CHOST', '')
57                 eapi_attrs = _get_eapi_attrs(self.metadata["EAPI"])
58                 self.cpv = _pkg_str(self.cpv, metadata=self.metadata,
59                         settings=self.root_config.settings)
60                 if hasattr(self.cpv, 'slot_invalid'):
61                         self._invalid_metadata('SLOT.invalid',
62                                 "SLOT: invalid value: '%s'" % self.metadata["SLOT"])
63                 self.cp = self.cpv.cp
64                 self.slot = self.cpv.slot
65                 self.sub_slot = self.cpv.sub_slot
66                 # sync metadata with validated repo (may be UNKNOWN_REPO)
67                 self.metadata['repository'] = self.cpv.repo
68
69                 if eapi_attrs.iuse_effective:
70                         implicit_match = self.root_config.settings._iuse_effective_match
71                 else:
72                         implicit_match = self.root_config.settings._iuse_implicit_match
73                 self.iuse = self._iuse(self.metadata["IUSE"].split(), implicit_match)
74
75                 if (self.iuse.enabled or self.iuse.disabled) and \
76                         not eapi_attrs.iuse_defaults:
77                         if not self.installed:
78                                 self._invalid_metadata('EAPI.incompatible',
79                                         "IUSE contains defaults, but EAPI doesn't allow them")
80                 self.slot_atom = Atom("%s%s%s" % (self.cp, _slot_separator, self.slot))
81                 self.category, self.pf = portage.catsplit(self.cpv)
82                 self.cpv_split = self.cpv.cpv_split
83                 self.version = self.cpv.version
84                 if self.inherited is None:
85                         self.inherited = frozenset()
86
87                 if self.operation is None:
88                         if self.onlydeps or self.installed:
89                                 self.operation = "nomerge"
90                         else:
91                                 self.operation = "merge"
92
93                 self._hash_key = Package._gen_hash_key(cpv=self.cpv,
94                         installed=self.installed, onlydeps=self.onlydeps,
95                         operation=self.operation, repo_name=self.cpv.repo,
96                         root_config=self.root_config,
97                         type_name=self.type_name)
98                 self._hash_value = hash(self._hash_key)
99
100         # For consistency with _pkg_str
101         @property
102         def _metadata(self):
103                 return self.metadata
104
105         # These are calculated on-demand, so that they are calculated
106         # after FakeVartree applies its metadata tweaks.
107         @property
108         def invalid(self):
109                 if self._invalid is None:
110                         self._validate_deps()
111                         if self._invalid is None:
112                                 self._invalid = False
113                 return self._invalid
114
115         @property
116         def masks(self):
117                 if self._masks is None:
118                         self._masks = self._eval_masks()
119                 return self._masks
120
121         @property
122         def visible(self):
123                 if self._visible is None:
124                         self._visible = self._eval_visiblity(self.masks)
125                 return self._visible
126
127         @property
128         def validated_atoms(self):
129                 """
130                 Returns *all* validated atoms from the deps, regardless
131                 of USE conditionals, with USE conditionals inside
132                 atoms left unevaluated.
133                 """
134                 if self._validated_atoms is None:
135                         self._validate_deps()
136                 return self._validated_atoms
137
138         @property
139         def stable(self):
140                 return self.cpv.stable
141
142         @classmethod
143         def _gen_hash_key(cls, cpv=None, installed=None, onlydeps=None,
144                 operation=None, repo_name=None, root_config=None,
145                 type_name=None, **kwargs):
146
147                 if operation is None:
148                         if installed or onlydeps:
149                                 operation = "nomerge"
150                         else:
151                                 operation = "merge"
152
153                 root = None
154                 if root_config is not None:
155                         root = root_config.root
156                 else:
157                         raise TypeError("root_config argument is required")
158
159                 # For installed (and binary) packages we don't care for the repo
160                 # when it comes to hashing, because there can only be one cpv.
161                 # So overwrite the repo_key with type_name.
162                 if type_name is None:
163                         raise TypeError("type_name argument is required")
164                 elif type_name == "ebuild":
165                         if repo_name is None:
166                                 raise AssertionError(
167                                         "Package._gen_hash_key() " + \
168                                         "called without 'repo_name' argument")
169                         repo_key = repo_name
170                 else:
171                         # For installed (and binary) packages we don't care for the repo
172                         # when it comes to hashing, because there can only be one cpv.
173                         # So overwrite the repo_key with type_name.
174                         repo_key = type_name
175
176                 return (type_name, root, _unicode(cpv), operation, repo_key)
177
178         def _validate_deps(self):
179                 """
180                 Validate deps. This does not trigger USE calculation since that
181                 is expensive for ebuilds and therefore we want to avoid doing
182                 in unnecessarily (like for masked packages).
183                 """
184                 eapi = self.metadata['EAPI']
185                 dep_eapi = eapi
186                 dep_valid_flag = self.iuse.is_valid_flag
187                 if self.installed:
188                         # Ignore EAPI.incompatible and conditionals missing
189                         # from IUSE for installed packages since these issues
190                         # aren't relevant now (re-evaluate when new EAPIs are
191                         # deployed).
192                         dep_eapi = None
193                         dep_valid_flag = None
194
195                 validated_atoms = []
196                 for k in self._dep_keys:
197                         v = self.metadata.get(k)
198                         if not v:
199                                 continue
200                         try:
201                                 atoms = use_reduce(v, eapi=dep_eapi,
202                                         matchall=True, is_valid_flag=dep_valid_flag,
203                                         token_class=Atom, flat=True)
204                         except InvalidDependString as e:
205                                 self._metadata_exception(k, e)
206                         else:
207                                 validated_atoms.extend(atoms)
208                                 if not self.built:
209                                         for atom in atoms:
210                                                 if not isinstance(atom, Atom):
211                                                         continue
212                                                 if atom.slot_operator_built:
213                                                         e = InvalidDependString(
214                                                                 _("Improper context for slot-operator "
215                                                                 "\"built\" atom syntax: %s") %
216                                                                 (atom.unevaluated_atom,))
217                                                         self._metadata_exception(k, e)
218
219                 self._validated_atoms = tuple(set(atom for atom in
220                         validated_atoms if isinstance(atom, Atom)))
221
222                 k = 'PROVIDE'
223                 v = self.metadata.get(k)
224                 if v:
225                         try:
226                                 use_reduce(v, eapi=dep_eapi, matchall=True,
227                                         is_valid_flag=dep_valid_flag, token_class=Atom)
228                         except InvalidDependString as e:
229                                 self._invalid_metadata("PROVIDE.syntax",
230                                         _unicode_decode("%s: %s") % (k, e))
231
232                 for k in self._use_conditional_misc_keys:
233                         v = self.metadata.get(k)
234                         if not v:
235                                 continue
236                         try:
237                                 use_reduce(v, eapi=dep_eapi, matchall=True,
238                                         is_valid_flag=dep_valid_flag)
239                         except InvalidDependString as e:
240                                 self._metadata_exception(k, e)
241
242                 k = 'REQUIRED_USE'
243                 v = self.metadata.get(k)
244                 if v and not self.built:
245                         if not _get_eapi_attrs(eapi).required_use:
246                                 self._invalid_metadata('EAPI.incompatible',
247                                         "REQUIRED_USE set, but EAPI='%s' doesn't allow it" % eapi)
248                         else:
249                                 try:
250                                         check_required_use(v, (),
251                                                 self.iuse.is_valid_flag, eapi=eapi)
252                                 except InvalidDependString as e:
253                                         # Force unicode format string for python-2.x safety,
254                                         # ensuring that PortageException.__unicode__() is used
255                                         # when necessary.
256                                         self._invalid_metadata(k + ".syntax",
257                                                 _unicode_decode("%s: %s") % (k, e))
258
259                 k = 'SRC_URI'
260                 v = self.metadata.get(k)
261                 if v:
262                         try:
263                                 use_reduce(v, is_src_uri=True, eapi=eapi, matchall=True,
264                                         is_valid_flag=self.iuse.is_valid_flag)
265                         except InvalidDependString as e:
266                                 if not self.installed:
267                                         self._metadata_exception(k, e)
268
269         def copy(self):
270                 return Package(built=self.built, cpv=self.cpv, depth=self.depth,
271                         installed=self.installed, metadata=self._raw_metadata,
272                         onlydeps=self.onlydeps, operation=self.operation,
273                         root_config=self.root_config, type_name=self.type_name)
274
275         def _eval_masks(self):
276                 masks = {}
277                 settings = self.root_config.settings
278
279                 if self.invalid is not False:
280                         masks['invalid'] = self.invalid
281
282                 if not settings._accept_chost(self.cpv, self.metadata):
283                         masks['CHOST'] = self.metadata['CHOST']
284
285                 eapi = self.metadata["EAPI"]
286                 if not portage.eapi_is_supported(eapi):
287                         masks['EAPI.unsupported'] = eapi
288                 if portage._eapi_is_deprecated(eapi):
289                         masks['EAPI.deprecated'] = eapi
290
291                 missing_keywords = settings._getMissingKeywords(
292                         self.cpv, self.metadata)
293                 if missing_keywords:
294                         masks['KEYWORDS'] = missing_keywords
295
296                 try:
297                         missing_properties = settings._getMissingProperties(
298                                 self.cpv, self.metadata)
299                         if missing_properties:
300                                 masks['PROPERTIES'] = missing_properties
301                 except InvalidDependString:
302                         # already recorded as 'invalid'
303                         pass
304
305                 mask_atom = settings._getMaskAtom(self.cpv, self.metadata)
306                 if mask_atom is not None:
307                         masks['package.mask'] = mask_atom
308
309                 try:
310                         missing_licenses = settings._getMissingLicenses(
311                                 self.cpv, self.metadata)
312                         if missing_licenses:
313                                 masks['LICENSE'] = missing_licenses
314                 except InvalidDependString:
315                         # already recorded as 'invalid'
316                         pass
317
318                 if not masks:
319                         masks = False
320
321                 return masks
322
323         def _eval_visiblity(self, masks):
324
325                 if masks is not False:
326
327                         if 'EAPI.unsupported' in masks:
328                                 return False
329
330                         if 'invalid' in masks:
331                                 return False
332
333                         if not self.installed and ( \
334                                 'CHOST' in masks or \
335                                 'EAPI.deprecated' in masks or \
336                                 'KEYWORDS' in masks or \
337                                 'PROPERTIES' in masks):
338                                 return False
339
340                         if 'package.mask' in masks or \
341                                 'LICENSE' in masks:
342                                 return False
343
344                 return True
345
346         def get_keyword_mask(self):
347                 """returns None, 'missing', or 'unstable'."""
348
349                 missing = self.root_config.settings._getRawMissingKeywords(
350                                 self.cpv, self.metadata)
351
352                 if not missing:
353                         return None
354
355                 if '**' in missing:
356                         return 'missing'
357
358                 global_accept_keywords = frozenset(
359                         self.root_config.settings.get("ACCEPT_KEYWORDS", "").split())
360
361                 for keyword in missing:
362                         if keyword.lstrip("~") in global_accept_keywords:
363                                 return 'unstable'
364
365                 return 'missing'
366
367         def isHardMasked(self):
368                 """returns a bool if the cpv is in the list of
369                 expanded pmaskdict[cp] available ebuilds"""
370                 pmask = self.root_config.settings._getRawMaskAtom(
371                         self.cpv, self.metadata)
372                 return pmask is not None
373
374         def _metadata_exception(self, k, e):
375
376                 if k.endswith('DEPEND'):
377                         qacat = 'dependency.syntax'
378                 else:
379                         qacat = k + ".syntax"
380
381                 # For unicode safety with python-2.x we need to avoid
382                 # using the string format operator with a non-unicode
383                 # format string, since that will result in the
384                 # PortageException.__str__() method being invoked,
385                 # followed by unsafe decoding that may result in a
386                 # UnicodeDecodeError. Therefore, use _unicode_decode()
387                 # to ensure that format strings are unicode, so that
388                 # PortageException.__unicode__() is used when necessary
389                 # in python-2.x.
390                 if not self.installed:
391                         categorized_error = False
392                         if e.errors:
393                                 for error in e.errors:
394                                         if getattr(error, 'category', None) is None:
395                                                 continue
396                                         categorized_error = True
397                                         self._invalid_metadata(error.category,
398                                                 _unicode_decode("%s: %s") % (k, error))
399
400                         if not categorized_error:
401                                 self._invalid_metadata(qacat,
402                                         _unicode_decode("%s: %s") % (k, e))
403                 else:
404                         # For installed packages, show the path of the file
405                         # containing the invalid metadata, since the user may
406                         # want to fix the deps by hand.
407                         vardb = self.root_config.trees['vartree'].dbapi
408                         path = vardb.getpath(self.cpv, filename=k)
409                         self._invalid_metadata(qacat,
410                                 _unicode_decode("%s: %s in '%s'") % (k, e, path))
411
412         def _invalid_metadata(self, msg_type, msg):
413                 if self._invalid is None:
414                         self._invalid = {}
415                 msgs = self._invalid.get(msg_type)
416                 if msgs is None:
417                         msgs = []
418                         self._invalid[msg_type] = msgs
419                 msgs.append(msg)
420
421         def __str__(self):
422                 if self.operation == "merge":
423                         if self.type_name == "binary":
424                                 cpv_color = "PKG_BINARY_MERGE"
425                         else:
426                                 cpv_color = "PKG_MERGE"
427                 elif self.operation == "uninstall":
428                         cpv_color = "PKG_UNINSTALL"
429                 else:
430                         cpv_color = "PKG_NOMERGE"
431
432                 s = "(%s, %s" \
433                         % (portage.output.colorize(cpv_color, self.cpv + _repo_separator + self.repo) , self.type_name)
434
435                 if self.type_name == "installed":
436                         if self.root_config.settings['ROOT'] != "/":
437                                 s += " in '%s'" % self.root_config.settings['ROOT']
438                         if self.operation == "uninstall":
439                                 s += " scheduled for uninstall"
440                 else:
441                         if self.operation == "merge":
442                                 s += " scheduled for merge"
443                                 if self.root_config.settings['ROOT'] != "/":
444                                         s += " to '%s'" % self.root_config.settings['ROOT']
445                 s += ")"
446                 return s
447
448         if sys.hexversion < 0x3000000:
449
450                 __unicode__ = __str__
451
452                 def __str__(self):
453                         return _unicode_encode(self.__unicode__(),
454                                 encoding=_encodings['content'])
455
456         class _use_class(object):
457
458                 __slots__ = ("enabled", "_expand", "_expand_hidden",
459                         "_force", "_pkg", "_mask")
460
461                 # Share identical frozenset instances when available.
462                 _frozensets = {}
463
464                 def __init__(self, pkg, use_str):
465                         self._pkg = pkg
466                         self._expand = None
467                         self._expand_hidden = None
468                         self._force = None
469                         self._mask = None
470                         self.enabled = frozenset(use_str.split())
471                         if pkg.built:
472                                 # Use IUSE to validate USE settings for built packages,
473                                 # in case the package manager that built this package
474                                 # failed to do that for some reason (or in case of
475                                 # data corruption).
476                                 missing_iuse = pkg.iuse.get_missing_iuse(self.enabled)
477                                 if missing_iuse:
478                                         self.enabled = self.enabled.difference(missing_iuse)
479
480                 def _init_force_mask(self):
481                         pkgsettings = self._pkg._get_pkgsettings()
482                         frozensets = self._frozensets
483                         s = frozenset(
484                                 pkgsettings.get("USE_EXPAND", "").lower().split())
485                         self._expand = frozensets.setdefault(s, s)
486                         s = frozenset(
487                                 pkgsettings.get("USE_EXPAND_HIDDEN", "").lower().split())
488                         self._expand_hidden = frozensets.setdefault(s, s)
489                         s = pkgsettings.useforce
490                         self._force = frozensets.setdefault(s, s)
491                         s = pkgsettings.usemask
492                         self._mask = frozensets.setdefault(s, s)
493
494                 @property
495                 def expand(self):
496                         if self._expand is None:
497                                 self._init_force_mask()
498                         return self._expand
499
500                 @property
501                 def expand_hidden(self):
502                         if self._expand_hidden is None:
503                                 self._init_force_mask()
504                         return self._expand_hidden
505
506                 @property
507                 def force(self):
508                         if self._force is None:
509                                 self._init_force_mask()
510                         return self._force
511
512                 @property
513                 def mask(self):
514                         if self._mask is None:
515                                 self._init_force_mask()
516                         return self._mask
517
518         @property
519         def repo(self):
520                 return self.metadata['repository']
521
522         @property
523         def repo_priority(self):
524                 repo_info = self.root_config.settings.repositories.prepos.get(self.repo)
525                 if repo_info is None:
526                         return None
527                 return repo_info.priority
528
529         @property
530         def use(self):
531                 if self._use is None:
532                         self.metadata._init_use()
533                 return self._use
534
535         def _get_pkgsettings(self):
536                 pkgsettings = self.root_config.trees[
537                         'porttree'].dbapi.doebuild_settings
538                 pkgsettings.setcpv(self)
539                 return pkgsettings
540
541         class _iuse(object):
542
543                 __slots__ = ("__weakref__", "all", "enabled", "disabled",
544                         "tokens") + ("_iuse_implicit_match",)
545
546                 def __init__(self, tokens, iuse_implicit_match):
547                         self.tokens = tuple(tokens)
548                         self._iuse_implicit_match = iuse_implicit_match
549                         enabled = []
550                         disabled = []
551                         other = []
552                         for x in tokens:
553                                 prefix = x[:1]
554                                 if prefix == "+":
555                                         enabled.append(x[1:])
556                                 elif prefix == "-":
557                                         disabled.append(x[1:])
558                                 else:
559                                         other.append(x)
560                         self.enabled = frozenset(enabled)
561                         self.disabled = frozenset(disabled)
562                         self.all = frozenset(chain(enabled, disabled, other))
563
564                 def is_valid_flag(self, flags):
565                         """
566                         @return: True if all flags are valid USE values which may
567                                 be specified in USE dependencies, False otherwise.
568                         """
569                         if isinstance(flags, basestring):
570                                 flags = [flags]
571
572                         for flag in flags:
573                                 if not flag in self.all and \
574                                         not self._iuse_implicit_match(flag):
575                                         return False
576                         return True
577
578                 def get_missing_iuse(self, flags):
579                         """
580                         @return: A list of flags missing from IUSE.
581                         """
582                         if isinstance(flags, basestring):
583                                 flags = [flags]
584                         missing_iuse = []
585                         for flag in flags:
586                                 if not flag in self.all and \
587                                         not self._iuse_implicit_match(flag):
588                                         missing_iuse.append(flag)
589                         return missing_iuse
590
591         def __len__(self):
592                 return 4
593
594         def __iter__(self):
595                 """
596                 This is used to generate mtimedb resume mergelist entries, so we
597                 limit it to 4 items for backward compatibility.
598                 """
599                 return iter(self._hash_key[:4])
600
601         def __lt__(self, other):
602                 if other.cp != self.cp:
603                         return False
604                 if portage.vercmp(self.version, other.version) < 0:
605                         return True
606                 return False
607
608         def __le__(self, other):
609                 if other.cp != self.cp:
610                         return False
611                 if portage.vercmp(self.version, other.version) <= 0:
612                         return True
613                 return False
614
615         def __gt__(self, other):
616                 if other.cp != self.cp:
617                         return False
618                 if portage.vercmp(self.version, other.version) > 0:
619                         return True
620                 return False
621
622         def __ge__(self, other):
623                 if other.cp != self.cp:
624                         return False
625                 if portage.vercmp(self.version, other.version) >= 0:
626                         return True
627                 return False
628
629 _all_metadata_keys = set(x for x in portage.auxdbkeys \
630         if not x.startswith("UNUSED_"))
631 _all_metadata_keys.update(Package.metadata_keys)
632 _all_metadata_keys = frozenset(_all_metadata_keys)
633
634 _PackageMetadataWrapperBase = slot_dict_class(_all_metadata_keys)
635
636 class _PackageMetadataWrapper(_PackageMetadataWrapperBase):
637         """
638         Detect metadata updates and synchronize Package attributes.
639         """
640
641         __slots__ = ("_pkg",)
642         _wrapped_keys = frozenset(
643                 ["COUNTER", "INHERITED", "USE", "_mtime_"])
644         _use_conditional_keys = frozenset(
645                 ['LICENSE', 'PROPERTIES', 'PROVIDE', 'RESTRICT',])
646
647         def __init__(self, pkg, metadata):
648                 _PackageMetadataWrapperBase.__init__(self)
649                 self._pkg = pkg
650                 if not pkg.built:
651                         # USE is lazy, but we want it to show up in self.keys().
652                         _PackageMetadataWrapperBase.__setitem__(self, 'USE', '')
653
654                 self.update(metadata)
655
656         def _init_use(self):
657                 if self._pkg.built:
658                         use_str = self['USE']
659                         self._pkg._use = self._pkg._use_class(
660                                 self._pkg, use_str)
661                 else:
662                         try:
663                                 use_str = _PackageMetadataWrapperBase.__getitem__(self, 'USE')
664                         except KeyError:
665                                 use_str = None
666                         calculated_use = False
667                         if not use_str:
668                                 use_str = self._pkg._get_pkgsettings()["PORTAGE_USE"]
669                                 calculated_use = True
670                         _PackageMetadataWrapperBase.__setitem__(self, 'USE', use_str)
671                         self._pkg._use = self._pkg._use_class(
672                                 self._pkg, use_str)
673                         # Initialize these now, since USE access has just triggered
674                         # setcpv, and we want to cache the result of the force/mask
675                         # calculations that were done.
676                         if calculated_use:
677                                 self._pkg._use._init_force_mask()
678
679                 return use_str
680
681         def __getitem__(self, k):
682                 v = _PackageMetadataWrapperBase.__getitem__(self, k)
683                 if k in self._use_conditional_keys:
684                         if self._pkg.root_config.settings.local_config and '?' in v:
685                                 try:
686                                         v = paren_enclose(use_reduce(v, uselist=self._pkg.use.enabled, \
687                                                 is_valid_flag=self._pkg.iuse.is_valid_flag))
688                                 except InvalidDependString:
689                                         # This error should already have been registered via
690                                         # self._pkg._invalid_metadata().
691                                         pass
692                                 else:
693                                         self[k] = v
694
695                 elif k == 'USE' and not self._pkg.built:
696                         if not v:
697                                 # This is lazy because it's expensive.
698                                 v = self._init_use()
699
700                 return v
701
702         def __setitem__(self, k, v):
703                 _PackageMetadataWrapperBase.__setitem__(self, k, v)
704                 if k in self._wrapped_keys:
705                         getattr(self, "_set_" + k.lower())(k, v)
706
707         def _set_inherited(self, k, v):
708                 if isinstance(v, basestring):
709                         v = frozenset(v.split())
710                 self._pkg.inherited = v
711
712         def _set_counter(self, k, v):
713                 if isinstance(v, basestring):
714                         try:
715                                 v = long(v.strip())
716                         except ValueError:
717                                 v = 0
718                 self._pkg.counter = v
719
720         def _set_use(self, k, v):
721                 # Force regeneration of _use attribute
722                 self._pkg._use = None
723                 # Use raw metadata to restore USE conditional values
724                 # to unevaluated state
725                 raw_metadata = self._pkg._raw_metadata
726                 for x in self._use_conditional_keys:
727                         try:
728                                 self[x] = raw_metadata[x]
729                         except KeyError:
730                                 pass
731
732         def _set__mtime_(self, k, v):
733                 if isinstance(v, basestring):
734                         try:
735                                 v = long(v.strip())
736                         except ValueError:
737                                 v = 0
738                 self._pkg.mtime = v
739
740         @property
741         def properties(self):
742                 return self['PROPERTIES'].split()
743
744         @property
745         def restrict(self):
746                 return self['RESTRICT'].split()
747
748         @property
749         def defined_phases(self):
750                 """
751                 Returns tokens from DEFINED_PHASES metadata if it is defined,
752                 otherwise returns a tuple containing all possible phases. This
753                 makes it easy to do containment checks to see if it's safe to
754                 skip execution of a given phase.
755                 """
756                 s = self['DEFINED_PHASES']
757                 if s:
758                         return s.split()
759                 return EBUILD_PHASES