From 89ab70daea0e1bf77eb8310a42fb7312a8076d0f Mon Sep 17 00:00:00 2001
From: Paul Moore
Date: Fri, 19 Jan 2024 10:24:56 +0000
Subject: [PATCH 1/2] Allow creating a SpecifierSet from a list of specifiers
---
CHANGELOG.rst | 1 +
src/packaging/specifiers.py | 21 +++++++++++++++------
tests/test_specifiers.py | 6 ++++++
3 files changed, 22 insertions(+), 6 deletions(-)
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 153c2af6..73568358 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -14,6 +14,7 @@ Changelog
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
~~~~~~~~~~~~~~~~~
diff --git a/src/packaging/specifiers.py b/src/packaging/specifiers.py
index 2d015bab..3f88fd12 100644
--- a/src/packaging/specifiers.py
+++ b/src/packaging/specifiers.py
@@ -701,13 +701,17 @@ class SpecifierSet(BaseSpecifier):
"""
def __init__(
- self, specifiers: str = "", prereleases: Optional[bool] = None
+ self,
+ specifiers: Union[str, Iterable[Specifier]] = "",
+ prereleases: Optional[bool] = 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
@@ -718,12 +722,17 @@ def __init__(
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.
diff --git a/tests/test_specifiers.py b/tests/test_specifiers.py
index a79b860e..de360981 100644
--- a/tests/test_specifiers.py
+++ b/tests/test_specifiers.py
@@ -650,6 +650,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
From 805bf15ddf954101208d281bb2d20b7116d896e7 Mon Sep 17 00:00:00 2001
From: Paul Moore
Date: Thu, 8 Aug 2024 09:28:52 +0100
Subject: [PATCH 2/2] Change from Union to | in type annotation
---
src/packaging/specifiers.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/packaging/specifiers.py b/src/packaging/specifiers.py
index 54a12238..5fe042b3 100644
--- a/src/packaging/specifiers.py
+++ b/src/packaging/specifiers.py
@@ -696,7 +696,7 @@ class SpecifierSet(BaseSpecifier):
def __init__(
self,
- specifiers: Union[str, Iterable[Specifier]] = "",
+ specifiers: str | Iterable[Specifier] = "",
prereleases: bool | None = None,
) -> None:
"""Initialize a SpecifierSet instance.