Version Ranges¶
A VersionRange is the set of Version
values accepted by a SpecifierSet, viewed as
intervals on the PEP 440 ordering. It supports intersection, union, and
complement, so tooling that combines many requirements, such as a resolver,
can work on the intervals directly.
Usage¶
>>> from packaging.ranges import VersionRange
>>> from packaging.specifiers import SpecifierSet
>>> from packaging.version import Version
>>> # Build a range from a specifier set
>>> r = SpecifierSet(">=1.0,<2.0").to_range()
>>> r
<VersionRange '[1.0, 2.0.dev0)'>
>>> Version("1.5") in r
True
>>> Version("2.0") in r
False
>>> # Combine ranges with set algebra
>>> a = SpecifierSet(">=1.0").to_range()
>>> b = SpecifierSet("<2.0").to_range()
>>> a & b == r
True
>>> # The complement is every other version
>>> Version("0.5") in ~r
True
>>> # Filter an iterable of versions
>>> list(r.filter(["0.9", "1.5", "2.0"]))
['1.5']
>>> # An unsatisfiable set produces the empty range
>>> SpecifierSet(">=2.0,<1.0").to_range().is_empty
True
Comparing ranges¶
Equality on a VersionRange is structural: two ranges are equal only
when they behave the same under VersionRange.contains() and
VersionRange.filter(). Equality covers the pre-release policy and any
=== admission, not only the versions matched. Intersection can change one
of those without changing the version set, so the textbook subset test
a & b == a can report a false negative. Use the algebra and
VersionRange.is_empty for set relations:
>>> from packaging.specifiers import SpecifierSet
>>> a = SpecifierSet(">=1.0").to_range()
>>> b = SpecifierSet(">=1.0a1").to_range()
>>> # a is a subset of b (every version >=1.0 is also >=1.0a1), but b
>>> # admits pre-releases, so ``a & b`` and ``a`` differ only in policy:
>>> a & b == a
False
>>> (a & ~b).is_empty # subset: a has no version outside b
True
>>> (a & b).is_empty # disjoint: a and b share no version
False
Different specifiers for the same set of versions canonicalize to one form, so
they compare equal. >1.0a1 excludes 1.0a1’s post-releases per PEP 440,
so its smallest member is 1.0a2.dev0, exactly the set of >=1.0a2.dev0:
>>> SpecifierSet(">1.0a1").to_range() == SpecifierSet(">=1.0a2.dev0").to_range()
True
The pre-release policy is still part of equality. <1.0.post0.dev0 and
<=1.0 cover the same versions, but the first admits pre-releases by default
(its bound is a .dev release) while the second does not, so they are not
substitutable and compare unequal:
>>> SpecifierSet("<1.0.post0.dev0").to_range() == SpecifierSet("<=1.0").to_range()
False
Reference¶
Public VersionRange API.
A set-algebra view of the versions accepted by a
SpecifierSet. Ranges support intersection,
union, and complement; membership and filtering match the originating
specifier set.
- class packaging.ranges.VersionRange¶
A set of
Versionvalues accepted by aSpecifierSet.Construct via
to_range(), or with thefull(),empty(), andsingleton()class methods. Compose withintersection(),union(), andcomplement()(or the&/|/~operators). Test membership withinorcontains(), filter an iterable withfilter().The configured pre-release policy of the originating specifier set carries onto the range and controls whether pre-releases are admitted under
in,contains(), andfilter().intersection()andunion()require both operands to share the same policy.>>> r = SpecifierSet(">=1.0,<2.0").to_range() >>> "1.5" in r True >>> "2.0" in r False >>> SpecifierSet(">=2.0,<1.0").to_range().is_empty True
PEP 440’s
===operator matches a candidate string verbatim (case-insensitive) rather than a set of versions. Ranges built from===specifiers still support membership and set operations; matching follows the literal-equality rule.- static __new__(cls, *args, **kwargs)¶
- Parameters:
- Return type:
- classmethod empty(*, prereleases=None)¶
Return the empty range. No version satisfies it.
>>> VersionRange.empty().is_empty True >>> "1.0" in VersionRange.empty() False
- Parameters:
prereleases (bool | None)
- Return type:
- classmethod full(*, admit_arbitrary=True, prereleases=None)¶
Return the full range. Every PEP 440 version satisfies it.
admit_arbitrary=Falserestricts the range to PEP 440 versions only (matching the same versions asSpecifierSet(">=0.dev0").to_range()); its complement isempty(). The flag propagates through set algebra and is part of equality. DefaultTrueso thatr & full()preservesr’s own flag structurally.>>> "1.0" in VersionRange.full() True >>> "garbage" in VersionRange.full() True >>> "garbage" in VersionRange.full(admit_arbitrary=False) False
- Parameters:
- Return type:
- classmethod singleton(version, *, prereleases=None)¶
Return the strict singleton range
{version}.Built as the closed interval
[version, version]with strict equality.Specifier("==V")matchesV+localtoo, so the strict singleton is narrower:>>> "1.0+local" in VersionRange.singleton("1.0") False >>> "1.0+local" in SpecifierSet("==1.0").to_range() True
- Raises:
packaging.version.InvalidVersion – if version is a string that does not parse as a PEP 440 version.
- Parameters:
- Return type:
- intersection(other)¶
Range containing exactly the versions in both self and other.
Both operands must share the same configured pre-release policy; otherwise
ValueErroris raised.>>> a = SpecifierSet(">=1.0").to_range() >>> b = SpecifierSet("<2.0").to_range() >>> a.intersection(b) == SpecifierSet(">=1.0,<2.0").to_range() True
- Parameters:
other (VersionRange)
- Return type:
- union(other)¶
Range containing every version in self or other.
Both operands must share the same configured pre-release policy; otherwise
ValueErroris raised.>>> a = VersionRange.singleton("1.0") >>> b = VersionRange.singleton("2.0") >>> "1.0" in a.union(b) and "2.0" in a.union(b) True >>> "1.5" in a.union(b) False
- Parameters:
other (VersionRange)
- Return type:
- complement()¶
Range containing every version not in self.
Preserves the configured pre-release policy. Within the PEP 440 universe (no
===literals and no arbitrary admission) double negation holds; for===ranges complement is one-way.>>> r = SpecifierSet(">=1.0").to_range() >>> "0.5" in r.complement() True >>> "1.5" in r.complement() False >>> r.complement().complement() == r True
- Return type:
- __and__(other)¶
Operator alias for
intersection().- Parameters:
other (object)
- Return type:
- __invert__()¶
Operator alias for
complement().- Return type:
- filter(iterable: Iterable[UnparsedVersionVar], prereleases: bool | None = None, key: None = None) Iterator[UnparsedVersionVar]¶
- filter(iterable: Iterable[T], prereleases: bool | None = None, key: Callable[[T], UnparsedVersion] = None) Iterator[T]
Yield items from iterable whose version falls inside the range.
With prereleases
Nonethe PEP 440 default applies: pre-releases are buffered and only emitted if no final release in iterable is in range.The signature mirrors
filter().>>> r = SpecifierSet(">=1.0,<2.0").to_range() >>> list(r.filter(["0.9", "1.5", "2.0"])) ['1.5']
- property is_empty: bool¶
Trueif no version or string satisfies this range.Agrees with
is_unsatisfiable(), including the pre-release policy: a range whose only members are pre-releases is empty when that policy excludes them.>>> SpecifierSet(">=2,<1").to_range().is_empty True >>> SpecifierSet(">=1,<2").to_range().is_empty False >>> SpecifierSet("==1.0a1", prereleases=False).to_range().is_empty True
- contains(item, prereleases=None, installed=None)¶
Return whether item is contained in this range.
- Parameters:
- Return type:
Unparsable strings do not match, except where the full
SpecifierSetwould also match: the full range admits any string, and a===range admits items equal to the literal case-insensitively.>>> r = SpecifierSet(">=1.0,<2.0").to_range() >>> r.contains("1.5") True >>> r.contains("2.0") False
- __contains__(item)¶
Return whether item is contained in this range.
Forwards to
contains()with default arguments.>>> "1.5" in SpecifierSet(">=1.0,<2.0").to_range() True
- __eq__(other)¶
Structural equality.
Two ranges compare equal when every input to
contains()andfilter()agrees: the bounds, the===admit/reject literals, the arbitrary-string flag, and both the configured and resolved pre-release policies. Equal ranges therefore filter identically.Different specifiers for the same set fold to one canonical bound form, so they compare equal.
>1.0a1excludes1.0a1’s post-releases, so its smallest member is1.0a2.dev0, the same set as>=1.0a2.dev0:>>> SpecifierSet(">1.0a1").to_range() == SpecifierSet(">=1.0a2.dev0").to_range() True
The pre-release policy is still part of equality, so two ranges with the same versions but different policies stay unequal:
>>> le, lt = SpecifierSet("<=1.0"), SpecifierSet("<1.0.post0.dev0") >>> le.to_range() == lt.to_range() False
>>> r = SpecifierSet(">=1.0,<2.0").to_range() >>> r == SpecifierSet(">=1.0,<2.0").to_range() True