build-common

Shared build system code (usually as a git submodule)
Log | Files | Refs | README | LICENSE

semver.py (47080B)


      1 # -*- coding: utf-8 -*-
      2 # Copyright (c) The python-semanticversion project
      3 # This code is distributed under the two-clause BSD License.
      4 
      5 import functools
      6 import re
      7 import warnings
      8 
      9 
     10 def _has_leading_zero(value):
     11     return (value
     12             and value[0] == '0'
     13             and value.isdigit()
     14             and value != '0')
     15 
     16 
     17 class MaxIdentifier(object):
     18     __slots__ = []
     19 
     20     def __repr__(self):
     21         return 'MaxIdentifier()'
     22 
     23     def __eq__(self, other):
     24         return isinstance(other, self.__class__)
     25 
     26 
     27 @functools.total_ordering
     28 class NumericIdentifier(object):
     29     __slots__ = ['value']
     30 
     31     def __init__(self, value):
     32         self.value = int(value)
     33 
     34     def __repr__(self):
     35         return 'NumericIdentifier(%r)' % self.value
     36 
     37     def __eq__(self, other):
     38         if isinstance(other, NumericIdentifier):
     39             return self.value == other.value
     40         return NotImplemented
     41 
     42     def __lt__(self, other):
     43         if isinstance(other, MaxIdentifier):
     44             return True
     45         elif isinstance(other, AlphaIdentifier):
     46             return True
     47         elif isinstance(other, NumericIdentifier):
     48             return self.value < other.value
     49         else:
     50             return NotImplemented
     51 
     52 
     53 @functools.total_ordering
     54 class AlphaIdentifier(object):
     55     __slots__ = ['value']
     56 
     57     def __init__(self, value):
     58         self.value = value.encode('ascii')
     59 
     60     def __repr__(self):
     61         return 'AlphaIdentifier(%r)' % self.value
     62 
     63     def __eq__(self, other):
     64         if isinstance(other, AlphaIdentifier):
     65             return self.value == other.value
     66         return NotImplemented
     67 
     68     def __lt__(self, other):
     69         if isinstance(other, MaxIdentifier):
     70             return True
     71         elif isinstance(other, NumericIdentifier):
     72             return False
     73         elif isinstance(other, AlphaIdentifier):
     74             return self.value < other.value
     75         else:
     76             return NotImplemented
     77 
     78 
     79 class Version(object):
     80 
     81     version_re = re.compile(r'^(\d+)\.(\d+)\.(\d+)(?:-([0-9a-zA-Z.-]+))?(?:\+([0-9a-zA-Z.-]+))?$')
     82     partial_version_re = re.compile(r'^(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:-([0-9a-zA-Z.-]*))?(?:\+([0-9a-zA-Z.-]*))?$')
     83 
     84     def __init__(
     85             self,
     86             version_string=None,
     87             major=None,
     88             minor=None,
     89             patch=None,
     90             prerelease=None,
     91             build=None,
     92             partial=False):
     93         if partial:
     94             warnings.warn(
     95                 "Partial versions will be removed in 3.0; use SimpleSpec('1.x.x') instead.",
     96                 DeprecationWarning,
     97                 stacklevel=2,
     98             )
     99         has_text = version_string is not None
    100         has_parts = not (major is minor is patch is prerelease is build is None)
    101         if not has_text ^ has_parts:
    102             raise ValueError("Call either Version('1.2.3') or Version(major=1, ...).")
    103 
    104         if has_text:
    105             major, minor, patch, prerelease, build = self.parse(version_string, partial)
    106         else:
    107             # Convenience: allow to omit prerelease/build.
    108             prerelease = tuple(prerelease or ())
    109             if not partial:
    110                 build = tuple(build or ())
    111             self._validate_kwargs(major, minor, patch, prerelease, build, partial)
    112 
    113         self.major = major
    114         self.minor = minor
    115         self.patch = patch
    116         self.prerelease = prerelease
    117         self.build = build
    118 
    119         self.partial = partial
    120 
    121     @classmethod
    122     def _coerce(cls, value, allow_none=False):
    123         if value is None and allow_none:
    124             return value
    125         return int(value)
    126 
    127     def next_major(self):
    128         if self.prerelease and self.minor == self.patch == 0:
    129             return Version(
    130                 major=self.major,
    131                 minor=0,
    132                 patch=0,
    133                 partial=self.partial,
    134             )
    135         else:
    136             return Version(
    137                 major=self.major + 1,
    138                 minor=0,
    139                 patch=0,
    140                 partial=self.partial,
    141             )
    142 
    143     def next_minor(self):
    144         if self.prerelease and self.patch == 0:
    145             return Version(
    146                 major=self.major,
    147                 minor=self.minor,
    148                 patch=0,
    149                 partial=self.partial,
    150             )
    151         else:
    152             return Version(
    153                 major=self.major,
    154                 minor=self.minor + 1,
    155                 patch=0,
    156                 partial=self.partial,
    157             )
    158 
    159     def next_patch(self):
    160         if self.prerelease:
    161             return Version(
    162                 major=self.major,
    163                 minor=self.minor,
    164                 patch=self.patch,
    165                 partial=self.partial,
    166             )
    167         else:
    168             return Version(
    169                 major=self.major,
    170                 minor=self.minor,
    171                 patch=self.patch + 1,
    172                 partial=self.partial,
    173             )
    174 
    175     def truncate(self, level='patch'):
    176         """Return a new Version object, truncated up to the selected level."""
    177         if level == 'build':
    178             return self
    179         elif level == 'prerelease':
    180             return Version(
    181                 major=self.major,
    182                 minor=self.minor,
    183                 patch=self.patch,
    184                 prerelease=self.prerelease,
    185                 partial=self.partial,
    186             )
    187         elif level == 'patch':
    188             return Version(
    189                 major=self.major,
    190                 minor=self.minor,
    191                 patch=self.patch,
    192                 partial=self.partial,
    193             )
    194         elif level == 'minor':
    195             return Version(
    196                 major=self.major,
    197                 minor=self.minor,
    198                 patch=None if self.partial else 0,
    199                 partial=self.partial,
    200             )
    201         elif level == 'major':
    202             return Version(
    203                 major=self.major,
    204                 minor=None if self.partial else 0,
    205                 patch=None if self.partial else 0,
    206                 partial=self.partial,
    207             )
    208         else:
    209             raise ValueError("Invalid truncation level `%s`." % level)
    210 
    211     @classmethod
    212     def coerce(cls, version_string, partial=False):
    213         """Coerce an arbitrary version string into a semver-compatible one.
    214 
    215         The rule is:
    216         - If not enough components, fill minor/patch with zeroes; unless
    217           partial=True
    218         - If more than 3 dot-separated components, extra components are "build"
    219           data. If some "build" data already appeared, append it to the
    220           extra components
    221 
    222         Examples:
    223             >>> Version.coerce('0.1')
    224             Version(0, 1, 0)
    225             >>> Version.coerce('0.1.2.3')
    226             Version(0, 1, 2, (), ('3',))
    227             >>> Version.coerce('0.1.2.3+4')
    228             Version(0, 1, 2, (), ('3', '4'))
    229             >>> Version.coerce('0.1+2-3+4_5')
    230             Version(0, 1, 0, (), ('2-3', '4-5'))
    231         """
    232         base_re = re.compile(r'^\d+(?:\.\d+(?:\.\d+)?)?')
    233 
    234         match = base_re.match(version_string)
    235         if not match:
    236             raise ValueError(
    237                 "Version string lacks a numerical component: %r"
    238                 % version_string
    239             )
    240 
    241         version = version_string[:match.end()]
    242         if not partial:
    243             # We need a not-partial version.
    244             while version.count('.') < 2:
    245                 version += '.0'
    246 
    247         # Strip leading zeros in components
    248         # Version is of the form nn, nn.pp or nn.pp.qq
    249         version = '.'.join(
    250             # If the part was '0', we end up with an empty string.
    251             part.lstrip('0') or '0'
    252             for part in version.split('.')
    253         )
    254 
    255         if match.end() == len(version_string):
    256             return Version(version, partial=partial)
    257 
    258         rest = version_string[match.end():]
    259 
    260         # Cleanup the 'rest'
    261         rest = re.sub(r'[^a-zA-Z0-9+.-]', '-', rest)
    262 
    263         if rest[0] == '+':
    264             # A 'build' component
    265             prerelease = ''
    266             build = rest[1:]
    267         elif rest[0] == '.':
    268             # An extra version component, probably 'build'
    269             prerelease = ''
    270             build = rest[1:]
    271         elif rest[0] == '-':
    272             rest = rest[1:]
    273             if '+' in rest:
    274                 prerelease, build = rest.split('+', 1)
    275             else:
    276                 prerelease, build = rest, ''
    277         elif '+' in rest:
    278             prerelease, build = rest.split('+', 1)
    279         else:
    280             prerelease, build = rest, ''
    281 
    282         build = build.replace('+', '.')
    283 
    284         if prerelease:
    285             version = '%s-%s' % (version, prerelease)
    286         if build:
    287             version = '%s+%s' % (version, build)
    288 
    289         return cls(version, partial=partial)
    290 
    291     @classmethod
    292     def parse(cls, version_string, partial=False, coerce=False):
    293         """Parse a version string into a Version() object.
    294 
    295         Args:
    296             version_string (str), the version string to parse
    297             partial (bool), whether to accept incomplete input
    298             coerce (bool), whether to try to map the passed in string into a
    299                 valid Version.
    300         """
    301         if not version_string:
    302             raise ValueError('Invalid empty version string: %r' % version_string)
    303 
    304         if partial:
    305             version_re = cls.partial_version_re
    306         else:
    307             version_re = cls.version_re
    308 
    309         match = version_re.match(version_string)
    310         if not match:
    311             raise ValueError('Invalid version string: %r' % version_string)
    312 
    313         major, minor, patch, prerelease, build = match.groups()
    314 
    315         if _has_leading_zero(major):
    316             raise ValueError("Invalid leading zero in major: %r" % version_string)
    317         if _has_leading_zero(minor):
    318             raise ValueError("Invalid leading zero in minor: %r" % version_string)
    319         if _has_leading_zero(patch):
    320             raise ValueError("Invalid leading zero in patch: %r" % version_string)
    321 
    322         major = int(major)
    323         minor = cls._coerce(minor, partial)
    324         patch = cls._coerce(patch, partial)
    325 
    326         if prerelease is None:
    327             if partial and (build is None):
    328                 # No build info, strip here
    329                 return (major, minor, patch, None, None)
    330             else:
    331                 prerelease = ()
    332         elif prerelease == '':
    333             prerelease = ()
    334         else:
    335             prerelease = tuple(prerelease.split('.'))
    336             cls._validate_identifiers(prerelease, allow_leading_zeroes=False)
    337 
    338         if build is None:
    339             if partial:
    340                 build = None
    341             else:
    342                 build = ()
    343         elif build == '':
    344             build = ()
    345         else:
    346             build = tuple(build.split('.'))
    347             cls._validate_identifiers(build, allow_leading_zeroes=True)
    348 
    349         return (major, minor, patch, prerelease, build)
    350 
    351     @classmethod
    352     def _validate_identifiers(cls, identifiers, allow_leading_zeroes=False):
    353         for item in identifiers:
    354             if not item:
    355                 raise ValueError(
    356                     "Invalid empty identifier %r in %r"
    357                     % (item, '.'.join(identifiers))
    358                 )
    359 
    360             if item[0] == '0' and item.isdigit() and item != '0' and not allow_leading_zeroes:
    361                 raise ValueError("Invalid leading zero in identifier %r" % item)
    362 
    363     @classmethod
    364     def _validate_kwargs(cls, major, minor, patch, prerelease, build, partial):
    365         if (
    366                 major != int(major)
    367                 or minor != cls._coerce(minor, partial)
    368                 or patch != cls._coerce(patch, partial)
    369                 or prerelease is None and not partial
    370                 or build is None and not partial
    371         ):
    372             raise ValueError(
    373                 "Invalid kwargs to Version(major=%r, minor=%r, patch=%r, "
    374                 "prerelease=%r, build=%r, partial=%r" % (
    375                     major, minor, patch, prerelease, build, partial
    376                 ))
    377         if prerelease is not None:
    378             cls._validate_identifiers(prerelease, allow_leading_zeroes=False)
    379         if build is not None:
    380             cls._validate_identifiers(build, allow_leading_zeroes=True)
    381 
    382     def __iter__(self):
    383         return iter((self.major, self.minor, self.patch, self.prerelease, self.build))
    384 
    385     def __str__(self):
    386         version = '%d' % self.major
    387         if self.minor is not None:
    388             version = '%s.%d' % (version, self.minor)
    389         if self.patch is not None:
    390             version = '%s.%d' % (version, self.patch)
    391 
    392         if self.prerelease or (self.partial and self.prerelease == () and self.build is None):
    393             version = '%s-%s' % (version, '.'.join(self.prerelease))
    394         if self.build or (self.partial and self.build == ()):
    395             version = '%s+%s' % (version, '.'.join(self.build))
    396         return version
    397 
    398     def __repr__(self):
    399         return '%s(%r%s)' % (
    400             self.__class__.__name__,
    401             str(self),
    402             ', partial=True' if self.partial else '',
    403         )
    404 
    405     def __hash__(self):
    406         # We don't include 'partial', since this is strictly equivalent to having
    407         # at least a field being `None`.
    408         return hash((self.major, self.minor, self.patch, self.prerelease, self.build))
    409 
    410     @property
    411     def precedence_key(self):
    412         if self.prerelease:
    413             prerelease_key = tuple(
    414                 NumericIdentifier(part) if re.match(r'^[0-9]+$', part) else AlphaIdentifier(part)
    415                 for part in self.prerelease
    416             )
    417         else:
    418             prerelease_key = (
    419                 MaxIdentifier(),
    420             )
    421 
    422         return (
    423             self.major,
    424             self.minor,
    425             self.patch,
    426             prerelease_key,
    427         )
    428 
    429     def __cmp__(self, other):
    430         if not isinstance(other, self.__class__):
    431             return NotImplemented
    432         if self < other:
    433             return -1
    434         elif self > other:
    435             return 1
    436         elif self == other:
    437             return 0
    438         else:
    439             return NotImplemented
    440 
    441     def __eq__(self, other):
    442         if not isinstance(other, self.__class__):
    443             return NotImplemented
    444         return (
    445             self.major == other.major
    446             and self.minor == other.minor
    447             and self.patch == other.patch
    448             and (self.prerelease or ()) == (other.prerelease or ())
    449             and (self.build or ()) == (other.build or ())
    450         )
    451 
    452     def __ne__(self, other):
    453         if not isinstance(other, self.__class__):
    454             return NotImplemented
    455         return tuple(self) != tuple(other)
    456 
    457     def __lt__(self, other):
    458         if not isinstance(other, self.__class__):
    459             return NotImplemented
    460         return self.precedence_key < other.precedence_key
    461 
    462     def __le__(self, other):
    463         if not isinstance(other, self.__class__):
    464             return NotImplemented
    465         return self.precedence_key <= other.precedence_key
    466 
    467     def __gt__(self, other):
    468         if not isinstance(other, self.__class__):
    469             return NotImplemented
    470         return self.precedence_key > other.precedence_key
    471 
    472     def __ge__(self, other):
    473         if not isinstance(other, self.__class__):
    474             return NotImplemented
    475         return self.precedence_key >= other.precedence_key
    476 
    477 
    478 class SpecItem(object):
    479     """A requirement specification."""
    480 
    481     KIND_ANY = '*'
    482     KIND_LT = '<'
    483     KIND_LTE = '<='
    484     KIND_EQUAL = '=='
    485     KIND_SHORTEQ = '='
    486     KIND_EMPTY = ''
    487     KIND_GTE = '>='
    488     KIND_GT = '>'
    489     KIND_NEQ = '!='
    490     KIND_CARET = '^'
    491     KIND_TILDE = '~'
    492     KIND_COMPATIBLE = '~='
    493 
    494     # Map a kind alias to its full version
    495     KIND_ALIASES = {
    496         KIND_SHORTEQ: KIND_EQUAL,
    497         KIND_EMPTY: KIND_EQUAL,
    498     }
    499 
    500     re_spec = re.compile(r'^(<|<=||=|==|>=|>|!=|\^|~|~=)(\d.*)$')
    501 
    502     def __init__(self, requirement_string, _warn=True):
    503         if _warn:
    504             warnings.warn(
    505                 "The `SpecItem` class will be removed in 3.0.",
    506                 DeprecationWarning,
    507                 stacklevel=2,
    508             )
    509         kind, spec = self.parse(requirement_string)
    510         self.kind = kind
    511         self.spec = spec
    512         self._clause = Spec(requirement_string).clause
    513 
    514     @classmethod
    515     def parse(cls, requirement_string):
    516         if not requirement_string:
    517             raise ValueError("Invalid empty requirement specification: %r" % requirement_string)
    518 
    519         # Special case: the 'any' version spec.
    520         if requirement_string == '*':
    521             return (cls.KIND_ANY, '')
    522 
    523         match = cls.re_spec.match(requirement_string)
    524         if not match:
    525             raise ValueError("Invalid requirement specification: %r" % requirement_string)
    526 
    527         kind, version = match.groups()
    528         if kind in cls.KIND_ALIASES:
    529             kind = cls.KIND_ALIASES[kind]
    530 
    531         spec = Version(version, partial=True)
    532         if spec.build is not None and kind not in (cls.KIND_EQUAL, cls.KIND_NEQ):
    533             raise ValueError(
    534                 "Invalid requirement specification %r: build numbers have no ordering."
    535                 % requirement_string
    536             )
    537         return (kind, spec)
    538 
    539     @classmethod
    540     def from_matcher(cls, matcher):
    541         if matcher == Always():
    542             return cls('*', _warn=False)
    543         elif matcher == Never():
    544             return cls('<0.0.0-', _warn=False)
    545         elif isinstance(matcher, Range):
    546             return cls('%s%s' % (matcher.operator, matcher.target), _warn=False)
    547 
    548     def match(self, version):
    549         return self._clause.match(version)
    550 
    551     def __str__(self):
    552         return '%s%s' % (self.kind, self.spec)
    553 
    554     def __repr__(self):
    555         return '<SpecItem: %s %r>' % (self.kind, self.spec)
    556 
    557     def __eq__(self, other):
    558         if not isinstance(other, SpecItem):
    559             return NotImplemented
    560         return self.kind == other.kind and self.spec == other.spec
    561 
    562     def __hash__(self):
    563         return hash((self.kind, self.spec))
    564 
    565 
    566 def compare(v1, v2):
    567     return Version(v1).__cmp__(Version(v2))
    568 
    569 
    570 def match(spec, version):
    571     return Spec(spec).match(Version(version))
    572 
    573 
    574 def validate(version_string):
    575     """Validates a version string against the SemVer specification."""
    576     try:
    577         Version.parse(version_string)
    578         return True
    579     except ValueError:
    580         return False
    581 
    582 
    583 DEFAULT_SYNTAX = 'simple'
    584 
    585 
    586 class BaseSpec(object):
    587     """A specification of compatible versions.
    588 
    589     Usage:
    590     >>> Spec('>=1.0.0', syntax='npm')
    591 
    592     A version matches a specification if it matches any
    593     of the clauses of that specification.
    594 
    595     Internally, a Spec is AnyOf(
    596         AllOf(Matcher, Matcher, Matcher),
    597         AllOf(...),
    598     )
    599     """
    600     SYNTAXES = {}
    601 
    602     @classmethod
    603     def register_syntax(cls, subclass):
    604         syntax = subclass.SYNTAX
    605         if syntax is None:
    606             raise ValueError("A Spec needs its SYNTAX field to be set.")
    607         elif syntax in cls.SYNTAXES:
    608             raise ValueError(
    609                 "Duplicate syntax for %s: %r, %r"
    610                 % (syntax, cls.SYNTAXES[syntax], subclass)
    611             )
    612         cls.SYNTAXES[syntax] = subclass
    613         return subclass
    614 
    615     def __init__(self, expression):
    616         super(BaseSpec, self).__init__()
    617         self.expression = expression
    618         self.clause = self._parse_to_clause(expression)
    619 
    620     @classmethod
    621     def parse(cls, expression, syntax=DEFAULT_SYNTAX):
    622         """Convert a syntax-specific expression into a BaseSpec instance."""
    623         return cls.SYNTAXES[syntax](expression)
    624 
    625     @classmethod
    626     def _parse_to_clause(cls, expression):
    627         """Converts an expression to a clause."""
    628         raise NotImplementedError()
    629 
    630     def filter(self, versions):
    631         """Filter an iterable of versions satisfying the Spec."""
    632         for version in versions:
    633             if self.match(version):
    634                 yield version
    635 
    636     def match(self, version):
    637         """Check whether a Version satisfies the Spec."""
    638         return self.clause.match(version)
    639 
    640     def select(self, versions):
    641         """Select the best compatible version among an iterable of options."""
    642         options = list(self.filter(versions))
    643         if options:
    644             return max(options)
    645         return None
    646 
    647     def __contains__(self, version):
    648         """Whether `version in self`."""
    649         if isinstance(version, Version):
    650             return self.match(version)
    651         return False
    652 
    653     def __eq__(self, other):
    654         if not isinstance(other, self.__class__):
    655             return NotImplemented
    656 
    657         return self.clause == other.clause
    658 
    659     def __hash__(self):
    660         return hash(self.clause)
    661 
    662     def __str__(self):
    663         return self.expression
    664 
    665     def __repr__(self):
    666         return '<%s: %r>' % (self.__class__.__name__, self.expression)
    667 
    668 
    669 class Clause(object):
    670     __slots__ = []
    671 
    672     def match(self, version):
    673         raise NotImplementedError()
    674 
    675     def __and__(self, other):
    676         raise NotImplementedError()
    677 
    678     def __or__(self, other):
    679         raise NotImplementedError()
    680 
    681     def __eq__(self, other):
    682         raise NotImplementedError()
    683 
    684     def prettyprint(self, indent='\t'):
    685         """Pretty-print the clause.
    686         """
    687         return '\n'.join(self._pretty()).replace('\t', indent)
    688 
    689     def _pretty(self):
    690         """Actual pretty-printing logic.
    691 
    692         Yields:
    693             A list of string. Indentation is performed with \t.
    694         """
    695         yield repr(self)
    696 
    697     def __ne__(self, other):
    698         return not self == other
    699 
    700     def simplify(self):
    701         return self
    702 
    703 
    704 class AnyOf(Clause):
    705     __slots__ = ['clauses']
    706 
    707     def __init__(self, *clauses):
    708         super(AnyOf, self).__init__()
    709         self.clauses = frozenset(clauses)
    710 
    711     def match(self, version):
    712         return any(c.match(version) for c in self.clauses)
    713 
    714     def simplify(self):
    715         subclauses = set()
    716         for clause in self.clauses:
    717             simplified = clause.simplify()
    718             if isinstance(simplified, AnyOf):
    719                 subclauses |= simplified.clauses
    720             elif simplified == Never():
    721                 continue
    722             else:
    723                 subclauses.add(simplified)
    724         if len(subclauses) == 1:
    725             return subclauses.pop()
    726         return AnyOf(*subclauses)
    727 
    728     def __hash__(self):
    729         return hash((AnyOf, self.clauses))
    730 
    731     def __iter__(self):
    732         return iter(self.clauses)
    733 
    734     def __eq__(self, other):
    735         return isinstance(other, self.__class__) and self.clauses == other.clauses
    736 
    737     def __and__(self, other):
    738         if isinstance(other, AllOf):
    739             return other & self
    740         elif isinstance(other, Matcher) or isinstance(other, AnyOf):
    741             return AllOf(self, other)
    742         else:
    743             return NotImplemented
    744 
    745     def __or__(self, other):
    746         if isinstance(other, AnyOf):
    747             clauses = list(self.clauses | other.clauses)
    748         elif isinstance(other, Matcher) or isinstance(other, AllOf):
    749             clauses = list(self.clauses | set([other]))
    750         else:
    751             return NotImplemented
    752         return AnyOf(*clauses)
    753 
    754     def __repr__(self):
    755         return 'AnyOf(%s)' % ', '.join(sorted(repr(c) for c in self.clauses))
    756 
    757     def _pretty(self):
    758         yield 'AnyOF('
    759         for clause in self.clauses:
    760             lines = list(clause._pretty())
    761             for line in lines[:-1]:
    762                 yield '\t' + line
    763             yield '\t' + lines[-1] + ','
    764         yield ')'
    765 
    766 
    767 class AllOf(Clause):
    768     __slots__ = ['clauses']
    769 
    770     def __init__(self, *clauses):
    771         super(AllOf, self).__init__()
    772         self.clauses = frozenset(clauses)
    773 
    774     def match(self, version):
    775         return all(clause.match(version) for clause in self.clauses)
    776 
    777     def simplify(self):
    778         subclauses = set()
    779         for clause in self.clauses:
    780             simplified = clause.simplify()
    781             if isinstance(simplified, AllOf):
    782                 subclauses |= simplified.clauses
    783             elif simplified == Always():
    784                 continue
    785             else:
    786                 subclauses.add(simplified)
    787         if len(subclauses) == 1:
    788             return subclauses.pop()
    789         return AllOf(*subclauses)
    790 
    791     def __hash__(self):
    792         return hash((AllOf, self.clauses))
    793 
    794     def __iter__(self):
    795         return iter(self.clauses)
    796 
    797     def __eq__(self, other):
    798         return isinstance(other, self.__class__) and self.clauses == other.clauses
    799 
    800     def __and__(self, other):
    801         if isinstance(other, Matcher) or isinstance(other, AnyOf):
    802             clauses = list(self.clauses | set([other]))
    803         elif isinstance(other, AllOf):
    804             clauses = list(self.clauses | other.clauses)
    805         else:
    806             return NotImplemented
    807         return AllOf(*clauses)
    808 
    809     def __or__(self, other):
    810         if isinstance(other, AnyOf):
    811             return other | self
    812         elif isinstance(other, Matcher):
    813             return AnyOf(self, AllOf(other))
    814         elif isinstance(other, AllOf):
    815             return AnyOf(self, other)
    816         else:
    817             return NotImplemented
    818 
    819     def __repr__(self):
    820         return 'AllOf(%s)' % ', '.join(sorted(repr(c) for c in self.clauses))
    821 
    822     def _pretty(self):
    823         yield 'AllOF('
    824         for clause in self.clauses:
    825             lines = list(clause._pretty())
    826             for line in lines[:-1]:
    827                 yield '\t' + line
    828             yield '\t' + lines[-1] + ','
    829         yield ')'
    830 
    831 
    832 class Matcher(Clause):
    833     __slots__ = []
    834 
    835     def __and__(self, other):
    836         if isinstance(other, AllOf):
    837             return other & self
    838         elif isinstance(other, Matcher) or isinstance(other, AnyOf):
    839             return AllOf(self, other)
    840         else:
    841             return NotImplemented
    842 
    843     def __or__(self, other):
    844         if isinstance(other, AnyOf):
    845             return other | self
    846         elif isinstance(other, Matcher) or isinstance(other, AllOf):
    847             return AnyOf(self, other)
    848         else:
    849             return NotImplemented
    850 
    851 
    852 class Never(Matcher):
    853     __slots__ = []
    854 
    855     def match(self, version):
    856         return False
    857 
    858     def __hash__(self):
    859         return hash((Never,))
    860 
    861     def __eq__(self, other):
    862         return isinstance(other, self.__class__)
    863 
    864     def __and__(self, other):
    865         return self
    866 
    867     def __or__(self, other):
    868         return other
    869 
    870     def __repr__(self):
    871         return 'Never()'
    872 
    873 
    874 class Always(Matcher):
    875     __slots__ = []
    876 
    877     def match(self, version):
    878         return True
    879 
    880     def __hash__(self):
    881         return hash((Always,))
    882 
    883     def __eq__(self, other):
    884         return isinstance(other, self.__class__)
    885 
    886     def __and__(self, other):
    887         return other
    888 
    889     def __or__(self, other):
    890         return self
    891 
    892     def __repr__(self):
    893         return 'Always()'
    894 
    895 
    896 class Range(Matcher):
    897     OP_EQ = '=='
    898     OP_GT = '>'
    899     OP_GTE = '>='
    900     OP_LT = '<'
    901     OP_LTE = '<='
    902     OP_NEQ = '!='
    903 
    904     # <1.2.3 matches 1.2.3-a1
    905     PRERELEASE_ALWAYS = 'always'
    906     # <1.2.3 does not match 1.2.3-a1
    907     PRERELEASE_NATURAL = 'natural'
    908     # 1.2.3-a1 is only considered if target == 1.2.3-xxx
    909     PRERELEASE_SAMEPATCH = 'same-patch'
    910 
    911     # 1.2.3 matches 1.2.3+*
    912     BUILD_IMPLICIT = 'implicit'
    913     # 1.2.3 matches only 1.2.3, not 1.2.3+4
    914     BUILD_STRICT = 'strict'
    915 
    916     __slots__ = ['operator', 'target', 'prerelease_policy', 'build_policy']
    917 
    918     def __init__(self, operator, target, prerelease_policy=PRERELEASE_NATURAL, build_policy=BUILD_IMPLICIT):
    919         super(Range, self).__init__()
    920         if target.build and operator not in (self.OP_EQ, self.OP_NEQ):
    921             raise ValueError(
    922                 "Invalid range %s%s: build numbers have no ordering."
    923                 % (operator, target))
    924         self.operator = operator
    925         self.target = target
    926         self.prerelease_policy = prerelease_policy
    927         self.build_policy = self.BUILD_STRICT if target.build else build_policy
    928 
    929     def match(self, version):
    930         if self.build_policy != self.BUILD_STRICT:
    931             version = version.truncate('prerelease')
    932 
    933         if version.prerelease:
    934             same_patch = self.target.truncate() == version.truncate()
    935 
    936             if self.prerelease_policy == self.PRERELEASE_SAMEPATCH and not same_patch:
    937                 return False
    938 
    939         if self.operator == self.OP_EQ:
    940             if self.build_policy == self.BUILD_STRICT:
    941                 return (
    942                     self.target.truncate('prerelease') == version.truncate('prerelease')
    943                     and version.build == self.target.build
    944                 )
    945             return version == self.target
    946         elif self.operator == self.OP_GT:
    947             return version > self.target
    948         elif self.operator == self.OP_GTE:
    949             return version >= self.target
    950         elif self.operator == self.OP_LT:
    951             if (
    952                 version.prerelease
    953                 and self.prerelease_policy == self.PRERELEASE_NATURAL
    954                 and version.truncate() == self.target.truncate()
    955                 and not self.target.prerelease
    956             ):
    957                 return False
    958             return version < self.target
    959         elif self.operator == self.OP_LTE:
    960             return version <= self.target
    961         else:
    962             assert self.operator == self.OP_NEQ
    963             if self.build_policy == self.BUILD_STRICT:
    964                 return not (
    965                     self.target.truncate('prerelease') == version.truncate('prerelease')
    966                     and version.build == self.target.build
    967                 )
    968 
    969             if (
    970                 version.prerelease
    971                 and self.prerelease_policy == self.PRERELEASE_NATURAL
    972                 and version.truncate() == self.target.truncate()
    973                 and not self.target.prerelease
    974             ):
    975                 return False
    976             return version != self.target
    977 
    978     def __hash__(self):
    979         return hash((Range, self.operator, self.target, self.prerelease_policy))
    980 
    981     def __eq__(self, other):
    982         return (
    983             isinstance(other, self.__class__)
    984             and self.operator == other.operator
    985             and self.target == other.target
    986             and self.prerelease_policy == other.prerelease_policy
    987         )
    988 
    989     def __str__(self):
    990         return '%s%s' % (self.operator, self.target)
    991 
    992     def __repr__(self):
    993         policy_part = (
    994             '' if self.prerelease_policy == self.PRERELEASE_NATURAL
    995             else ', prerelease_policy=%r' % self.prerelease_policy
    996         ) + (
    997             '' if self.build_policy == self.BUILD_IMPLICIT
    998             else ', build_policy=%r' % self.build_policy
    999         )
   1000         return 'Range(%r, %r%s)' % (
   1001             self.operator,
   1002             self.target,
   1003             policy_part,
   1004         )
   1005 
   1006 
   1007 @BaseSpec.register_syntax
   1008 class SimpleSpec(BaseSpec):
   1009 
   1010     SYNTAX = 'simple'
   1011 
   1012     @classmethod
   1013     def _parse_to_clause(cls, expression):
   1014         return cls.Parser.parse(expression)
   1015 
   1016     class Parser:
   1017         NUMBER = r'\*|0|[1-9][0-9]*'
   1018         NAIVE_SPEC = re.compile(r"""^
   1019             (?P<op><|<=||=|==|>=|>|!=|\^|~|~=)
   1020             (?P<major>{nb})(?:\.(?P<minor>{nb})(?:\.(?P<patch>{nb}))?)?
   1021             (?:-(?P<prerel>[a-z0-9A-Z.-]*))?
   1022             (?:\+(?P<build>[a-z0-9A-Z.-]*))?
   1023             $
   1024             """.format(nb=NUMBER),
   1025             re.VERBOSE,
   1026         )
   1027 
   1028         @classmethod
   1029         def parse(cls, expression):
   1030             blocks = expression.split(',')
   1031             clause = Always()
   1032             for block in blocks:
   1033                 if not cls.NAIVE_SPEC.match(block):
   1034                     raise ValueError("Invalid simple block %r" % block)
   1035                 clause &= cls.parse_block(block)
   1036 
   1037             return clause
   1038 
   1039         PREFIX_CARET = '^'
   1040         PREFIX_TILDE = '~'
   1041         PREFIX_COMPATIBLE = '~='
   1042         PREFIX_EQ = '=='
   1043         PREFIX_NEQ = '!='
   1044         PREFIX_GT = '>'
   1045         PREFIX_GTE = '>='
   1046         PREFIX_LT = '<'
   1047         PREFIX_LTE = '<='
   1048 
   1049         PREFIX_ALIASES = {
   1050             '=': PREFIX_EQ,
   1051             '': PREFIX_EQ,
   1052         }
   1053 
   1054         EMPTY_VALUES = ['*', 'x', 'X', None]
   1055 
   1056         @classmethod
   1057         def parse_block(cls, expr):
   1058             if not cls.NAIVE_SPEC.match(expr):
   1059                 raise ValueError("Invalid simple spec component: %r" % expr)
   1060             prefix, major_t, minor_t, patch_t, prerel, build = cls.NAIVE_SPEC.match(expr).groups()
   1061             prefix = cls.PREFIX_ALIASES.get(prefix, prefix)
   1062 
   1063             major = None if major_t in cls.EMPTY_VALUES else int(major_t)
   1064             minor = None if minor_t in cls.EMPTY_VALUES else int(minor_t)
   1065             patch = None if patch_t in cls.EMPTY_VALUES else int(patch_t)
   1066 
   1067             if major is None:  # '*'
   1068                 target = Version(major=0, minor=0, patch=0)
   1069                 if prefix not in (cls.PREFIX_EQ, cls.PREFIX_GTE):
   1070                     raise ValueError("Invalid simple spec: %r" % expr)
   1071             elif minor is None:
   1072                 target = Version(major=major, minor=0, patch=0)
   1073             elif patch is None:
   1074                 target = Version(major=major, minor=minor, patch=0)
   1075             else:
   1076                 target = Version(
   1077                     major=major,
   1078                     minor=minor,
   1079                     patch=patch,
   1080                     prerelease=prerel.split('.') if prerel else (),
   1081                     build=build.split('.') if build else (),
   1082                 )
   1083 
   1084             if (major is None or minor is None or patch is None) and (prerel or build):
   1085                 raise ValueError("Invalid simple spec: %r" % expr)
   1086 
   1087             if build is not None and prefix not in (cls.PREFIX_EQ, cls.PREFIX_NEQ):
   1088                 raise ValueError("Invalid simple spec: %r" % expr)
   1089 
   1090             if prefix == cls.PREFIX_CARET:
   1091                 # Accept anything with the same most-significant digit
   1092                 if target.major:
   1093                     high = target.next_major()
   1094                 elif target.minor:
   1095                     high = target.next_minor()
   1096                 else:
   1097                     high = target.next_patch()
   1098                 return Range(Range.OP_GTE, target) & Range(Range.OP_LT, high)
   1099 
   1100             elif prefix == cls.PREFIX_TILDE:
   1101                 assert major is not None
   1102                 # Accept any higher patch in the same minor
   1103                 # Might go higher if the initial version was a partial
   1104                 if minor is None:
   1105                     high = target.next_major()
   1106                 else:
   1107                     high = target.next_minor()
   1108                 return Range(Range.OP_GTE, target) & Range(Range.OP_LT, high)
   1109 
   1110             elif prefix == cls.PREFIX_COMPATIBLE:
   1111                 assert major is not None
   1112                 # ~1 is 1.0.0..2.0.0; ~=2.2 is 2.2.0..3.0.0; ~=1.4.5 is 1.4.5..1.5.0
   1113                 if minor is None or patch is None:
   1114                     # We got a partial version
   1115                     high = target.next_major()
   1116                 else:
   1117                     high = target.next_minor()
   1118                 return Range(Range.OP_GTE, target) & Range(Range.OP_LT, high)
   1119 
   1120             elif prefix == cls.PREFIX_EQ:
   1121                 if major is None:
   1122                     return Range(Range.OP_GTE, target)
   1123                 elif minor is None:
   1124                     return Range(Range.OP_GTE, target) & Range(Range.OP_LT, target.next_major())
   1125                 elif patch is None:
   1126                     return Range(Range.OP_GTE, target) & Range(Range.OP_LT, target.next_patch())
   1127                 elif build == '':
   1128                     return Range(Range.OP_EQ, target, build_policy=Range.BUILD_STRICT)
   1129                 else:
   1130                     return Range(Range.OP_EQ, target)
   1131 
   1132             elif prefix == cls.PREFIX_NEQ:
   1133                 assert major is not None
   1134                 if minor is None:
   1135                     # !=1.x => <1.0.0 || >=2.0.0
   1136                     return Range(Range.OP_LT, target) | Range(Range.OP_GTE, target.next_major())
   1137                 elif patch is None:
   1138                     # !=1.2.x => <1.2.0 || >=1.3.0
   1139                     return Range(Range.OP_LT, target) | Range(Range.OP_GTE, target.next_minor())
   1140                 elif prerel == '':
   1141                     # !=1.2.3-
   1142                     return Range(Range.OP_NEQ, target, prerelease_policy=Range.PRERELEASE_ALWAYS)
   1143                 elif build == '':
   1144                     # !=1.2.3+ or !=1.2.3-a2+
   1145                     return Range(Range.OP_NEQ, target, build_policy=Range.BUILD_STRICT)
   1146                 else:
   1147                     return Range(Range.OP_NEQ, target)
   1148 
   1149             elif prefix == cls.PREFIX_GT:
   1150                 assert major is not None
   1151                 if minor is None:
   1152                     # >1.x => >=2.0
   1153                     return Range(Range.OP_GTE, target.next_major())
   1154                 elif patch is None:
   1155                     return Range(Range.OP_GTE, target.next_minor())
   1156                 else:
   1157                     return Range(Range.OP_GT, target)
   1158 
   1159             elif prefix == cls.PREFIX_GTE:
   1160                 return Range(Range.OP_GTE, target)
   1161 
   1162             elif prefix == cls.PREFIX_LT:
   1163                 assert major is not None
   1164                 if prerel == '':
   1165                     # <1.2.3-
   1166                     return Range(Range.OP_LT, target, prerelease_policy=Range.PRERELEASE_ALWAYS)
   1167                 return Range(Range.OP_LT, target)
   1168 
   1169             else:
   1170                 assert prefix == cls.PREFIX_LTE
   1171                 assert major is not None
   1172                 if minor is None:
   1173                     # <=1.x => <2.0
   1174                     return Range(Range.OP_LT, target.next_major())
   1175                 elif patch is None:
   1176                     return Range(Range.OP_LT, target.next_minor())
   1177                 else:
   1178                     return Range(Range.OP_LTE, target)
   1179 
   1180 
   1181 class LegacySpec(SimpleSpec):
   1182     def __init__(self, *expressions):
   1183         warnings.warn(
   1184             "The Spec() class will be removed in 3.1; use SimpleSpec() instead.",
   1185             PendingDeprecationWarning,
   1186             stacklevel=2,
   1187         )
   1188 
   1189         if len(expressions) > 1:
   1190             warnings.warn(
   1191                 "Passing 2+ arguments to SimpleSpec will be removed in 3.0; concatenate them with ',' instead.",
   1192                 DeprecationWarning,
   1193                 stacklevel=2,
   1194             )
   1195         expression = ','.join(expressions)
   1196         super(LegacySpec, self).__init__(expression)
   1197 
   1198     @property
   1199     def specs(self):
   1200         return list(self)
   1201 
   1202     def __iter__(self):
   1203         warnings.warn(
   1204             "Iterating over the components of a SimpleSpec object will be removed in 3.0.",
   1205             DeprecationWarning,
   1206             stacklevel=2,
   1207         )
   1208         try:
   1209             clauses = list(self.clause)
   1210         except TypeError:  # Not an iterable
   1211             clauses = [self.clause]
   1212         for clause in clauses:
   1213             yield SpecItem.from_matcher(clause)
   1214 
   1215 
   1216 Spec = LegacySpec
   1217 
   1218 
   1219 @BaseSpec.register_syntax
   1220 class NpmSpec(BaseSpec):
   1221     SYNTAX = 'npm'
   1222 
   1223     @classmethod
   1224     def _parse_to_clause(cls, expression):
   1225         return cls.Parser.parse(expression)
   1226 
   1227     class Parser:
   1228         JOINER = '||'
   1229         HYPHEN = ' - '
   1230 
   1231         NUMBER = r'x|X|\*|0|[1-9][0-9]*'
   1232         PART = r'[a-zA-Z0-9.-]*'
   1233         NPM_SPEC_BLOCK = re.compile(r"""
   1234             ^(?:v)?                     # Strip optional initial v
   1235             (?P<op><|<=|>=|>|=|\^|~|)   # Operator, can be empty
   1236             (?P<major>{nb})(?:\.(?P<minor>{nb})(?:\.(?P<patch>{nb}))?)?
   1237             (?:-(?P<prerel>{part}))?    # Optional re-release
   1238             (?:\+(?P<build>{part}))?    # Optional build
   1239             $""".format(nb=NUMBER, part=PART),
   1240             re.VERBOSE,
   1241         )
   1242 
   1243         @classmethod
   1244         def range(cls, operator, target):
   1245             return Range(operator, target, prerelease_policy=Range.PRERELEASE_SAMEPATCH)
   1246 
   1247         @classmethod
   1248         def parse(cls, expression):
   1249             result = Never()
   1250             groups = expression.split(cls.JOINER)
   1251             for group in groups:
   1252                 group = group.strip()
   1253                 if not group:
   1254                     group = '>=0.0.0'
   1255 
   1256                 subclauses = []
   1257                 if cls.HYPHEN in group:
   1258                     low, high = group.split(cls.HYPHEN, 2)
   1259                     subclauses = cls.parse_simple('>=' + low) + cls.parse_simple('<=' + high)
   1260 
   1261                 else:
   1262                     blocks = group.split(' ')
   1263                     for block in blocks:
   1264                         if not cls.NPM_SPEC_BLOCK.match(block):
   1265                             raise ValueError("Invalid NPM block in %r: %r" % (expression, block))
   1266 
   1267                         subclauses.extend(cls.parse_simple(block))
   1268 
   1269                 prerelease_clauses = []
   1270                 non_prerel_clauses = []
   1271                 for clause in subclauses:
   1272                     if clause.target.prerelease:
   1273                         if clause.operator in (Range.OP_GT, Range.OP_GTE):
   1274                             prerelease_clauses.append(Range(
   1275                                 operator=Range.OP_LT,
   1276                                 target=Version(
   1277                                     major=clause.target.major,
   1278                                     minor=clause.target.minor,
   1279                                     patch=clause.target.patch + 1,
   1280                                 ),
   1281                                 prerelease_policy=Range.PRERELEASE_ALWAYS,
   1282                             ))
   1283                         elif clause.operator in (Range.OP_LT, Range.OP_LTE):
   1284                             prerelease_clauses.append(Range(
   1285                                 operator=Range.OP_GTE,
   1286                                 target=Version(
   1287                                     major=clause.target.major,
   1288                                     minor=clause.target.minor,
   1289                                     patch=0,
   1290                                     prerelease=(),
   1291                                 ),
   1292                                 prerelease_policy=Range.PRERELEASE_ALWAYS,
   1293                             ))
   1294                         prerelease_clauses.append(clause)
   1295                         non_prerel_clauses.append(cls.range(
   1296                             operator=clause.operator,
   1297                             target=clause.target.truncate(),
   1298                         ))
   1299                     else:
   1300                         non_prerel_clauses.append(clause)
   1301                 if prerelease_clauses:
   1302                     result |= AllOf(*prerelease_clauses)
   1303                 result |= AllOf(*non_prerel_clauses)
   1304 
   1305             return result
   1306 
   1307         PREFIX_CARET = '^'
   1308         PREFIX_TILDE = '~'
   1309         PREFIX_EQ = '='
   1310         PREFIX_GT = '>'
   1311         PREFIX_GTE = '>='
   1312         PREFIX_LT = '<'
   1313         PREFIX_LTE = '<='
   1314 
   1315         PREFIX_ALIASES = {
   1316             '': PREFIX_EQ,
   1317         }
   1318 
   1319         PREFIX_TO_OPERATOR = {
   1320             PREFIX_EQ: Range.OP_EQ,
   1321             PREFIX_LT: Range.OP_LT,
   1322             PREFIX_LTE: Range.OP_LTE,
   1323             PREFIX_GTE: Range.OP_GTE,
   1324             PREFIX_GT: Range.OP_GT,
   1325         }
   1326 
   1327         EMPTY_VALUES = ['*', 'x', 'X', None]
   1328 
   1329         @classmethod
   1330         def parse_simple(cls, simple):
   1331             match = cls.NPM_SPEC_BLOCK.match(simple)
   1332 
   1333             prefix, major_t, minor_t, patch_t, prerel, build = match.groups()
   1334 
   1335             prefix = cls.PREFIX_ALIASES.get(prefix, prefix)
   1336             major = None if major_t in cls.EMPTY_VALUES else int(major_t)
   1337             minor = None if minor_t in cls.EMPTY_VALUES else int(minor_t)
   1338             patch = None if patch_t in cls.EMPTY_VALUES else int(patch_t)
   1339 
   1340             if build is not None and prefix not in [cls.PREFIX_EQ]:
   1341                 # Ignore the 'build' part when not comparing to a specific part.
   1342                 build = None
   1343 
   1344             if major is None:  # '*', 'x', 'X'
   1345                 target = Version(major=0, minor=0, patch=0)
   1346                 if prefix not in [cls.PREFIX_EQ, cls.PREFIX_GTE]:
   1347                     raise ValueError("Invalid expression %r" % simple)
   1348                 prefix = cls.PREFIX_GTE
   1349             elif minor is None:
   1350                 target = Version(major=major, minor=0, patch=0)
   1351             elif patch is None:
   1352                 target = Version(major=major, minor=minor, patch=0)
   1353             else:
   1354                 target = Version(
   1355                     major=major,
   1356                     minor=minor,
   1357                     patch=patch,
   1358                     prerelease=prerel.split('.') if prerel else (),
   1359                     build=build.split('.') if build else (),
   1360                 )
   1361 
   1362             if (major is None or minor is None or patch is None) and (prerel or build):
   1363                 raise ValueError("Invalid NPM spec: %r" % simple)
   1364 
   1365             if prefix == cls.PREFIX_CARET:
   1366                 if target.major:  # ^1.2.4 => >=1.2.4 <2.0.0 ; ^1.x => >=1.0.0 <2.0.0
   1367                     high = target.truncate().next_major()
   1368                 elif target.minor:  # ^0.1.2 => >=0.1.2 <0.2.0
   1369                     high = target.truncate().next_minor()
   1370                 elif minor is None:  # ^0.x => >=0.0.0 <1.0.0
   1371                     high = target.truncate().next_major()
   1372                 elif patch is None:  # ^0.2.x => >=0.2.0 <0.3.0
   1373                     high = target.truncate().next_minor()
   1374                 else:  # ^0.0.1 => >=0.0.1 <0.0.2
   1375                     high = target.truncate().next_patch()
   1376                 return [cls.range(Range.OP_GTE, target), cls.range(Range.OP_LT, high)]
   1377 
   1378             elif prefix == cls.PREFIX_TILDE:
   1379                 assert major is not None
   1380                 if minor is None:  # ~1.x => >=1.0.0 <2.0.0
   1381                     high = target.next_major()
   1382                 else:  # ~1.2.x => >=1.2.0 <1.3.0; ~1.2.3 => >=1.2.3 <1.3.0
   1383                     high = target.next_minor()
   1384                 return [cls.range(Range.OP_GTE, target), cls.range(Range.OP_LT, high)]
   1385 
   1386             elif prefix == cls.PREFIX_EQ:
   1387                 if major is None:
   1388                     return [cls.range(Range.OP_GTE, target)]
   1389                 elif minor is None:
   1390                     return [cls.range(Range.OP_GTE, target), cls.range(Range.OP_LT, target.next_major())]
   1391                 elif patch is None:
   1392                     return [cls.range(Range.OP_GTE, target), cls.range(Range.OP_LT, target.next_minor())]
   1393                 else:
   1394                     return [cls.range(Range.OP_EQ, target)]
   1395 
   1396             elif prefix == cls.PREFIX_GT:
   1397                 assert major is not None
   1398                 if minor is None:  # >1.x
   1399                     return [cls.range(Range.OP_GTE, target.next_major())]
   1400                 elif patch is None:  # >1.2.x => >=1.3.0
   1401                     return [cls.range(Range.OP_GTE, target.next_minor())]
   1402                 else:
   1403                     return [cls.range(Range.OP_GT, target)]
   1404 
   1405             elif prefix == cls.PREFIX_GTE:
   1406                 return [cls.range(Range.OP_GTE, target)]
   1407 
   1408             elif prefix == cls.PREFIX_LT:
   1409                 assert major is not None
   1410                 return [cls.range(Range.OP_LT, target)]
   1411 
   1412             else:
   1413                 assert prefix == cls.PREFIX_LTE
   1414                 assert major is not None
   1415                 if minor is None:  # <=1.x => <2.0.0
   1416                     return [cls.range(Range.OP_LT, target.next_major())]
   1417                 elif patch is None:  # <=1.2.x => <1.3.0
   1418                     return [cls.range(Range.OP_LT, target.next_minor())]
   1419                 else:
   1420                     return [cls.range(Range.OP_LTE, target)]