def entries_matching_range(self, from_ver=None, to_ver=None): """Return entries whose header versions are within a range of versions. @type from_ver: str @param from_ver: valid Gentoo version @type to_ver: str @param to_ver: valid Gentoo version @rtype: list @return: entries between from_ver and to_ver @raise errors.GentoolkitFatalError: if neither vers are set @raise errors.GentoolkitInvalidVersion: if either ver is invalid """ result = [] # Make sure we have at least one version set if not (from_ver or to_ver): raise errors.GentoolkitFatalError( "Need to specifiy 'from_ver' or 'to_ver'" ) # Create a VersionMatch instance out of from_ver from_restriction = None if from_ver: try: from_ver_rev = CPV("null-%s" % from_ver) except errors.GentoolkitInvalidCPV: raise errors.GentoolkitInvalidVersion(from_ver) from_restriction = VersionMatch(from_ver_rev, op='>=') # Create a VersionMatch instance out of to_ver to_restriction = None if to_ver: try: to_ver_rev = CPV("null-%s" % to_ver) except errors.GentoolkitInvalidCPV: raise errors.GentoolkitInvalidVersion(to_ver) to_restriction = VersionMatch(to_ver_rev, op='<=') # Add entry to result if version ranges intersect it for entry_set in self.indexed_entries: i, entry = entry_set if from_restriction and not from_restriction.match(i): continue if to_restriction and not to_restriction.match(i): # TODO: is it safe to break here? continue result.append(entry) return result
def _index_changelog(self): """Use the output of L{self._split_changelog} to create an index list of L{gentoolkit.versionmatch.VersionMatch} objects. @rtype: list @return: tuples containing a VersionMatch instance for the release version of each entry header as the first item and the entire entry as the second item @raise ValueError: if self.invalid_entry_is_fatal is True and we hit an invalid entry """ result = [] for entry in self.entries: # Extract the package name from the entry header, ex: # *xterm-242 (07 Mar 2009) => xterm-242 pkg_name = entry.split(' ', 1)[0].lstrip('*') if not pkg_name.strip(): continue try: entry_ver = CPV(pkg_name, validate=True) except errors.GentoolkitInvalidCPV: if self.invalid_entry_is_fatal: raise ValueError(entry_ver) continue result.append((VersionMatch(entry_ver, op='='), entry)) return result
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')