Repository: pypa/setuptools

Issue:
check_specifier() raises an exception when not passed as string
`dist.check_specifier()` is used to validate that the given argument is valid. The documentation and the error message it produces clearly states the value should be a string. However, if the value is something other than a string, `DistutilsSetupError` is not raised. This is because `SpecifierSet` class `.split()` assuming that the value is a string. This causes:

```
AttributeError: 'lXXX' object has no attribute 'split'
```
In my case, it happened to be a list containing one valid specifier.

Instead of failing with this error, `check_specifier()` should raise `DistutilsSetupError` to clearly indicate that it was given a value of the wrong type.

This is related to #1869 in that it is hitting the same error. But, I'm suggesting the type be properly checked instead of allowing other types.
check_specifier() raises an exception when not passed as string
`dist.check_specifier()` is used to validate that the given argument is valid. The documentation and the error message it produces clearly states the value should be a string. However, if the value is something other than a string, `DistutilsSetupError` is not raised. This is because `SpecifierSet` class `.split()` assuming that the value is a string. This causes:

```
AttributeError: 'lXXX' object has no attribute 'split'
```
In my case, it happened to be a list containing one valid specifier.

Instead of failing with this error, `check_specifier()` should raise `DistutilsSetupError` to clearly indicate that it was given a value of the wrong type.

This is related to #1869 in that it is hitting the same error. But, I'm suggesting the type be properly checked instead of allowing other types.


Discussion:

Resolving patch:
diff --git a/setuptools/dist.py b/setuptools/dist.py
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -292,7 +292,7 @@ def check_specifier(dist, attr, value):
     """Verify that value is a valid version specifier"""
     try:
         packaging.specifiers.SpecifierSet(value)
-    except packaging.specifiers.InvalidSpecifier as error:
+    except (packaging.specifiers.InvalidSpecifier, AttributeError) as error:
         tmpl = (
             "{attr!r} must be a string "
             "containing valid version specifiers; {error}"


Test patch:
diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py
--- a/setuptools/tests/test_dist.py
+++ b/setuptools/tests/test_dist.py
@@ -9,6 +9,7 @@
     _get_unpatched,
     check_package_data,
     DistDeprecationWarning,
+    check_specifier,
 )
 from setuptools import sic
 from setuptools import Distribution
@@ -323,3 +324,15 @@ def test_check_package_data(package_data, expected_message):
         with pytest.raises(
                 DistutilsSetupError, match=re.escape(expected_message)):
             check_package_data(None, str('package_data'), package_data)
+
+
+def test_check_specifier():
+    # valid specifier value
+    attrs = {'name': 'foo', 'python_requires': '>=3.0, !=3.1'}
+    dist = Distribution(attrs)
+    check_specifier(dist, attrs, attrs['python_requires'])
+
+    # invalid specifier value
+    attrs = {'name': 'foo', 'python_requires': ['>=3.0', '!=3.1']}
+    with pytest.raises(DistutilsSetupError):
+        dist = Distribution(attrs)