aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'pym/gentoolkit/atom.py')
-rw-r--r--pym/gentoolkit/atom.py626
1 files changed, 313 insertions, 313 deletions
diff --git a/pym/gentoolkit/atom.py b/pym/gentoolkit/atom.py
index 364fe4e..dd843d7 100644
--- a/pym/gentoolkit/atom.py
+++ b/pym/gentoolkit/atom.py
@@ -6,7 +6,7 @@
"""Subclasses portage.dep.Atom to provide methods on a Gentoo atom string."""
-__all__ = ('Atom',)
+__all__ = ("Atom",)
# =======
# Imports
@@ -24,319 +24,319 @@ from gentoolkit import errors
# Classes
# =======
+
class Atom(portage.dep.Atom, CPV):
- """Portage's Atom class with improvements from pkgcore.
+ """Portage's Atom class with improvements from pkgcore.
+
+ portage.dep.Atom provides the following instance variables:
+
+ @type operator: str
+ @ivar operator: one of ('=', '=*', '<', '>', '<=', '>=', '~', None)
+ @type cp: str
+ @ivar cp: cat/pkg
+ @type cpv: str
+ @ivar cpv: cat/pkg-ver (if ver)
+ @type slot: str or None (modified to tuple if not None)
+ @ivar slot: slot passed in as cpv:#
+ """
+
+ # Necessary for Portage versions < 2.1.7
+ _atoms = weakref.WeakValueDictionary()
+
+ def __init__(self, atom):
+ self.atom = atom
+ self.operator = self.blocker = self.use = self.slot = None
+
+ try:
+ portage.dep.Atom.__init__(self, atom)
+ except portage.exception.InvalidAtom:
+ raise errors.GentoolkitInvalidAtom(atom)
+
+ # Make operator compatible with intersects
+ if self.operator is None:
+ self.operator = ""
+
+ CPV.__init__(self, self.cpv)
+
+ # use_conditional is USE flag condition for this Atom to be required:
+ # For: !build? ( >=sys-apps/sed-4.0.5 ), use_conditional = '!build'
+ self.use_conditional = None
+
+ def __eq__(self, other):
+ if not isinstance(other, self.__class__):
+ err = "other isn't of %s type, is %s"
+ raise TypeError(err % (self.__class__, other.__class__))
+
+ if self.operator != other.operator:
+ return False
+
+ if not CPV.__eq__(self, other):
+ return False
+
+ if bool(self.blocker) != bool(other.blocker):
+ return False
+
+ if self.blocker and other.blocker:
+ if self.blocker.overlap.forbid != other.blocker.overlap.forbid:
+ return False
+
+ if self.use_conditional != other.use_conditional:
+ return False
+
+ # Don't believe Portage has something like this
+ # c = cmp(self.negate_vers, other.negate_vers)
+ # if c:
+ # return c
+
+ if self.slot != other.slot:
+ return False
+
+ this_use = None
+ if self.use is not None:
+ this_use = sorted(self.use.tokens)
+ that_use = None
+ if other.use is not None:
+ that_use = sorted(other.use.tokens)
+ if this_use != that_use:
+ return False
+
+ # Not supported by Portage Atom yet
+ # return cmp(self.repo_name, other.repo_name)
+ return True
+
+ def __hash__(self):
+ return hash(self.atom)
+
+ def __ne__(self, other):
+ return not self == other
+
+ def __lt__(self, other):
+ if not isinstance(other, self.__class__):
+ err = "other isn't of %s type, is %s"
+ raise TypeError(err % (self.__class__, other.__class__))
+
+ if self.operator != other.operator:
+ return self.operator < other.operator
+
+ if not CPV.__eq__(self, other):
+ return CPV.__lt__(self, other)
+
+ if bool(self.blocker) != bool(other.blocker):
+ # We want non blockers, then blockers, so only return True
+ # if self.blocker is True and other.blocker is False.
+ return bool(self.blocker) > bool(other.blocker)
+
+ if self.blocker and other.blocker:
+ if self.blocker.overlap.forbid != other.blocker.overlap.forbid:
+ # we want !! prior to !
+ return self.blocker.overlap.forbid < other.blocker.overlap.forbid
+
+ # Don't believe Portage has something like this
+ # c = cmp(self.negate_vers, other.negate_vers)
+ # if c:
+ # return c
+
+ if self.slot != other.slot:
+ if self.slot is None:
+ return False
+ elif other.slot is None:
+ return True
+ return self.slot < other.slot
+
+ this_use = []
+ if self.use is not None:
+ this_use = sorted(self.use.tokens)
+ that_use = []
+ if other.use is not None:
+ that_use = sorted(other.use.tokens)
+ if this_use != that_use:
+ return this_use < that_use
+
+ # Not supported by Portage Atom yet
+ # return cmp(self.repo_name, other.repo_name)
+
+ return False
+
+ def __gt__(self, other):
+ if not isinstance(other, self.__class__):
+ err = "other isn't of %s type, is %s"
+ raise TypeError(err % (self.__class__, other.__class__))
+
+ return not self <= other
+
+ def __le__(self, other):
+ if not isinstance(other, self.__class__):
+ raise TypeError(
+ "other isn't of %s type, is %s" % (self.__class__, other.__class__)
+ )
+ return self < other or self == other
+
+ def __ge__(self, other):
+ if not isinstance(other, self.__class__):
+ raise TypeError(
+ "other isn't of %s type, is %s" % (self.__class__, other.__class__)
+ )
+ return self > other or self == other
+
+ def __repr__(self):
+ uc = self.use_conditional
+ uc = "%s? " % uc if uc is not None else ""
+ return "<%s %r>" % (self.__class__.__name__, "%s%s" % (uc, self.atom))
+
+ def __setattr__(self, name, value):
+ object.__setattr__(self, name, value)
+
+ def intersects(self, other):
+ """Check if a passed in package atom "intersects" this atom.
+
+ Lifted from pkgcore.
+
+ Two atoms "intersect" if a package can be constructed that
+ matches both:
+ - if you query for just "dev-lang/python" it "intersects" both
+ "dev-lang/python" and ">=dev-lang/python-2.4"
+ - if you query for "=dev-lang/python-2.4" it "intersects"
+ ">=dev-lang/python-2.4" and "dev-lang/python" but not
+ "<dev-lang/python-2.3"
+
+ @type other: L{gentoolkit.atom.Atom} or
+ L{gentoolkit.versionmatch.VersionMatch}
+ @param other: other package to compare
+ @see: L{pkgcore.ebuild.atom}
+ """
+ # Our "cp" (cat/pkg) must match exactly:
+ if self.cp != other.cp:
+ # Check to see if one is name only:
+ # We don't bother checking if self.category is None: it can't be
+ # because we're an Atom subclass and that would be invalid.
+ return not other.category and self.name == other.name
+
+ # Slot dep only matters if we both have one. If we do they
+ # must be identical:
+ this_slot = getattr(self, "slot", None)
+ that_slot = getattr(other, "slot", None)
+ if this_slot is not None and that_slot is not None and this_slot != that_slot:
+ return False
+
+ if self.repo is not None and other.repo is not None and self.repo != other.repo:
+ return False
+
+ # Use deps are similar: if one of us forces a flag on and the
+ # other forces it off we do not intersect. If only one of us
+ # cares about a flag it is irrelevant.
+
+ # Skip the (very common) case of one of us not having use deps:
+ this_use = getattr(self, "use", None)
+ that_use = getattr(other, "use", None)
+ if this_use and that_use:
+ # Set of flags we do not have in common:
+ flags = set(this_use.tokens) ^ set(that_use.tokens)
+ for flag in flags:
+ # If this is unset and we also have the set version we fail:
+ if flag[0] == "-" and flag[1:] in flags:
+ return False
+
+ # Remaining thing to check is version restrictions. Get the
+ # ones we can check without actual version comparisons out of
+ # the way first.
+
+ # If one of us is unversioned we intersect:
+ if not self.operator or not other.operator:
+ return True
+
+ # If we are both "unbounded" in the same direction we intersect:
+ if ("<" in self.operator and "<" in other.operator) or (
+ ">" in self.operator and ">" in other.operator
+ ):
+ return True
+
+ # If one of us is an exact match we intersect if the other matches it:
+ if self.operator == "=":
+ if other.operator == "=*":
+ return self.fullversion.startswith(other.fullversion)
+ return VersionMatch(other, op=other.operator).match(self)
+ if other.operator == "=":
+ if self.operator == "=*":
+ return other.fullversion.startswith(self.fullversion)
+ return VersionMatch(self, op=self.operator).match(other)
+
+ # If we are both ~ matches we match if we are identical:
+ if self.operator == other.operator == "~":
+ return self.version == other.version and self.revision == other.revision
+
+ # If we are both glob matches we match if one of us matches the other.
+ if self.operator == other.operator == "=*":
+ return self.fullversion.startswith(
+ other.fullversion
+ ) or other.fullversion.startswith(self.fullversion)
+
+ # If one of us is a glob match and the other a ~ we match if the glob
+ # matches the ~ (ignoring a revision on the glob):
+ if self.operator == "=*" and other.operator == "~":
+ return other.fullversion.startswith(self.version)
+ if other.operator == "=*" and self.operator == "~":
+ return self.fullversion.startswith(other.version)
+
+ # If we get here at least one of us is a <, <=, > or >=:
+ if self.operator in ("<", "<=", ">", ">="):
+ ranged, ranged.operator = self, self.operator
+ else:
+ ranged, ranged.operator = other, other.operator
+ other, other.operator = self, self.operator
+
+ if "<" in other.operator or ">" in other.operator:
+ # We are both ranged, and in the opposite "direction" (or
+ # we would have matched above). We intersect if we both
+ # match the other's endpoint (just checking one endpoint
+ # is not enough, it would give a false positive on <=2 vs >2)
+ return VersionMatch(other, op=other.operator).match(
+ ranged
+ ) and VersionMatch(ranged, op=ranged.operator).match(other)
+
+ if other.operator == "~":
+ # Other definitely matches its own version. If ranged also
+ # does we're done:
+ if VersionMatch(ranged, op=ranged.operator).match(other):
+ return True
+ # The only other case where we intersect is if ranged is a
+ # > or >= on other's version and a nonzero revision. In
+ # that case other will match ranged. Be careful not to
+ # give a false positive for ~2 vs <2 here:
+ return ranged.operator in (">", ">=") and VersionMatch(
+ other, op=other.operator
+ ).match(ranged)
+
+ if other.operator == "=*":
+ # a glob match definitely matches its own version, so if
+ # ranged does too we're done:
+ if VersionMatch(ranged, op=ranged.operator).match(other):
+ return True
+ if "<" in ranged.operator:
+ # If other.revision is not defined then other does not
+ # match anything smaller than its own fullversion:
+ if other.revision:
+ return False
+
+ # If other.revision is defined then we can always
+ # construct a package smaller than other.fullversion by
+ # tagging e.g. an _alpha1 on.
+ return ranged.fullversion.startswith(other.version)
+ else:
+ # Remaining cases where this intersects: there is a
+ # package greater than ranged.fullversion and
+ # other.fullversion that they both match.
+ return ranged.fullversion.startswith(other.version)
+
+ # Handled all possible ops.
+ raise NotImplementedError(
+ "Someone added an operator without adding it to intersects"
+ )
+
+ def get_depstr(self):
+ """Returns a string representation of the original dep"""
+ uc = self.use_conditional
+ uc = "%s? " % uc if uc is not None else ""
+ return "%s%s" % (uc, self.atom)
- portage.dep.Atom provides the following instance variables:
-
- @type operator: str
- @ivar operator: one of ('=', '=*', '<', '>', '<=', '>=', '~', None)
- @type cp: str
- @ivar cp: cat/pkg
- @type cpv: str
- @ivar cpv: cat/pkg-ver (if ver)
- @type slot: str or None (modified to tuple if not None)
- @ivar slot: slot passed in as cpv:#
- """
-
- # Necessary for Portage versions < 2.1.7
- _atoms = weakref.WeakValueDictionary()
-
- def __init__(self, atom):
- self.atom = atom
- self.operator = self.blocker = self.use = self.slot = None
-
- try:
- portage.dep.Atom.__init__(self, atom)
- except portage.exception.InvalidAtom:
- raise errors.GentoolkitInvalidAtom(atom)
-
- # Make operator compatible with intersects
- if self.operator is None:
- self.operator = ''
-
- CPV.__init__(self, self.cpv)
-
- # use_conditional is USE flag condition for this Atom to be required:
- # For: !build? ( >=sys-apps/sed-4.0.5 ), use_conditional = '!build'
- self.use_conditional = None
-
- def __eq__(self, other):
- if not isinstance(other, self.__class__):
- err = "other isn't of %s type, is %s"
- raise TypeError(err % (self.__class__, other.__class__))
-
- if self.operator != other.operator:
- return False
-
- if not CPV.__eq__(self, other):
- return False
-
- if bool(self.blocker) != bool(other.blocker):
- return False
-
- if self.blocker and other.blocker:
- if self.blocker.overlap.forbid != other.blocker.overlap.forbid:
- return False
-
- if self.use_conditional != other.use_conditional:
- return False
-
- # Don't believe Portage has something like this
- #c = cmp(self.negate_vers, other.negate_vers)
- #if c:
- # return c
-
- if self.slot != other.slot:
- return False
-
- this_use = None
- if self.use is not None:
- this_use = sorted(self.use.tokens)
- that_use = None
- if other.use is not None:
- that_use = sorted(other.use.tokens)
- if this_use != that_use:
- return False
-
- # Not supported by Portage Atom yet
- #return cmp(self.repo_name, other.repo_name)
- return True
-
- def __hash__(self):
- return hash(self.atom)
-
- def __ne__(self, other):
- return not self == other
-
- def __lt__(self, other):
- if not isinstance(other, self.__class__):
- err = "other isn't of %s type, is %s"
- raise TypeError(err % (self.__class__, other.__class__))
-
- if self.operator != other.operator:
- return self.operator < other.operator
-
- if not CPV.__eq__(self, other):
- return CPV.__lt__(self, other)
-
- if bool(self.blocker) != bool(other.blocker):
- # We want non blockers, then blockers, so only return True
- # if self.blocker is True and other.blocker is False.
- return bool(self.blocker) > bool(other.blocker)
-
- if self.blocker and other.blocker:
- if self.blocker.overlap.forbid != other.blocker.overlap.forbid:
- # we want !! prior to !
- return (self.blocker.overlap.forbid <
- other.blocker.overlap.forbid)
-
- # Don't believe Portage has something like this
- #c = cmp(self.negate_vers, other.negate_vers)
- #if c:
- # return c
-
- if self.slot != other.slot:
- if self.slot is None:
- return False
- elif other.slot is None:
- return True
- return self.slot < other.slot
-
- this_use = []
- if self.use is not None:
- this_use = sorted(self.use.tokens)
- that_use = []
- if other.use is not None:
- that_use = sorted(other.use.tokens)
- if this_use != that_use:
- return this_use < that_use
-
- # Not supported by Portage Atom yet
- #return cmp(self.repo_name, other.repo_name)
-
- return False
-
- def __gt__(self, other):
- if not isinstance(other, self.__class__):
- err = "other isn't of %s type, is %s"
- raise TypeError(err % (self.__class__, other.__class__))
-
- return not self <= other
-
- def __le__(self, other):
- if not isinstance(other, self.__class__):
- raise TypeError("other isn't of %s type, is %s" % (
- self.__class__, other.__class__)
- )
- return self < other or self == other
-
- def __ge__(self, other):
- if not isinstance(other, self.__class__):
- raise TypeError("other isn't of %s type, is %s" % (
- self.__class__, other.__class__)
- )
- return self > other or self == other
-
- def __repr__(self):
- uc = self.use_conditional
- uc = "%s? " % uc if uc is not None else ''
- return "<%s %r>" % (self.__class__.__name__, "%s%s" % (uc, self.atom))
-
- def __setattr__(self, name, value):
- object.__setattr__(self, name, value)
-
- def intersects(self, other):
- """Check if a passed in package atom "intersects" this atom.
-
- Lifted from pkgcore.
-
- Two atoms "intersect" if a package can be constructed that
- matches both:
- - if you query for just "dev-lang/python" it "intersects" both
- "dev-lang/python" and ">=dev-lang/python-2.4"
- - if you query for "=dev-lang/python-2.4" it "intersects"
- ">=dev-lang/python-2.4" and "dev-lang/python" but not
- "<dev-lang/python-2.3"
-
- @type other: L{gentoolkit.atom.Atom} or
- L{gentoolkit.versionmatch.VersionMatch}
- @param other: other package to compare
- @see: L{pkgcore.ebuild.atom}
- """
- # Our "cp" (cat/pkg) must match exactly:
- if self.cp != other.cp:
- # Check to see if one is name only:
- # We don't bother checking if self.category is None: it can't be
- # because we're an Atom subclass and that would be invalid.
- return (not other.category and self.name == other.name)
-
- # Slot dep only matters if we both have one. If we do they
- # must be identical:
- this_slot = getattr(self, 'slot', None)
- that_slot = getattr(other, 'slot', None)
- if (this_slot is not None and that_slot is not None and
- this_slot != that_slot):
- return False
-
- if (self.repo is not None and other.repo is not None and
- self.repo != other.repo):
- return False
-
- # Use deps are similar: if one of us forces a flag on and the
- # other forces it off we do not intersect. If only one of us
- # cares about a flag it is irrelevant.
-
- # Skip the (very common) case of one of us not having use deps:
- this_use = getattr(self, 'use', None)
- that_use = getattr(other, 'use', None)
- if this_use and that_use:
- # Set of flags we do not have in common:
- flags = set(this_use.tokens) ^ set(that_use.tokens)
- for flag in flags:
- # If this is unset and we also have the set version we fail:
- if flag[0] == '-' and flag[1:] in flags:
- return False
-
- # Remaining thing to check is version restrictions. Get the
- # ones we can check without actual version comparisons out of
- # the way first.
-
- # If one of us is unversioned we intersect:
- if not self.operator or not other.operator:
- return True
-
- # If we are both "unbounded" in the same direction we intersect:
- if (('<' in self.operator and '<' in other.operator) or
- ('>' in self.operator and '>' in other.operator)):
- return True
-
- # If one of us is an exact match we intersect if the other matches it:
- if self.operator == '=':
- if other.operator == '=*':
- return self.fullversion.startswith(other.fullversion)
- return VersionMatch(other, op=other.operator).match(self)
- if other.operator == '=':
- if self.operator == '=*':
- return other.fullversion.startswith(self.fullversion)
- return VersionMatch(self, op=self.operator).match(other)
-
- # If we are both ~ matches we match if we are identical:
- if self.operator == other.operator == '~':
- return (self.version == other.version and
- self.revision == other.revision)
-
- # If we are both glob matches we match if one of us matches the other.
- if self.operator == other.operator == '=*':
- return (self.fullversion.startswith(other.fullversion) or
- other.fullversion.startswith(self.fullversion))
-
- # If one of us is a glob match and the other a ~ we match if the glob
- # matches the ~ (ignoring a revision on the glob):
- if self.operator == '=*' and other.operator == '~':
- return other.fullversion.startswith(self.version)
- if other.operator == '=*' and self.operator == '~':
- return self.fullversion.startswith(other.version)
-
- # If we get here at least one of us is a <, <=, > or >=:
- if self.operator in ('<', '<=', '>', '>='):
- ranged, ranged.operator = self, self.operator
- else:
- ranged, ranged.operator = other, other.operator
- other, other.operator = self, self.operator
-
- if '<' in other.operator or '>' in other.operator:
- # We are both ranged, and in the opposite "direction" (or
- # we would have matched above). We intersect if we both
- # match the other's endpoint (just checking one endpoint
- # is not enough, it would give a false positive on <=2 vs >2)
- return (
- VersionMatch(other, op=other.operator).match(ranged) and
- VersionMatch(ranged, op=ranged.operator).match(other)
- )
-
- if other.operator == '~':
- # Other definitely matches its own version. If ranged also
- # does we're done:
- if VersionMatch(ranged, op=ranged.operator).match(other):
- return True
- # The only other case where we intersect is if ranged is a
- # > or >= on other's version and a nonzero revision. In
- # that case other will match ranged. Be careful not to
- # give a false positive for ~2 vs <2 here:
- return (ranged.operator in ('>', '>=') and
- VersionMatch(other, op=other.operator).match(ranged))
-
- if other.operator == '=*':
- # a glob match definitely matches its own version, so if
- # ranged does too we're done:
- if VersionMatch(ranged, op=ranged.operator).match(other):
- return True
- if '<' in ranged.operator:
- # If other.revision is not defined then other does not
- # match anything smaller than its own fullversion:
- if other.revision:
- return False
-
- # If other.revision is defined then we can always
- # construct a package smaller than other.fullversion by
- # tagging e.g. an _alpha1 on.
- return ranged.fullversion.startswith(other.version)
- else:
- # Remaining cases where this intersects: there is a
- # package greater than ranged.fullversion and
- # other.fullversion that they both match.
- return ranged.fullversion.startswith(other.version)
-
- # Handled all possible ops.
- raise NotImplementedError(
- 'Someone added an operator without adding it to intersects')
-
- def get_depstr(self):
- """Returns a string representation of the original dep
- """
- uc = self.use_conditional
- uc = "%s? " % uc if uc is not None else ''
- return "%s%s" % (uc, self.atom)
# vim: set ts=4 sw=4 tw=79: