From 40c56e200263566a9f49ee7e7c6c17c7627592ec Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 3 Aug 2024 20:38:56 +0200 Subject: [PATCH 01/16] Let _show_help() use commands instead of self.commands Depending on the context, _show_help() is called with a different commands argument. I believe the intent was to adapt the help message to the context. However, it would operate on self.commands instead of the commands argument, which would break this purpose. --- distutils/dist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/distutils/dist.py b/distutils/dist.py index 0a57d60be91..115302b3e74 100644 --- a/distutils/dist.py +++ b/distutils/dist.py @@ -658,7 +658,7 @@ def _show_help( ) print() - for command in self.commands: + for command in commands: if isinstance(command, type) and issubclass(command, Command): klass = command else: From ab13bf2c34ce2e1bce60cc8a752b9a2065a83df5 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Sat, 3 Aug 2024 21:36:13 +0200 Subject: [PATCH 02/16] Enforce flake8-bugbear (B) and isort (I) ruff rules Ignore B028 and B904. --- ruff.toml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ruff.toml b/ruff.toml index b7850b6ddd6..41c9459bdba 100644 --- a/ruff.toml +++ b/ruff.toml @@ -5,6 +5,8 @@ extend-select = [ "W", # local + "B", + "I", "ISC", "RUF010", "RUF100", @@ -26,6 +28,10 @@ ignore = [ "COM819", "ISC001", "ISC002", + + # local + "B028", + "B904", ] [format] From 06acfd262258d809242c74179477af324389e1c7 Mon Sep 17 00:00:00 2001 From: Dimitri Papadopoulos Orfanos <3234522+DimitriPapadopoulos@users.noreply.github.com> Date: Thu, 8 Aug 2024 23:14:35 +0200 Subject: [PATCH 03/16] Update to the latest ruff version (jaraco/skeleton#137) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5a4a7e9166e..ff54405eade 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.1.8 + rev: v0.5.6 hooks: - id: ruff - id: ruff-format From dd30b7600f33ce06a479a73002b950f4a3947759 Mon Sep 17 00:00:00 2001 From: Avasam Date: Thu, 8 Aug 2024 17:19:17 -0400 Subject: [PATCH 04/16] Add Protocols, remove @overload, from `.coveragerc` `exclude_also` (jaraco/skeleton#135) Co-authored-by: Jason R. Coombs --- .coveragerc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.coveragerc b/.coveragerc index 35b98b1df96..2e3f4dd791a 100644 --- a/.coveragerc +++ b/.coveragerc @@ -8,6 +8,8 @@ disable_warnings = [report] show_missing = True exclude_also = - # jaraco/skeleton#97 - @overload + # Exclude common false positives per + # https://coverage.readthedocs.io/en/latest/excluding.html#advanced-exclusion + # Ref jaraco/skeleton#97 and jaraco/skeleton#135 + class .*\bProtocol\): if TYPE_CHECKING: From 3841656c61bad87f922fcba50445b503209b69c2 Mon Sep 17 00:00:00 2001 From: Avasam Date: Mon, 12 Aug 2024 12:13:19 -0400 Subject: [PATCH 05/16] Loosen restrictions on mypy (jaraco/skeleton#136) Based on changes downstream in setuptools. --- mypy.ini | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/mypy.ini b/mypy.ini index b6f972769e6..83b0d15c6b6 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,5 +1,14 @@ [mypy] -ignore_missing_imports = True -# required to support namespace packages -# https://github.com/python/mypy/issues/14057 +# Is the project well-typed? +strict = False + +# Early opt-in even when strict = False +warn_unused_ignores = True +warn_redundant_casts = True +enable_error_code = ignore-without-code + +# Support namespace packages per https://github.com/python/mypy/issues/14057 explicit_package_bases = True + +# Disable overload-overlap due to many false-positives +disable_error_code = overload-overlap From 1a27fd5b8815e65571e6c028d6bef2c1daf61688 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 12 Aug 2024 12:16:15 -0400 Subject: [PATCH 06/16] Split the test dependencies into four classes (test, cover, type, check). (jaraco/skeleton#139) --- pyproject.toml | 25 ++++++++++++++++++++----- tox.ini | 4 ++++ 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 1307e1fa206..31057d85f10 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,14 +28,10 @@ Source = "https://github.com/PROJECT_PATH" test = [ # upstream "pytest >= 6, != 8.1.*", - "pytest-checkdocs >= 2.4", - "pytest-cov", - "pytest-mypy", - "pytest-enabler >= 2.2", - "pytest-ruff >= 0.2.1; sys_platform != 'cygwin'", # local ] + doc = [ # upstream "sphinx >= 3.5", @@ -47,4 +43,23 @@ doc = [ # local ] +check = [ + "pytest-checkdocs >= 2.4", + "pytest-ruff >= 0.2.1; sys_platform != 'cygwin'", +] + +cover = [ + "pytest-cov", +] + +enabler = [ + "pytest-enabler >= 2.2", +] + +type = [ + "pytest-mypy", +] + + + [tool.setuptools_scm] diff --git a/tox.ini b/tox.ini index cc4db36e76f..01f0975f5cb 100644 --- a/tox.ini +++ b/tox.ini @@ -8,6 +8,10 @@ commands = usedevelop = True extras = test + check + cover + enabler + type [testenv:diffcov] description = run tests and check that diff from main is covered From 49d7438a23b7ec7cfca20c4d0ae5083030f631bd Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Fri, 16 Aug 2024 15:28:12 +0100 Subject: [PATCH 07/16] Replace OrderedSet with dict Since Python 3.7, ``dict`` preserve insertion order. This means that ``dict`` is a capable implementation of an ordered set, despite the method names not lining up completely. --- setuptools/dist.py | 5 ++--- setuptools/tests/config/test_setupcfg.py | 2 +- setuptools/tests/test_core_metadata.py | 2 +- setuptools/tests/test_dist.py | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/setuptools/dist.py b/setuptools/dist.py index b2250e3476a..715e8fbb73e 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -11,7 +11,6 @@ from typing import TYPE_CHECKING, MutableMapping from more_itertools import partition, unique_everseen -from ordered_set import OrderedSet from packaging.markers import InvalidMarker, Marker from packaging.specifiers import InvalidSpecifier, SpecifierSet from packaging.version import Version @@ -251,7 +250,7 @@ class Distribution(_Distribution): _DISTUTILS_UNSUPPORTED_METADATA = { 'long_description_content_type': lambda: None, 'project_urls': dict, - 'provides_extras': OrderedSet, + 'provides_extras': dict, # behaves like an ordered set 'license_file': lambda: None, 'license_files': lambda: None, 'install_requires': list, @@ -349,7 +348,7 @@ def _finalize_requires(self): # Setuptools allows a weird ": syntax for extras extra = extra.split(':')[0] if extra: - self.metadata.provides_extras.add(extra) + self.metadata.provides_extras.setdefault(extra) def _normalize_requires(self): """Make sure requirement-related attributes exist and are normalized""" diff --git a/setuptools/tests/config/test_setupcfg.py b/setuptools/tests/config/test_setupcfg.py index 2c50ee2db7f..4f0a7349f55 100644 --- a/setuptools/tests/config/test_setupcfg.py +++ b/setuptools/tests/config/test_setupcfg.py @@ -675,7 +675,7 @@ def test_extras_require(self, tmpdir): 'pdf': ['ReportLab>=1.2', 'RXP'], 'rest': ['docutils>=0.3', 'pack==1.1,==1.3'], } - assert dist.metadata.provides_extras == set(['pdf', 'rest']) + assert set(dist.metadata.provides_extras) == {'pdf', 'rest'} @pytest.mark.parametrize( "config", diff --git a/setuptools/tests/test_core_metadata.py b/setuptools/tests/test_core_metadata.py index 3e2ce65ea38..34828ac750d 100644 --- a/setuptools/tests/test_core_metadata.py +++ b/setuptools/tests/test_core_metadata.py @@ -188,7 +188,7 @@ def test_read_metadata(name, attrs): ('requires', dist_class.get_requires), ('classifiers', dist_class.get_classifiers), ('project_urls', lambda s: getattr(s, 'project_urls', {})), - ('provides_extras', lambda s: getattr(s, 'provides_extras', set())), + ('provides_extras', lambda s: getattr(s, 'provides_extras', {})), ] for attr, getter in tested_attrs: diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py index a5a5e7606d3..597785b519e 100644 --- a/setuptools/tests/test_dist.py +++ b/setuptools/tests/test_dist.py @@ -77,12 +77,12 @@ def test_provides_extras_deterministic_order(): extras['b'] = ['bar'] attrs = dict(extras_require=extras) dist = Distribution(attrs) - assert dist.metadata.provides_extras == ['a', 'b'] + assert list(dist.metadata.provides_extras) == ['a', 'b'] attrs['extras_require'] = collections.OrderedDict( reversed(list(attrs['extras_require'].items())) ) dist = Distribution(attrs) - assert dist.metadata.provides_extras == ['b', 'a'] + assert list(dist.metadata.provides_extras) == ['b', 'a'] CHECK_PACKAGE_DATA_TESTS = ( From d081fbdfc92da4b8bcf01455445cf739ca51d5f1 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Fri, 16 Aug 2024 15:33:52 +0100 Subject: [PATCH 08/16] Remove bundled ordered_set --- pyproject.toml | 1 - .../ordered_set-4.1.0.dist-info/INSTALLER | 1 - .../ordered_set-4.1.0.dist-info/METADATA | 158 ------ .../ordered_set-4.1.0.dist-info/RECORD | 8 - .../ordered_set-4.1.0.dist-info/REQUESTED | 0 .../_vendor/ordered_set-4.1.0.dist-info/WHEEL | 4 - setuptools/_vendor/ordered_set/__init__.py | 536 ------------------ setuptools/_vendor/ordered_set/py.typed | 0 setuptools/tests/test_extern.py | 12 +- 9 files changed, 3 insertions(+), 717 deletions(-) delete mode 100644 setuptools/_vendor/ordered_set-4.1.0.dist-info/INSTALLER delete mode 100644 setuptools/_vendor/ordered_set-4.1.0.dist-info/METADATA delete mode 100644 setuptools/_vendor/ordered_set-4.1.0.dist-info/RECORD delete mode 100644 setuptools/_vendor/ordered_set-4.1.0.dist-info/REQUESTED delete mode 100644 setuptools/_vendor/ordered_set-4.1.0.dist-info/WHEEL delete mode 100644 setuptools/_vendor/ordered_set/__init__.py delete mode 100644 setuptools/_vendor/ordered_set/py.typed diff --git a/pyproject.toml b/pyproject.toml index 1ce17e63ab2..89c2fe890b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -110,7 +110,6 @@ ssl = [] certs = [] core = [ "packaging>=24", - "ordered-set>=3.1.1", "more_itertools>=8.8", "jaraco.text>=3.7", "importlib_resources>=5.10.2; python_version < '3.9'", diff --git a/setuptools/_vendor/ordered_set-4.1.0.dist-info/INSTALLER b/setuptools/_vendor/ordered_set-4.1.0.dist-info/INSTALLER deleted file mode 100644 index a1b589e38a3..00000000000 --- a/setuptools/_vendor/ordered_set-4.1.0.dist-info/INSTALLER +++ /dev/null @@ -1 +0,0 @@ -pip diff --git a/setuptools/_vendor/ordered_set-4.1.0.dist-info/METADATA b/setuptools/_vendor/ordered_set-4.1.0.dist-info/METADATA deleted file mode 100644 index 7aea136818b..00000000000 --- a/setuptools/_vendor/ordered_set-4.1.0.dist-info/METADATA +++ /dev/null @@ -1,158 +0,0 @@ -Metadata-Version: 2.1 -Name: ordered-set -Version: 4.1.0 -Summary: An OrderedSet is a custom MutableSet that remembers its order, so that every -Author-email: Elia Robyn Lake -Requires-Python: >=3.7 -Description-Content-Type: text/markdown -Classifier: Development Status :: 5 - Production/Stable -Classifier: Intended Audience :: Developers -Classifier: License :: OSI Approved :: MIT License -Classifier: Programming Language :: Python -Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.7 -Classifier: Programming Language :: Python :: 3.8 -Classifier: Programming Language :: Python :: 3.9 -Classifier: Programming Language :: Python :: 3.10 -Classifier: Programming Language :: Python :: Implementation :: CPython -Classifier: Programming Language :: Python :: Implementation :: PyPy -Requires-Dist: pytest ; extra == "dev" -Requires-Dist: black ; extra == "dev" -Requires-Dist: mypy ; extra == "dev" -Project-URL: Home, https://github.com/rspeer/ordered-set -Provides-Extra: dev - -[![Pypi](https://img.shields.io/pypi/v/ordered-set.svg)](https://pypi.python.org/pypi/ordered-set) - -An OrderedSet is a mutable data structure that is a hybrid of a list and a set. -It remembers the order of its entries, and every entry has an index number that -can be looked up. - -## Installation - -`ordered_set` is available on PyPI and packaged as a wheel. You can list it -as a dependency of your project, in whatever form that takes. - -To install it into your current Python environment: - - pip install ordered-set - -To install the code for development, after checking out the repository: - - pip install flit - flit install - -## Usage examples - -An OrderedSet is created and used like a set: - - >>> from ordered_set import OrderedSet - - >>> letters = OrderedSet('abracadabra') - - >>> letters - OrderedSet(['a', 'b', 'r', 'c', 'd']) - - >>> 'r' in letters - True - -It is efficient to find the index of an entry in an OrderedSet, or find an -entry by its index. To help with this use case, the `.add()` method returns -the index of the added item, whether it was already in the set or not. - - >>> letters.index('r') - 2 - - >>> letters[2] - 'r' - - >>> letters.add('r') - 2 - - >>> letters.add('x') - 5 - -OrderedSets implement the union (`|`), intersection (`&`), and difference (`-`) -operators like sets do. - - >>> letters |= OrderedSet('shazam') - - >>> letters - OrderedSet(['a', 'b', 'r', 'c', 'd', 'x', 's', 'h', 'z', 'm']) - - >>> letters & set('aeiou') - OrderedSet(['a']) - - >>> letters -= 'abcd' - - >>> letters - OrderedSet(['r', 'x', 's', 'h', 'z', 'm']) - -The `__getitem__()` and `index()` methods have been extended to accept any -iterable except a string, returning a list, to perform NumPy-like "fancy -indexing". - - >>> letters = OrderedSet('abracadabra') - - >>> letters[[0, 2, 3]] - ['a', 'r', 'c'] - - >>> letters.index(['a', 'r', 'c']) - [0, 2, 3] - -OrderedSet implements `__getstate__` and `__setstate__` so it can be pickled, -and implements the abstract base classes `collections.MutableSet` and -`collections.Sequence`. - -OrderedSet can be used as a generic collection type, similar to the collections -in the `typing` module like List, Dict, and Set. For example, you can annotate -a variable as having the type `OrderedSet[str]` or `OrderedSet[Tuple[int, -str]]`. - - -## OrderedSet in data science applications - -An OrderedSet can be used as a bi-directional mapping between a sparse -vocabulary and dense index numbers. As of version 3.1, it accepts NumPy arrays -of index numbers as well as lists. - -This combination of features makes OrderedSet a simple implementation of many -of the things that `pandas.Index` is used for, and many of its operations are -faster than the equivalent pandas operations. - -For further compatibility with pandas.Index, `get_loc` (the pandas method for -looking up a single index) and `get_indexer` (the pandas method for fancy -indexing in reverse) are both aliases for `index` (which handles both cases -in OrderedSet). - - -## Authors - -OrderedSet was implemented by Elia Robyn Lake (maiden name: Robyn Speer). -Jon Crall contributed changes and tests to make it fit the Python set API. -Roman Inflianskas added the original type annotations. - - -## Comparisons - -The original implementation of OrderedSet was a [recipe posted to ActiveState -Recipes][recipe] by Raymond Hettiger, released under the MIT license. - -[recipe]: https://code.activestate.com/recipes/576694-orderedset/ - -Hettiger's implementation kept its content in a doubly-linked list referenced by a -dict. As a result, looking up an item by its index was an O(N) operation, while -deletion was O(1). - -This version makes different trade-offs for the sake of efficient lookups. Its -content is a standard Python list instead of a doubly-linked list. This -provides O(1) lookups by index at the expense of O(N) deletion, as well as -slightly faster iteration. - -In Python 3.6 and later, the built-in `dict` type is inherently ordered. If you -ignore the dictionary values, that also gives you a simple ordered set, with -fast O(1) insertion, deletion, iteration and membership testing. However, `dict` -does not provide the list-like random access features of OrderedSet. You -would have to convert it to a list in O(N) to look up the index of an entry or -look up an entry by its index. - diff --git a/setuptools/_vendor/ordered_set-4.1.0.dist-info/RECORD b/setuptools/_vendor/ordered_set-4.1.0.dist-info/RECORD deleted file mode 100644 index a9875cde4e9..00000000000 --- a/setuptools/_vendor/ordered_set-4.1.0.dist-info/RECORD +++ /dev/null @@ -1,8 +0,0 @@ -ordered_set-4.1.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 -ordered_set-4.1.0.dist-info/METADATA,sha256=FqVN_VUTJTCDQ-vtnmXrbgapDjciET-54gSNJ47sro8,5340 -ordered_set-4.1.0.dist-info/RECORD,, -ordered_set-4.1.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 -ordered_set-4.1.0.dist-info/WHEEL,sha256=jPMR_Dzkc4X4icQtmz81lnNY_kAsfog7ry7qoRvYLXw,81 -ordered_set/__init__.py,sha256=ytazgKsyBKi9uFtBt938yXxQtdat1VCC681s9s0CMqg,17146 -ordered_set/__pycache__/__init__.cpython-312.pyc,, -ordered_set/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/setuptools/_vendor/ordered_set-4.1.0.dist-info/REQUESTED b/setuptools/_vendor/ordered_set-4.1.0.dist-info/REQUESTED deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/setuptools/_vendor/ordered_set-4.1.0.dist-info/WHEEL b/setuptools/_vendor/ordered_set-4.1.0.dist-info/WHEEL deleted file mode 100644 index c727d148239..00000000000 --- a/setuptools/_vendor/ordered_set-4.1.0.dist-info/WHEEL +++ /dev/null @@ -1,4 +0,0 @@ -Wheel-Version: 1.0 -Generator: flit 3.6.0 -Root-Is-Purelib: true -Tag: py3-none-any diff --git a/setuptools/_vendor/ordered_set/__init__.py b/setuptools/_vendor/ordered_set/__init__.py deleted file mode 100644 index e86c70ed80e..00000000000 --- a/setuptools/_vendor/ordered_set/__init__.py +++ /dev/null @@ -1,536 +0,0 @@ -""" -An OrderedSet is a custom MutableSet that remembers its order, so that every -entry has an index that can be looked up. It can also act like a Sequence. - -Based on a recipe originally posted to ActiveState Recipes by Raymond Hettiger, -and released under the MIT license. -""" -import itertools as it -from typing import ( - Any, - Dict, - Iterable, - Iterator, - List, - MutableSet, - AbstractSet, - Sequence, - Set, - TypeVar, - Union, - overload, -) - -SLICE_ALL = slice(None) -__version__ = "4.1.0" - - -T = TypeVar("T") - -# SetLike[T] is either a set of elements of type T, or a sequence, which -# we will convert to an OrderedSet by adding its elements in order. -SetLike = Union[AbstractSet[T], Sequence[T]] -OrderedSetInitializer = Union[AbstractSet[T], Sequence[T], Iterable[T]] - - -def _is_atomic(obj: Any) -> bool: - """ - Returns True for objects which are iterable but should not be iterated in - the context of indexing an OrderedSet. - - When we index by an iterable, usually that means we're being asked to look - up a list of things. - - However, in the case of the .index() method, we shouldn't handle strings - and tuples like other iterables. They're not sequences of things to look - up, they're the single, atomic thing we're trying to find. - - As an example, oset.index('hello') should give the index of 'hello' in an - OrderedSet of strings. It shouldn't give the indexes of each individual - character. - """ - return isinstance(obj, str) or isinstance(obj, tuple) - - -class OrderedSet(MutableSet[T], Sequence[T]): - """ - An OrderedSet is a custom MutableSet that remembers its order, so that - every entry has an index that can be looked up. - - Example: - >>> OrderedSet([1, 1, 2, 3, 2]) - OrderedSet([1, 2, 3]) - """ - - def __init__(self, initial: OrderedSetInitializer[T] = None): - self.items: List[T] = [] - self.map: Dict[T, int] = {} - if initial is not None: - # In terms of duck-typing, the default __ior__ is compatible with - # the types we use, but it doesn't expect all the types we - # support as values for `initial`. - self |= initial # type: ignore - - def __len__(self): - """ - Returns the number of unique elements in the ordered set - - Example: - >>> len(OrderedSet([])) - 0 - >>> len(OrderedSet([1, 2])) - 2 - """ - return len(self.items) - - @overload - def __getitem__(self, index: slice) -> "OrderedSet[T]": - ... - - @overload - def __getitem__(self, index: Sequence[int]) -> List[T]: - ... - - @overload - def __getitem__(self, index: int) -> T: - ... - - # concrete implementation - def __getitem__(self, index): - """ - Get the item at a given index. - - If `index` is a slice, you will get back that slice of items, as a - new OrderedSet. - - If `index` is a list or a similar iterable, you'll get a list of - items corresponding to those indices. This is similar to NumPy's - "fancy indexing". The result is not an OrderedSet because you may ask - for duplicate indices, and the number of elements returned should be - the number of elements asked for. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset[1] - 2 - """ - if isinstance(index, slice) and index == SLICE_ALL: - return self.copy() - elif isinstance(index, Iterable): - return [self.items[i] for i in index] - elif isinstance(index, slice) or hasattr(index, "__index__"): - result = self.items[index] - if isinstance(result, list): - return self.__class__(result) - else: - return result - else: - raise TypeError("Don't know how to index an OrderedSet by %r" % index) - - def copy(self) -> "OrderedSet[T]": - """ - Return a shallow copy of this object. - - Example: - >>> this = OrderedSet([1, 2, 3]) - >>> other = this.copy() - >>> this == other - True - >>> this is other - False - """ - return self.__class__(self) - - # Define the gritty details of how an OrderedSet is serialized as a pickle. - # We leave off type annotations, because the only code that should interact - # with these is a generalized tool such as pickle. - def __getstate__(self): - if len(self) == 0: - # In pickle, the state can't be an empty list. - # We need to return a truthy value, or else __setstate__ won't be run. - # - # This could have been done more gracefully by always putting the state - # in a tuple, but this way is backwards- and forwards- compatible with - # previous versions of OrderedSet. - return (None,) - else: - return list(self) - - def __setstate__(self, state): - if state == (None,): - self.__init__([]) - else: - self.__init__(state) - - def __contains__(self, key: Any) -> bool: - """ - Test if the item is in this ordered set. - - Example: - >>> 1 in OrderedSet([1, 3, 2]) - True - >>> 5 in OrderedSet([1, 3, 2]) - False - """ - return key in self.map - - # Technically type-incompatible with MutableSet, because we return an - # int instead of nothing. This is also one of the things that makes - # OrderedSet convenient to use. - def add(self, key: T) -> int: - """ - Add `key` as an item to this OrderedSet, then return its index. - - If `key` is already in the OrderedSet, return the index it already - had. - - Example: - >>> oset = OrderedSet() - >>> oset.append(3) - 0 - >>> print(oset) - OrderedSet([3]) - """ - if key not in self.map: - self.map[key] = len(self.items) - self.items.append(key) - return self.map[key] - - append = add - - def update(self, sequence: SetLike[T]) -> int: - """ - Update the set with the given iterable sequence, then return the index - of the last element inserted. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset.update([3, 1, 5, 1, 4]) - 4 - >>> print(oset) - OrderedSet([1, 2, 3, 5, 4]) - """ - item_index = 0 - try: - for item in sequence: - item_index = self.add(item) - except TypeError: - raise ValueError( - "Argument needs to be an iterable, got %s" % type(sequence) - ) - return item_index - - @overload - def index(self, key: Sequence[T]) -> List[int]: - ... - - @overload - def index(self, key: T) -> int: - ... - - # concrete implementation - def index(self, key): - """ - Get the index of a given entry, raising an IndexError if it's not - present. - - `key` can be an iterable of entries that is not a string, in which case - this returns a list of indices. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset.index(2) - 1 - """ - if isinstance(key, Iterable) and not _is_atomic(key): - return [self.index(subkey) for subkey in key] - return self.map[key] - - # Provide some compatibility with pd.Index - get_loc = index - get_indexer = index - - def pop(self, index=-1) -> T: - """ - Remove and return item at index (default last). - - Raises KeyError if the set is empty. - Raises IndexError if index is out of range. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset.pop() - 3 - """ - if not self.items: - raise KeyError("Set is empty") - - elem = self.items[index] - del self.items[index] - del self.map[elem] - return elem - - def discard(self, key: T) -> None: - """ - Remove an element. Do not raise an exception if absent. - - The MutableSet mixin uses this to implement the .remove() method, which - *does* raise an error when asked to remove a non-existent item. - - Example: - >>> oset = OrderedSet([1, 2, 3]) - >>> oset.discard(2) - >>> print(oset) - OrderedSet([1, 3]) - >>> oset.discard(2) - >>> print(oset) - OrderedSet([1, 3]) - """ - if key in self: - i = self.map[key] - del self.items[i] - del self.map[key] - for k, v in self.map.items(): - if v >= i: - self.map[k] = v - 1 - - def clear(self) -> None: - """ - Remove all items from this OrderedSet. - """ - del self.items[:] - self.map.clear() - - def __iter__(self) -> Iterator[T]: - """ - Example: - >>> list(iter(OrderedSet([1, 2, 3]))) - [1, 2, 3] - """ - return iter(self.items) - - def __reversed__(self) -> Iterator[T]: - """ - Example: - >>> list(reversed(OrderedSet([1, 2, 3]))) - [3, 2, 1] - """ - return reversed(self.items) - - def __repr__(self) -> str: - if not self: - return "%s()" % (self.__class__.__name__,) - return "%s(%r)" % (self.__class__.__name__, list(self)) - - def __eq__(self, other: Any) -> bool: - """ - Returns true if the containers have the same items. If `other` is a - Sequence, then order is checked, otherwise it is ignored. - - Example: - >>> oset = OrderedSet([1, 3, 2]) - >>> oset == [1, 3, 2] - True - >>> oset == [1, 2, 3] - False - >>> oset == [2, 3] - False - >>> oset == OrderedSet([3, 2, 1]) - False - """ - if isinstance(other, Sequence): - # Check that this OrderedSet contains the same elements, in the - # same order, as the other object. - return list(self) == list(other) - try: - other_as_set = set(other) - except TypeError: - # If `other` can't be converted into a set, it's not equal. - return False - else: - return set(self) == other_as_set - - def union(self, *sets: SetLike[T]) -> "OrderedSet[T]": - """ - Combines all unique items. - Each items order is defined by its first appearance. - - Example: - >>> oset = OrderedSet.union(OrderedSet([3, 1, 4, 1, 5]), [1, 3], [2, 0]) - >>> print(oset) - OrderedSet([3, 1, 4, 5, 2, 0]) - >>> oset.union([8, 9]) - OrderedSet([3, 1, 4, 5, 2, 0, 8, 9]) - >>> oset | {10} - OrderedSet([3, 1, 4, 5, 2, 0, 10]) - """ - cls: type = OrderedSet - if isinstance(self, OrderedSet): - cls = self.__class__ - containers = map(list, it.chain([self], sets)) - items = it.chain.from_iterable(containers) - return cls(items) - - def __and__(self, other: SetLike[T]) -> "OrderedSet[T]": - # the parent implementation of this is backwards - return self.intersection(other) - - def intersection(self, *sets: SetLike[T]) -> "OrderedSet[T]": - """ - Returns elements in common between all sets. Order is defined only - by the first set. - - Example: - >>> oset = OrderedSet.intersection(OrderedSet([0, 1, 2, 3]), [1, 2, 3]) - >>> print(oset) - OrderedSet([1, 2, 3]) - >>> oset.intersection([2, 4, 5], [1, 2, 3, 4]) - OrderedSet([2]) - >>> oset.intersection() - OrderedSet([1, 2, 3]) - """ - cls: type = OrderedSet - items: OrderedSetInitializer[T] = self - if isinstance(self, OrderedSet): - cls = self.__class__ - if sets: - common = set.intersection(*map(set, sets)) - items = (item for item in self if item in common) - return cls(items) - - def difference(self, *sets: SetLike[T]) -> "OrderedSet[T]": - """ - Returns all elements that are in this set but not the others. - - Example: - >>> OrderedSet([1, 2, 3]).difference(OrderedSet([2])) - OrderedSet([1, 3]) - >>> OrderedSet([1, 2, 3]).difference(OrderedSet([2]), OrderedSet([3])) - OrderedSet([1]) - >>> OrderedSet([1, 2, 3]) - OrderedSet([2]) - OrderedSet([1, 3]) - >>> OrderedSet([1, 2, 3]).difference() - OrderedSet([1, 2, 3]) - """ - cls = self.__class__ - items: OrderedSetInitializer[T] = self - if sets: - other = set.union(*map(set, sets)) - items = (item for item in self if item not in other) - return cls(items) - - def issubset(self, other: SetLike[T]) -> bool: - """ - Report whether another set contains this set. - - Example: - >>> OrderedSet([1, 2, 3]).issubset({1, 2}) - False - >>> OrderedSet([1, 2, 3]).issubset({1, 2, 3, 4}) - True - >>> OrderedSet([1, 2, 3]).issubset({1, 4, 3, 5}) - False - """ - if len(self) > len(other): # Fast check for obvious cases - return False - return all(item in other for item in self) - - def issuperset(self, other: SetLike[T]) -> bool: - """ - Report whether this set contains another set. - - Example: - >>> OrderedSet([1, 2]).issuperset([1, 2, 3]) - False - >>> OrderedSet([1, 2, 3, 4]).issuperset({1, 2, 3}) - True - >>> OrderedSet([1, 4, 3, 5]).issuperset({1, 2, 3}) - False - """ - if len(self) < len(other): # Fast check for obvious cases - return False - return all(item in self for item in other) - - def symmetric_difference(self, other: SetLike[T]) -> "OrderedSet[T]": - """ - Return the symmetric difference of two OrderedSets as a new set. - That is, the new set will contain all elements that are in exactly - one of the sets. - - Their order will be preserved, with elements from `self` preceding - elements from `other`. - - Example: - >>> this = OrderedSet([1, 4, 3, 5, 7]) - >>> other = OrderedSet([9, 7, 1, 3, 2]) - >>> this.symmetric_difference(other) - OrderedSet([4, 5, 9, 2]) - """ - cls: type = OrderedSet - if isinstance(self, OrderedSet): - cls = self.__class__ - diff1 = cls(self).difference(other) - diff2 = cls(other).difference(self) - return diff1.union(diff2) - - def _update_items(self, items: list) -> None: - """ - Replace the 'items' list of this OrderedSet with a new one, updating - self.map accordingly. - """ - self.items = items - self.map = {item: idx for (idx, item) in enumerate(items)} - - def difference_update(self, *sets: SetLike[T]) -> None: - """ - Update this OrderedSet to remove items from one or more other sets. - - Example: - >>> this = OrderedSet([1, 2, 3]) - >>> this.difference_update(OrderedSet([2, 4])) - >>> print(this) - OrderedSet([1, 3]) - - >>> this = OrderedSet([1, 2, 3, 4, 5]) - >>> this.difference_update(OrderedSet([2, 4]), OrderedSet([1, 4, 6])) - >>> print(this) - OrderedSet([3, 5]) - """ - items_to_remove = set() # type: Set[T] - for other in sets: - items_as_set = set(other) # type: Set[T] - items_to_remove |= items_as_set - self._update_items([item for item in self.items if item not in items_to_remove]) - - def intersection_update(self, other: SetLike[T]) -> None: - """ - Update this OrderedSet to keep only items in another set, preserving - their order in this set. - - Example: - >>> this = OrderedSet([1, 4, 3, 5, 7]) - >>> other = OrderedSet([9, 7, 1, 3, 2]) - >>> this.intersection_update(other) - >>> print(this) - OrderedSet([1, 3, 7]) - """ - other = set(other) - self._update_items([item for item in self.items if item in other]) - - def symmetric_difference_update(self, other: SetLike[T]) -> None: - """ - Update this OrderedSet to remove items from another set, then - add items from the other set that were not present in this set. - - Example: - >>> this = OrderedSet([1, 4, 3, 5, 7]) - >>> other = OrderedSet([9, 7, 1, 3, 2]) - >>> this.symmetric_difference_update(other) - >>> print(this) - OrderedSet([4, 5, 9, 2]) - """ - items_to_add = [item for item in other if item not in self] - items_to_remove = set(other) - self._update_items( - [item for item in self.items if item not in items_to_remove] + items_to_add - ) diff --git a/setuptools/_vendor/ordered_set/py.typed b/setuptools/_vendor/ordered_set/py.typed deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/setuptools/tests/test_extern.py b/setuptools/tests/test_extern.py index 105279d0846..d7eb3c62c19 100644 --- a/setuptools/tests/test_extern.py +++ b/setuptools/tests/test_extern.py @@ -1,20 +1,14 @@ import importlib import pickle -import ordered_set +import packaging from setuptools import Distribution def test_reimport_extern(): - ordered_set2 = importlib.import_module(ordered_set.__name__) - assert ordered_set is ordered_set2 - - -def test_orderedset_pickle_roundtrip(): - o1 = ordered_set.OrderedSet([1, 2, 5]) - o2 = pickle.loads(pickle.dumps(o1)) - assert o1 == o2 + packaging2 = importlib.import_module(packaging.__name__) + assert packaging is packaging2 def test_distribution_picklable(): From dbfcf800b2d130066319ee6dc54f485ff3a09dc6 Mon Sep 17 00:00:00 2001 From: Anderson Bravalheri Date: Fri, 16 Aug 2024 15:44:28 +0100 Subject: [PATCH 09/16] Add newsfragment --- newsfragments/4574.removal.rst | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 newsfragments/4574.removal.rst diff --git a/newsfragments/4574.removal.rst b/newsfragments/4574.removal.rst new file mode 100644 index 00000000000..17c8f61ec18 --- /dev/null +++ b/newsfragments/4574.removal.rst @@ -0,0 +1,4 @@ +``setuptools`` is replacing the usages of :pypi:`ordered_set` with simple +instances of ``dict[Hashable, None]``. This is done to remove the extra +dependency and it is possible because since Python 3.7, ``dict`` maintain +insertion order. From 9a9946f98a5c1597a230db5975bdd61dc1ca1a3f Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 19 Aug 2024 16:10:01 -0400 Subject: [PATCH 10/16] Add test capturing missed expectation. Ref pypa/distutils#284 --- distutils/tests/test_modified.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/distutils/tests/test_modified.py b/distutils/tests/test_modified.py index 2bd82346cf0..4f1a7caf9ad 100644 --- a/distutils/tests/test_modified.py +++ b/distutils/tests/test_modified.py @@ -117,3 +117,11 @@ def test_newer_pairwise_group(groups_target): newer = newer_pairwise_group([groups_target.newer], [groups_target.target]) assert older == ([], []) assert newer == ([groups_target.newer], [groups_target.target]) + + +@pytest.mark.xfail(reason="pypa/distutils#284") +def test_newer_group_no_sources_no_target(tmp_path): + """ + Consider no sources and no target "newer". + """ + assert newer_group([], str(tmp_path / 'does-not-exist')) From 30b7331b07fbc404959cb37ac311afdfb90813be Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 19 Aug 2024 16:11:46 -0400 Subject: [PATCH 11/16] Ensure a missing target is still indicated as 'sources are newer' even when there are no sources. Closes pypa/distutils#284 --- distutils/_modified.py | 2 +- distutils/tests/test_modified.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/distutils/_modified.py b/distutils/_modified.py index 6532aa10731..b7bdaa2943e 100644 --- a/distutils/_modified.py +++ b/distutils/_modified.py @@ -63,7 +63,7 @@ def missing_as_newer(source): return missing == 'newer' and not os.path.exists(source) ignored = os.path.exists if missing == 'ignore' else None - return any( + return not os.path.exists(target) or any( missing_as_newer(source) or _newer(source, target) for source in filter(ignored, sources) ) diff --git a/distutils/tests/test_modified.py b/distutils/tests/test_modified.py index 4f1a7caf9ad..e35cec2d6f1 100644 --- a/distutils/tests/test_modified.py +++ b/distutils/tests/test_modified.py @@ -119,7 +119,6 @@ def test_newer_pairwise_group(groups_target): assert newer == ([groups_target.newer], [groups_target.target]) -@pytest.mark.xfail(reason="pypa/distutils#284") def test_newer_group_no_sources_no_target(tmp_path): """ Consider no sources and no target "newer". From 4147b093d0aea4f57757c699a0b25bbc3aab2580 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Mon, 19 Aug 2024 16:31:35 -0400 Subject: [PATCH 12/16] =?UTF-8?q?Bump=20version:=2072.2.0=20=E2=86=92=2073?= =?UTF-8?q?.0.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- NEWS.rst | 34 ++++++++++++++++++++++++++++++ newsfragments/+61911d95.bugfix.rst | 1 - newsfragments/4383.bugfix.rst | 1 - newsfragments/4420.bugfix.rst | 2 -- newsfragments/4503.feature.rst | 1 - newsfragments/4505.feature.rst | 1 - newsfragments/4534.misc.rst | 1 - newsfragments/4546.misc.rst | 2 -- newsfragments/4554.misc.rst | 1 - newsfragments/4559.misc.rst | 2 -- newsfragments/4565.misc.rst | 3 --- newsfragments/4574.removal.rst | 4 ---- pyproject.toml | 2 +- 14 files changed, 36 insertions(+), 21 deletions(-) delete mode 100644 newsfragments/+61911d95.bugfix.rst delete mode 100644 newsfragments/4383.bugfix.rst delete mode 100644 newsfragments/4420.bugfix.rst delete mode 100644 newsfragments/4503.feature.rst delete mode 100644 newsfragments/4505.feature.rst delete mode 100644 newsfragments/4534.misc.rst delete mode 100644 newsfragments/4546.misc.rst delete mode 100644 newsfragments/4554.misc.rst delete mode 100644 newsfragments/4559.misc.rst delete mode 100644 newsfragments/4565.misc.rst delete mode 100644 newsfragments/4574.removal.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 7215487880a..8cd611b4a2f 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 72.2.0 +current_version = 73.0.0 commit = True tag = True diff --git a/NEWS.rst b/NEWS.rst index a2d5eeba366..cfb8a2379f9 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,3 +1,37 @@ +v73.0.0 +======= + +Features +-------- + +- Mark abstract base classes and methods with `abc.ABC` and `abc.abstractmethod` -- by :user:`Avasam` (#4503) +- Changed the order of type checks in ``setuptools.command.easy_install.CommandSpec.from_param`` to support any `collections.abc.Iterable` of `str` param -- by :user:`Avasam` (#4505) + + +Bugfixes +-------- + +- Prevent an error in ``bdist_wheel`` if ``compression`` is set to a `str` (even if valid) after finalizing options but before running the command. -- by :user:`Avasam` (#4383) +- Raises an exception when ``py_limited_api`` is used in a build with + ``Py_GIL_DISABLED``. This is currently not supported (python/cpython#111506). (#4420) +- Synced with pypa/distutils@30b7331 including fix for modified check on empty sources (pypa/distutils#284). + + +Deprecations and Removals +------------------------- + +- ``setuptools`` is replacing the usages of :pypi:`ordered_set` with simple + instances of ``dict[Hashable, None]``. This is done to remove the extra + dependency and it is possible because since Python 3.7, ``dict`` maintain + insertion order. (#4574) + + +Misc +---- + +- #4534, #4546, #4554, #4559, #4565 + + v72.2.0 ======= diff --git a/newsfragments/+61911d95.bugfix.rst b/newsfragments/+61911d95.bugfix.rst deleted file mode 100644 index f542998cd5a..00000000000 --- a/newsfragments/+61911d95.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Synced with pypa/distutils@30b7331 including fix for modified check on empty sources (pypa/distutils#284). \ No newline at end of file diff --git a/newsfragments/4383.bugfix.rst b/newsfragments/4383.bugfix.rst deleted file mode 100644 index e5fd603abbb..00000000000 --- a/newsfragments/4383.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Prevent an error in ``bdist_wheel`` if ``compression`` is set to a `str` (even if valid) after finalizing options but before running the command. -- by :user:`Avasam` diff --git a/newsfragments/4420.bugfix.rst b/newsfragments/4420.bugfix.rst deleted file mode 100644 index c5f75fcddbd..00000000000 --- a/newsfragments/4420.bugfix.rst +++ /dev/null @@ -1,2 +0,0 @@ -Raises an exception when ``py_limited_api`` is used in a build with -``Py_GIL_DISABLED``. This is currently not supported (python/cpython#111506). diff --git a/newsfragments/4503.feature.rst b/newsfragments/4503.feature.rst deleted file mode 100644 index 9c2e4332429..00000000000 --- a/newsfragments/4503.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Mark abstract base classes and methods with `abc.ABC` and `abc.abstractmethod` -- by :user:`Avasam` diff --git a/newsfragments/4505.feature.rst b/newsfragments/4505.feature.rst deleted file mode 100644 index e032dd997ef..00000000000 --- a/newsfragments/4505.feature.rst +++ /dev/null @@ -1 +0,0 @@ -Changed the order of type checks in ``setuptools.command.easy_install.CommandSpec.from_param`` to support any `collections.abc.Iterable` of `str` param -- by :user:`Avasam` diff --git a/newsfragments/4534.misc.rst b/newsfragments/4534.misc.rst deleted file mode 100644 index f7c1a1d3143..00000000000 --- a/newsfragments/4534.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Changed the import of ``ctypes.wintypes`` from ``__import__`` to a regular ``import`` statement -- by :user:`Avasam` diff --git a/newsfragments/4546.misc.rst b/newsfragments/4546.misc.rst deleted file mode 100644 index f056a2b379e..00000000000 --- a/newsfragments/4546.misc.rst +++ /dev/null @@ -1,2 +0,0 @@ -Added lower bound to test dependency on ``wheel`` (0.44.0) to avoid -small inconsistencies in ``Requires-Dist`` normalisation for ``METADATA``. diff --git a/newsfragments/4554.misc.rst b/newsfragments/4554.misc.rst deleted file mode 100644 index 9992f93441f..00000000000 --- a/newsfragments/4554.misc.rst +++ /dev/null @@ -1 +0,0 @@ -Removed ``setputools.sandbox``'s Python 2 ``builtins.file`` support -- by :user:`Avasam` diff --git a/newsfragments/4559.misc.rst b/newsfragments/4559.misc.rst deleted file mode 100644 index 34b5a30664b..00000000000 --- a/newsfragments/4559.misc.rst +++ /dev/null @@ -1,2 +0,0 @@ -Prevent deprecation warning from ``pypa/wheel#631`` to accidentally -trigger when validating ``pyproject.toml``. diff --git a/newsfragments/4565.misc.rst b/newsfragments/4565.misc.rst deleted file mode 100644 index 031f8d66caf..00000000000 --- a/newsfragments/4565.misc.rst +++ /dev/null @@ -1,3 +0,0 @@ -Replace ``pip install -I`` with ``pip install --force-reinstall`` in -integration tests. Additionally, remove ``wheel`` from virtual environment as -it is no longer a build dependency. diff --git a/newsfragments/4574.removal.rst b/newsfragments/4574.removal.rst deleted file mode 100644 index 17c8f61ec18..00000000000 --- a/newsfragments/4574.removal.rst +++ /dev/null @@ -1,4 +0,0 @@ -``setuptools`` is replacing the usages of :pypi:`ordered_set` with simple -instances of ``dict[Hashable, None]``. This is done to remove the extra -dependency and it is possible because since Python 3.7, ``dict`` maintain -insertion order. diff --git a/pyproject.toml b/pyproject.toml index 89c2fe890b7..c9f8e61bbe0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ backend-path = ["."] [project] name = "setuptools" -version = "72.2.0" +version = "73.0.0" authors = [ { name = "Python Packaging Authority", email = "distutils-sig@python.org" }, ] From 429ac589e5f290282f91b420350b002a2c519699 Mon Sep 17 00:00:00 2001 From: Avasam Date: Mon, 19 Aug 2024 18:59:56 -0400 Subject: [PATCH 13/16] Override distribution attribute type in all distutils-based commands --- setuptools/command/bdist_rpm.py | 3 +++ setuptools/command/build.py | 4 ++++ setuptools/command/build_clib.py | 4 ++++ setuptools/command/egg_info.py | 2 -- setuptools/command/register.py | 4 ++++ 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/setuptools/command/bdist_rpm.py b/setuptools/command/bdist_rpm.py index abf2b88bfc0..e0d4caf2e97 100644 --- a/setuptools/command/bdist_rpm.py +++ b/setuptools/command/bdist_rpm.py @@ -1,3 +1,4 @@ +from ..dist import Distribution from ..warnings import SetuptoolsDeprecationWarning import distutils.command.bdist_rpm as orig @@ -12,6 +13,8 @@ class bdist_rpm(orig.bdist_rpm): disable eggs in RPM distributions. """ + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution + def run(self): SetuptoolsDeprecationWarning.emit( "Deprecated command", diff --git a/setuptools/command/build.py b/setuptools/command/build.py index fd53fae8ca5..0c5e544804f 100644 --- a/setuptools/command/build.py +++ b/setuptools/command/build.py @@ -2,12 +2,16 @@ from typing import Protocol +from ..dist import Distribution + from distutils.command.build import build as _build _ORIGINAL_SUBCOMMANDS = {"build_py", "build_clib", "build_ext", "build_scripts"} class build(_build): + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution + # copy to avoid sharing the object with parent class sub_commands = _build.sub_commands[:] diff --git a/setuptools/command/build_clib.py b/setuptools/command/build_clib.py index 5366b0c5c61..9db57ac8a20 100644 --- a/setuptools/command/build_clib.py +++ b/setuptools/command/build_clib.py @@ -1,3 +1,5 @@ +from ..dist import Distribution + import distutils.command.build_clib as orig from distutils import log from distutils.errors import DistutilsSetupError @@ -25,6 +27,8 @@ class build_clib(orig.build_clib): the compiler. """ + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution + def build_libraries(self, libraries): for lib_name, build_info in libraries: sources = build_info.get('sources') diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 794ecd3dc3e..09255a32400 100644 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -18,7 +18,6 @@ from setuptools.command import bdist_egg from setuptools.command.sdist import sdist, walk_revctrl from setuptools.command.setopt import edit_config -from setuptools.dist import Distribution from setuptools.glob import glob from .. import _entry_points, _normalization @@ -522,7 +521,6 @@ def _safe_path(self, path): class manifest_maker(sdist): - distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution template = "MANIFEST.in" def initialize_options(self): diff --git a/setuptools/command/register.py b/setuptools/command/register.py index 575790e5f27..93ef04aa0e2 100644 --- a/setuptools/command/register.py +++ b/setuptools/command/register.py @@ -1,5 +1,7 @@ from setuptools.errors import RemovedCommandError +from ..dist import Distribution + import distutils.command.register as orig from distutils import log @@ -7,6 +9,8 @@ class register(orig.register): """Formerly used to register packages on PyPI.""" + distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution + def run(self): msg = ( "The register command has been removed, use twine to upload " From b7ee00da2cfa8208c47812fb657392e8b88f620c Mon Sep 17 00:00:00 2001 From: Avasam Date: Tue, 20 Aug 2024 13:46:39 -0400 Subject: [PATCH 14/16] Remove ABCMeta metaclass, keep abstractmethods --- newsfragments/4579.bugfix.rst | 1 + pkg_resources/__init__.py | 3 +-- setuptools/__init__.py | 4 ++-- setuptools/command/setopt.py | 3 +-- setuptools/sandbox.py | 3 +-- 5 files changed, 6 insertions(+), 8 deletions(-) create mode 100644 newsfragments/4579.bugfix.rst diff --git a/newsfragments/4579.bugfix.rst b/newsfragments/4579.bugfix.rst new file mode 100644 index 00000000000..bd5ad8c203f --- /dev/null +++ b/newsfragments/4579.bugfix.rst @@ -0,0 +1 @@ +Remove `abc.ABCMeta` metaclass from abstract classes. `pypa/setuptools#4503 `_ had an unintended consequence of causing potential ``TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases`` -- by :user:`Avasam` diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index 8bbf2493710..76aa5e77bac 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -23,7 +23,6 @@ from __future__ import annotations import sys -from abc import ABC if sys.version_info < (3, 8): # noqa: UP036 # Check for unsupported versions raise RuntimeError("Python 3.8 or later is required") @@ -306,7 +305,7 @@ def get_supported_platform(): ] -class ResolutionError(Exception, ABC): +class ResolutionError(Exception): """Abstract base for dependency resolution errors""" def __repr__(self): diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 73de2a03d36..1d3156ff107 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -6,7 +6,7 @@ import os import re import sys -from abc import ABC, abstractmethod +from abc import abstractmethod from typing import TYPE_CHECKING, TypeVar, overload sys.path.extend(((vendor_path := os.path.join(os.path.dirname(os.path.dirname(__file__)), 'setuptools', '_vendor')) not in sys.path) * [vendor_path]) # fmt: skip @@ -120,7 +120,7 @@ def setup(**attrs): _Command = monkey.get_unpatched(distutils.core.Command) -class Command(_Command, ABC): +class Command(_Command): """ Setuptools internal actions are organized using a *command design pattern*. This means that each action (or group of closely related actions) executed during diff --git a/setuptools/command/setopt.py b/setuptools/command/setopt.py index b2653bd4661..e351af22f0b 100644 --- a/setuptools/command/setopt.py +++ b/setuptools/command/setopt.py @@ -1,6 +1,5 @@ import configparser import os -from abc import ABC from .. import Command from ..unicode_utils import _cfg_read_utf8_with_fallback @@ -70,7 +69,7 @@ def edit_config(filename, settings, dry_run=False): opts.write(f) -class option_base(Command, ABC): +class option_base(Command): """Abstract base class for commands that mess with config files""" user_options = [ diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 9a101b71377..7d545f1004c 100644 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -11,7 +11,6 @@ import sys import tempfile import textwrap -from abc import ABC import pkg_resources from pkg_resources import working_set @@ -263,7 +262,7 @@ def run_setup(setup_script, args): # Normal exit, just return -class AbstractSandbox(ABC): +class AbstractSandbox: """Wrap 'os' module and 'open()' builtin for virtualizing setup scripts""" _active = False From ebddeb36f72c9d758b5cc0e9f81f8a66aa837d96 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Tue, 20 Aug 2024 14:08:21 -0400 Subject: [PATCH 15/16] =?UTF-8?q?Bump=20version:=2073.0.0=20=E2=86=92=2073?= =?UTF-8?q?.0.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .bumpversion.cfg | 2 +- NEWS.rst | 9 +++++++++ newsfragments/4579.bugfix.rst | 1 - pyproject.toml | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) delete mode 100644 newsfragments/4579.bugfix.rst diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 8cd611b4a2f..51d22156e43 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 73.0.0 +current_version = 73.0.1 commit = True tag = True diff --git a/NEWS.rst b/NEWS.rst index cfb8a2379f9..3c36fbfa5aa 100644 --- a/NEWS.rst +++ b/NEWS.rst @@ -1,3 +1,12 @@ +v73.0.1 +======= + +Bugfixes +-------- + +- Remove `abc.ABCMeta` metaclass from abstract classes. `pypa/setuptools#4503 `_ had an unintended consequence of causing potential ``TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases`` -- by :user:`Avasam` (#4579) + + v73.0.0 ======= diff --git a/newsfragments/4579.bugfix.rst b/newsfragments/4579.bugfix.rst deleted file mode 100644 index bd5ad8c203f..00000000000 --- a/newsfragments/4579.bugfix.rst +++ /dev/null @@ -1 +0,0 @@ -Remove `abc.ABCMeta` metaclass from abstract classes. `pypa/setuptools#4503 `_ had an unintended consequence of causing potential ``TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases`` -- by :user:`Avasam` diff --git a/pyproject.toml b/pyproject.toml index c9f8e61bbe0..390da0c634e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ backend-path = ["."] [project] name = "setuptools" -version = "73.0.0" +version = "73.0.1" authors = [ { name = "Python Packaging Authority", email = "distutils-sig@python.org" }, ] From f1350e413775a9e79e20779cc9705e28a1c55900 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Wed, 21 Aug 2024 07:05:32 -0400 Subject: [PATCH 16/16] Add upstream and local sections for 'type' extra, since many projects will have 'types-*' dependencies. --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 31057d85f10..3866a3237e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -57,7 +57,10 @@ enabler = [ ] type = [ + # upstream "pytest-mypy", + + # local ]