Skip to content

Commit

Permalink
Allow creating a SpecifierSet from a list of specifiers (#777)
Browse files Browse the repository at this point in the history
* Allow creating a SpecifierSet from a list of specifiers

* Change from Union to | in type annotation
  • Loading branch information
pfmoore authored Sep 5, 2024
1 parent 680c31a commit 2b06f90
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ No unreleased changes.
have not been provided (:issue:`733`)
* Fix a bug preventing the use of the built in ``ExceptionGroup`` on versions of
Python that support it (:issue:`725`)
* Support creating a ``SpecifierSet`` from an iterable of ``Specifier`` objects (:issue:`775`)

23.2 - 2023-10-01
~~~~~~~~~~~~~~~~~
Expand Down
23 changes: 17 additions & 6 deletions src/packaging/specifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -694,12 +694,18 @@ class SpecifierSet(BaseSpecifier):
specifiers (``>=3.0,!=3.1``), or no specifier at all.
"""

def __init__(self, specifiers: str = "", prereleases: bool | None = None) -> None:
def __init__(
self,
specifiers: str | Iterable[Specifier] = "",
prereleases: bool | None = None,
) -> None:
"""Initialize a SpecifierSet instance.
:param specifiers:
The string representation of a specifier or a comma-separated list of
specifiers which will be parsed and normalized before use.
May also be an iterable of ``Specifier`` instances, which will be used
as is.
:param prereleases:
This tells the SpecifierSet if it should accept prerelease versions if
applicable or not. The default of ``None`` will autodetect it from the
Expand All @@ -710,12 +716,17 @@ def __init__(self, specifiers: str = "", prereleases: bool | None = None) -> Non
raised.
"""

# Split on `,` to break each individual specifier into it's own item, and
# strip each item to remove leading/trailing whitespace.
split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()]
if isinstance(specifiers, str):
# Split on `,` to break each individual specifier into its own item, and
# strip each item to remove leading/trailing whitespace.
split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()]

# Make each individual specifier a Specifier and save in a frozen set for later.
self._specs = frozenset(map(Specifier, split_specifiers))
# Make each individual specifier a Specifier and save in a frozen set
# for later.
self._specs = frozenset(map(Specifier, split_specifiers))
else:
# Save the supplied specifiers in a frozen set.
self._specs = frozenset(specifiers)

# Store our prereleases value so we can use it later to determine if
# we accept prereleases or not.
Expand Down
6 changes: 6 additions & 0 deletions tests/test_specifiers.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,12 @@ def test_empty_specifier(self, version):
assert parse(version) in spec
assert spec.contains(parse(version))

def test_create_from_specifiers(self):
spec_strs = [">=1.0", "!=1.1", "!=1.2", "<2.0"]
specs = [Specifier(s) for s in spec_strs]
spec = SpecifierSet(iter(specs))
assert set(spec) == set(specs)

def test_specifier_prereleases_explicit(self):
spec = SpecifierSet()
assert not spec.prereleases
Expand Down

0 comments on commit 2b06f90

Please sign in to comment.