From 2072d9876f813dde19ea856751b076265c1d0305 Mon Sep 17 00:00:00 2001 From: Avasam Date: Thu, 17 Oct 2024 12:20:14 -0400 Subject: [PATCH] Using `dict` as an `OrderedDict` and allowed using `dict` as an ordered type in `setuptools.dist.check_requirements` --- newsfragments/4575.feature.rst | 1 + setuptools/command/_requirestxt.py | 3 +-- setuptools/command/egg_info.py | 5 +---- setuptools/dist.py | 29 +++++++++++++------------- setuptools/tests/test_core_metadata.py | 1 - setuptools/tests/test_dist.py | 10 ++------- 6 files changed, 20 insertions(+), 29 deletions(-) create mode 100644 newsfragments/4575.feature.rst diff --git a/newsfragments/4575.feature.rst b/newsfragments/4575.feature.rst new file mode 100644 index 0000000000..64ab49830f --- /dev/null +++ b/newsfragments/4575.feature.rst @@ -0,0 +1 @@ +Allowed using `dict` as an ordered type in ``setuptools.dist.check_requirements`` -- by :user:`Avasam` diff --git a/setuptools/command/_requirestxt.py b/setuptools/command/_requirestxt.py index b87476d6f4..d426f5dffb 100644 --- a/setuptools/command/_requirestxt.py +++ b/setuptools/command/_requirestxt.py @@ -18,12 +18,11 @@ from packaging.requirements import Requirement from .. import _reqs +from .._reqs import _StrOrIter # dict can work as an ordered set _T = TypeVar("_T") _Ordered = Dict[_T, None] -_ordered = dict -_StrOrIter = _reqs._StrOrIter def _prepare( diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index f4d3a2a57e..bc6c677878 100644 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -2,7 +2,6 @@ Create a distribution's .egg-info directory and contents""" -import collections import functools import os import re @@ -211,11 +210,9 @@ def save_version_info(self, filename): build tag. Install build keys in a deterministic order to avoid arbitrary reordering on subsequent builds. """ - egg_info = collections.OrderedDict() # follow the order these keys would have been added # when PYTHONHASHSEED=0 - egg_info['tag_build'] = self.tags() - egg_info['tag_date'] = 0 + egg_info = dict(tag_build=self.tags(), tag_date=0) edit_config(filename, dict(egg_info=egg_info)) def finalize_options(self): diff --git a/setuptools/dist.py b/setuptools/dist.py index f22e3eea54..d6b8e08214 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -11,12 +11,12 @@ from typing import ( TYPE_CHECKING, Any, + Dict, List, MutableMapping, - NoReturn, + Sequence, Tuple, Union, - overload, ) from more_itertools import partition, unique_everseen @@ -30,7 +30,6 @@ command as _, # noqa: F401 # imported for side-effects ) from ._importlib import metadata -from ._reqs import _StrOrIter from .config import pyprojecttoml, setupcfg from .discovery import ConfigDiscovery from .monkey import get_unpatched @@ -63,7 +62,13 @@ """ _Sequence: TypeAlias = Union[Tuple[str, ...], List[str]] # This is how stringifying _Sequence would look in Python 3.10 -_requence_type_repr = "tuple[str, ...] | list[str]" +_sequence_type_repr = "tuple[str, ...] | list[str]" +_OrderedStrSequence: TypeAlias = Union[str, Dict[str, Any], Sequence[str]] +""" +:meta private: +Avoid single-use iterable. Disallow sets. +A poor approximation of an OrderedSequence (dict doesn't match a Sequence). +""" def __getattr__(name: str) -> Any: # pragma: no cover @@ -97,7 +102,7 @@ def assert_string_list(dist, attr: str, value: _Sequence) -> None: assert ''.join(value) != value except (TypeError, ValueError, AttributeError, AssertionError) as e: raise DistutilsSetupError( - f"{attr!r} must be of type <{_requence_type_repr}> (got {value!r})" + f"{attr!r} must be of type <{_sequence_type_repr}> (got {value!r})" ) from e @@ -173,15 +178,11 @@ def invalid_unless_false(dist, attr, value): raise DistutilsSetupError(f"{attr} is invalid.") -@overload -def check_requirements(dist, attr: str, value: set | dict) -> NoReturn: ... -@overload -def check_requirements(dist, attr: str, value: _StrOrIter) -> None: ... -def check_requirements(dist, attr: str, value: _StrOrIter) -> None: +def check_requirements(dist, attr: str, value: _OrderedStrSequence) -> None: """Verify that install_requires is a valid requirements list""" try: list(_reqs.parse(value)) - if isinstance(value, (dict, set)): + if isinstance(value, set): raise TypeError("Unordered types are not allowed") except (TypeError, ValueError) as error: msg = ( @@ -810,7 +811,7 @@ def _exclude_misc(self, name: str, value: _Sequence) -> None: """Handle 'exclude()' for list/tuple attrs without a special handler""" if not isinstance(value, _sequence): raise DistutilsSetupError( - f"{name}: setting must be of type <{_requence_type_repr}> (got {value!r})" + f"{name}: setting must be of type <{_sequence_type_repr}> (got {value!r})" ) try: old = getattr(self, name) @@ -828,7 +829,7 @@ def _include_misc(self, name: str, value: _Sequence) -> None: if not isinstance(value, _sequence): raise DistutilsSetupError( - f"{name}: setting must be of type <{_requence_type_repr}> (got {value!r})" + f"{name}: setting must be of type <{_sequence_type_repr}> (got {value!r})" ) try: old = getattr(self, name) @@ -870,7 +871,7 @@ def exclude(self, **attrs): def _exclude_packages(self, packages: _Sequence) -> None: if not isinstance(packages, _sequence): raise DistutilsSetupError( - f"packages: setting must be of type <{_requence_type_repr}> (got {packages!r})" + f"packages: setting must be of type <{_sequence_type_repr}> (got {packages!r})" ) list(map(self.exclude_package, packages)) diff --git a/setuptools/tests/test_core_metadata.py b/setuptools/tests/test_core_metadata.py index 34828ac750..51d4a10810 100644 --- a/setuptools/tests/test_core_metadata.py +++ b/setuptools/tests/test_core_metadata.py @@ -310,7 +310,6 @@ def test_parity_with_metadata_from_pypa_wheel(tmp_path): python_requires=">=3.8", install_requires=""" packaging==23.2 - ordered-set==3.1.1 more-itertools==8.8.0; extra == "other" jaraco.text==3.7.0 importlib-resources==5.10.2; python_version<"3.8" diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py index fde0de99ac..1bc4923032 100644 --- a/setuptools/tests/test_dist.py +++ b/setuptools/tests/test_dist.py @@ -1,4 +1,3 @@ -import collections import os import re import urllib.parse @@ -72,15 +71,10 @@ def sdist_with_index(distname, version): def test_provides_extras_deterministic_order(): - extras = collections.OrderedDict() - extras['a'] = ['foo'] - extras['b'] = ['bar'] - attrs = dict(extras_require=extras) + attrs = dict(extras_require=dict(a=['foo'], b=['bar'])) dist = Distribution(attrs) assert list(dist.metadata.provides_extras) == ['a', 'b'] - attrs['extras_require'] = collections.OrderedDict( - reversed(list(attrs['extras_require'].items())) - ) + attrs['extras_require'] = dict(reversed(attrs['extras_require'].items())) dist = Distribution(attrs) assert list(dist.metadata.provides_extras) == ['b', 'a']