aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2025-01-19 00:01:09 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2025-01-19 00:11:11 +0300
commit4a1e3ae4a426d939fc4a694485f5ba122dc800a7 (patch)
tree99c417604709b0a7594d5a1b09e9f94b05732b81
parentfd9ad84b3d25d96de9905dbe53916e479b086204 (diff)
downloadydb-4a1e3ae4a426d939fc4a694485f5ba122dc800a7.tar.gz
Intermediate changes
commit_hash:6bde9d54b080b7f086eb02bec4faa80a0e116dda
-rw-r--r--contrib/python/jaraco.collections/.dist-info/METADATA85
-rw-r--r--contrib/python/jaraco.collections/.dist-info/top_level.txt1
-rw-r--r--contrib/python/jaraco.collections/LICENSE17
-rw-r--r--contrib/python/jaraco.collections/README.rst51
-rw-r--r--contrib/python/jaraco.collections/jaraco/collections/__init__.py1091
-rw-r--r--contrib/python/jaraco.collections/jaraco/collections/py.typed0
-rw-r--r--contrib/python/jaraco.collections/ya.make27
-rw-r--r--contrib/python/setuptools/py3/.dist-info/METADATA115
-rw-r--r--contrib/python/setuptools/py3/.dist-info/entry_points.txt1
-rw-r--r--contrib/python/setuptools/py3/.yandex_meta/yamaker.yaml1
-rw-r--r--contrib/python/setuptools/py3/_distutils_hack/__init__.py5
-rw-r--r--contrib/python/setuptools/py3/pkg_resources/__init__.py59
-rw-r--r--contrib/python/setuptools/py3/setuptools/__init__.py18
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/_collections.py58
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/_functools.py73
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/_itertools.py52
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/_modified.py3
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/_msvccompiler.py2
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py26
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/bcppcompiler.py396
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/ccompiler.py3
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/__init__.py2
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/install.py5
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/register.py322
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py12
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/command/upload.py208
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/compat/py38.py2
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/config.py151
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/core.py3
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/cygwinccompiler.py9
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/dir_util.py226
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/errors.py13
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/sysconfig.py3
-rw-r--r--contrib/python/setuptools/py3/setuptools/_distutils/util.py3
-rw-r--r--contrib/python/setuptools/py3/setuptools/_importlib.py5
-rw-r--r--contrib/python/setuptools/py3/setuptools/_path.py13
-rw-r--r--contrib/python/setuptools/py3/setuptools/_reqs.py3
-rw-r--r--contrib/python/setuptools/py3/setuptools/_shutil.py53
-rw-r--r--contrib/python/setuptools/py3/setuptools/archive_util.py10
-rw-r--r--contrib/python/setuptools/py3/setuptools/build_meta.py82
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/__init__.py10
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/_requirestxt.py8
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/alias.py7
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/bdist_egg.py40
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/bdist_rpm.py2
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py167
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/build.py6
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/build_clib.py11
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/build_ext.py29
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/build_py.py56
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/develop.py8
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/dist_info.py10
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/easy_install.py137
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/editable_wheel.py44
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/egg_info.py70
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/install.py44
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/install_egg_info.py8
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/install_lib.py17
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/install_scripts.py4
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/register.py22
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/rotate.py12
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/saveopts.py4
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/sdist.py21
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/setopt.py6
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/upload.py20
-rw-r--r--contrib/python/setuptools/py3/setuptools/command/upload_docs.py221
-rw-r--r--contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py33
-rw-r--r--contrib/python/setuptools/py3/setuptools/config/expand.py38
-rw-r--r--contrib/python/setuptools/py3/setuptools/config/pyprojecttoml.py22
-rw-r--r--contrib/python/setuptools/py3/setuptools/config/setupcfg.py86
-rw-r--r--contrib/python/setuptools/py3/setuptools/depends.py33
-rw-r--r--contrib/python/setuptools/py3/setuptools/discovery.py35
-rw-r--r--contrib/python/setuptools/py3/setuptools/dist.py153
-rw-r--r--contrib/python/setuptools/py3/setuptools/errors.py47
-rw-r--r--contrib/python/setuptools/py3/setuptools/extension.py24
-rw-r--r--contrib/python/setuptools/py3/setuptools/glob.py50
-rw-r--r--contrib/python/setuptools/py3/setuptools/installer.py7
-rw-r--r--contrib/python/setuptools/py3/setuptools/launch.py2
-rw-r--r--contrib/python/setuptools/py3/setuptools/logging.py4
-rw-r--r--contrib/python/setuptools/py3/setuptools/modified.py22
-rw-r--r--contrib/python/setuptools/py3/setuptools/monkey.py10
-rw-r--r--contrib/python/setuptools/py3/setuptools/msvc.py59
-rw-r--r--contrib/python/setuptools/py3/setuptools/namespaces.py4
-rw-r--r--contrib/python/setuptools/py3/setuptools/package_index.py85
-rw-r--r--contrib/python/setuptools/py3/setuptools/sandbox.py31
-rw-r--r--contrib/python/setuptools/py3/setuptools/unicode_utils.py6
-rw-r--r--contrib/python/setuptools/py3/setuptools/warnings.py7
-rw-r--r--contrib/python/setuptools/py3/setuptools/wheel.py8
-rw-r--r--contrib/python/setuptools/py3/ya.make14
-rw-r--r--yt/yt/library/program/program-inl.h18
-rw-r--r--yt/yt/library/program/program.cpp9
-rw-r--r--yt/yt/library/program/program.h9
92 files changed, 2478 insertions, 2531 deletions
diff --git a/contrib/python/jaraco.collections/.dist-info/METADATA b/contrib/python/jaraco.collections/.dist-info/METADATA
new file mode 100644
index 0000000000..fe6ca5ad88
--- /dev/null
+++ b/contrib/python/jaraco.collections/.dist-info/METADATA
@@ -0,0 +1,85 @@
+Metadata-Version: 2.1
+Name: jaraco.collections
+Version: 5.1.0
+Summary: Collection objects similar to those in stdlib by jaraco
+Author-email: "Jason R. Coombs" <jaraco@jaraco.com>
+Project-URL: Source, https://github.com/jaraco/jaraco.collections
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Requires-Python: >=3.8
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+Requires-Dist: jaraco.text
+Provides-Extra: check
+Requires-Dist: pytest-checkdocs >=2.4 ; extra == 'check'
+Requires-Dist: pytest-ruff >=0.2.1 ; (sys_platform != "cygwin") and extra == 'check'
+Provides-Extra: cover
+Requires-Dist: pytest-cov ; extra == 'cover'
+Provides-Extra: doc
+Requires-Dist: sphinx >=3.5 ; extra == 'doc'
+Requires-Dist: jaraco.packaging >=9.3 ; extra == 'doc'
+Requires-Dist: rst.linker >=1.9 ; extra == 'doc'
+Requires-Dist: furo ; extra == 'doc'
+Requires-Dist: sphinx-lint ; extra == 'doc'
+Requires-Dist: jaraco.tidelift >=1.4 ; extra == 'doc'
+Provides-Extra: enabler
+Requires-Dist: pytest-enabler >=2.2 ; extra == 'enabler'
+Provides-Extra: test
+Requires-Dist: pytest !=8.1.*,>=6 ; extra == 'test'
+Provides-Extra: type
+Requires-Dist: pytest-mypy ; extra == 'type'
+
+.. image:: https://img.shields.io/pypi/v/jaraco.collections.svg
+ :target: https://pypi.org/project/jaraco.collections
+
+.. image:: https://img.shields.io/pypi/pyversions/jaraco.collections.svg
+
+.. image:: https://github.com/jaraco/jaraco.collections/actions/workflows/main.yml/badge.svg
+ :target: https://github.com/jaraco/jaraco.collections/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
+ :target: https://github.com/astral-sh/ruff
+ :alt: Ruff
+
+.. image:: https://readthedocs.org/projects/jaracocollections/badge/?version=latest
+ :target: https://jaracocollections.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2024-informational
+ :target: https://blog.jaraco.com/skeleton
+
+.. image:: https://tidelift.com/badges/package/pypi/jaraco.collections
+ :target: https://tidelift.com/subscription/pkg/pypi-jaraco.collections?utm_source=pypi-jaraco.collections&utm_medium=readme
+
+Models and classes to supplement the stdlib 'collections' module.
+
+See the docs, linked above, for descriptions and usage examples.
+
+Highlights include:
+
+- RangeMap: A mapping that accepts a range of values for keys.
+- Projection: A subset over an existing mapping.
+- KeyTransformingDict: Generalized mapping with keys transformed by a function.
+- FoldedCaseKeyedDict: A dict whose string keys are case-insensitive.
+- BijectiveMap: A map where keys map to values and values back to their keys.
+- ItemsAsAttributes: A mapping mix-in exposing items as attributes.
+- IdentityOverrideMap: A map whose keys map by default to themselves unless overridden.
+- FrozenDict: A hashable, immutable map.
+- Enumeration: An object whose keys are enumerated.
+- Everything: A container that contains all things.
+- Least, Greatest: Objects that are always less than or greater than any other.
+- pop_all: Return all items from the mutable sequence and remove them from that sequence.
+- DictStack: A stack of dicts, great for sharing scopes.
+- WeightedLookup: A specialized RangeMap for selecting an item by weights.
+
+For Enterprise
+==============
+
+Available as part of the Tidelift Subscription.
+
+This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.
+
+`Learn more <https://tidelift.com/subscription/pkg/pypi-jaraco.collections?utm_source=pypi-jaraco.collections&utm_medium=referral&utm_campaign=github>`_.
diff --git a/contrib/python/jaraco.collections/.dist-info/top_level.txt b/contrib/python/jaraco.collections/.dist-info/top_level.txt
new file mode 100644
index 0000000000..f6205a5f19
--- /dev/null
+++ b/contrib/python/jaraco.collections/.dist-info/top_level.txt
@@ -0,0 +1 @@
+jaraco
diff --git a/contrib/python/jaraco.collections/LICENSE b/contrib/python/jaraco.collections/LICENSE
new file mode 100644
index 0000000000..1bb5a44356
--- /dev/null
+++ b/contrib/python/jaraco.collections/LICENSE
@@ -0,0 +1,17 @@
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+IN THE SOFTWARE.
diff --git a/contrib/python/jaraco.collections/README.rst b/contrib/python/jaraco.collections/README.rst
new file mode 100644
index 0000000000..5043d6a603
--- /dev/null
+++ b/contrib/python/jaraco.collections/README.rst
@@ -0,0 +1,51 @@
+.. image:: https://img.shields.io/pypi/v/jaraco.collections.svg
+ :target: https://pypi.org/project/jaraco.collections
+
+.. image:: https://img.shields.io/pypi/pyversions/jaraco.collections.svg
+
+.. image:: https://github.com/jaraco/jaraco.collections/actions/workflows/main.yml/badge.svg
+ :target: https://github.com/jaraco/jaraco.collections/actions?query=workflow%3A%22tests%22
+ :alt: tests
+
+.. image:: https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/charliermarsh/ruff/main/assets/badge/v2.json
+ :target: https://github.com/astral-sh/ruff
+ :alt: Ruff
+
+.. image:: https://readthedocs.org/projects/jaracocollections/badge/?version=latest
+ :target: https://jaracocollections.readthedocs.io/en/latest/?badge=latest
+
+.. image:: https://img.shields.io/badge/skeleton-2024-informational
+ :target: https://blog.jaraco.com/skeleton
+
+.. image:: https://tidelift.com/badges/package/pypi/jaraco.collections
+ :target: https://tidelift.com/subscription/pkg/pypi-jaraco.collections?utm_source=pypi-jaraco.collections&utm_medium=readme
+
+Models and classes to supplement the stdlib 'collections' module.
+
+See the docs, linked above, for descriptions and usage examples.
+
+Highlights include:
+
+- RangeMap: A mapping that accepts a range of values for keys.
+- Projection: A subset over an existing mapping.
+- KeyTransformingDict: Generalized mapping with keys transformed by a function.
+- FoldedCaseKeyedDict: A dict whose string keys are case-insensitive.
+- BijectiveMap: A map where keys map to values and values back to their keys.
+- ItemsAsAttributes: A mapping mix-in exposing items as attributes.
+- IdentityOverrideMap: A map whose keys map by default to themselves unless overridden.
+- FrozenDict: A hashable, immutable map.
+- Enumeration: An object whose keys are enumerated.
+- Everything: A container that contains all things.
+- Least, Greatest: Objects that are always less than or greater than any other.
+- pop_all: Return all items from the mutable sequence and remove them from that sequence.
+- DictStack: A stack of dicts, great for sharing scopes.
+- WeightedLookup: A specialized RangeMap for selecting an item by weights.
+
+For Enterprise
+==============
+
+Available as part of the Tidelift Subscription.
+
+This project and the maintainers of thousands of other packages are working with Tidelift to deliver one enterprise subscription that covers all of the open source you use.
+
+`Learn more <https://tidelift.com/subscription/pkg/pypi-jaraco.collections?utm_source=pypi-jaraco.collections&utm_medium=referral&utm_campaign=github>`_.
diff --git a/contrib/python/jaraco.collections/jaraco/collections/__init__.py b/contrib/python/jaraco.collections/jaraco/collections/__init__.py
new file mode 100644
index 0000000000..0d501cf9e9
--- /dev/null
+++ b/contrib/python/jaraco.collections/jaraco/collections/__init__.py
@@ -0,0 +1,1091 @@
+from __future__ import annotations
+
+import collections.abc
+import copy
+import functools
+import itertools
+import operator
+import random
+import re
+from collections.abc import Container, Iterable, Mapping
+from typing import TYPE_CHECKING, Any, Callable, Dict, TypeVar, Union, overload
+
+import jaraco.text
+
+if TYPE_CHECKING:
+ from _operator import _SupportsComparison
+
+ from _typeshed import SupportsKeysAndGetItem
+ from typing_extensions import Self
+
+ _RangeMapKT = TypeVar('_RangeMapKT', bound=_SupportsComparison)
+else:
+ # _SupportsComparison doesn't exist at runtime,
+ # but _RangeMapKT is used in RangeMap's superclass' type parameters
+ _RangeMapKT = TypeVar('_RangeMapKT')
+
+_T = TypeVar('_T')
+_VT = TypeVar('_VT')
+
+_Matchable = Union[Callable, Container, Iterable, re.Pattern]
+
+
+def _dispatch(obj: _Matchable) -> Callable:
+ # can't rely on singledispatch for Union[Container, Iterable]
+ # due to ambiguity
+ # (https://peps.python.org/pep-0443/#abstract-base-classes).
+ if isinstance(obj, re.Pattern):
+ return obj.fullmatch
+ # mypy issue: https://github.com/python/mypy/issues/11071
+ if not isinstance(obj, Callable): # type: ignore[arg-type]
+ if not isinstance(obj, Container):
+ obj = set(obj) # type: ignore[arg-type]
+ obj = obj.__contains__
+ return obj # type: ignore[return-value]
+
+
+class Projection(collections.abc.Mapping):
+ """
+ Project a set of keys over a mapping
+
+ >>> sample = {'a': 1, 'b': 2, 'c': 3}
+ >>> prj = Projection(['a', 'c', 'd'], sample)
+ >>> dict(prj)
+ {'a': 1, 'c': 3}
+
+ Projection also accepts an iterable or callable or pattern.
+
+ >>> iter_prj = Projection(iter('acd'), sample)
+ >>> call_prj = Projection(lambda k: ord(k) in (97, 99, 100), sample)
+ >>> pat_prj = Projection(re.compile(r'[acd]'), sample)
+ >>> prj == iter_prj == call_prj == pat_prj
+ True
+
+ Keys should only appear if they were specified and exist in the space.
+ Order is retained.
+
+ >>> list(prj)
+ ['a', 'c']
+
+ Attempting to access a key not in the projection
+ results in a KeyError.
+
+ >>> prj['b']
+ Traceback (most recent call last):
+ ...
+ KeyError: 'b'
+
+ Use the projection to update another dict.
+
+ >>> target = {'a': 2, 'b': 2}
+ >>> target.update(prj)
+ >>> target
+ {'a': 1, 'b': 2, 'c': 3}
+
+ Projection keeps a reference to the original dict, so
+ modifying the original dict may modify the Projection.
+
+ >>> del sample['a']
+ >>> dict(prj)
+ {'c': 3}
+ """
+
+ def __init__(self, keys: _Matchable, space: Mapping):
+ self._match = _dispatch(keys)
+ self._space = space
+
+ def __getitem__(self, key):
+ if not self._match(key):
+ raise KeyError(key)
+ return self._space[key]
+
+ def _keys_resolved(self):
+ return filter(self._match, self._space)
+
+ def __iter__(self):
+ return self._keys_resolved()
+
+ def __len__(self):
+ return len(tuple(self._keys_resolved()))
+
+
+class Mask(Projection):
+ """
+ The inverse of a :class:`Projection`, masking out keys.
+
+ >>> sample = {'a': 1, 'b': 2, 'c': 3}
+ >>> msk = Mask(['a', 'c', 'd'], sample)
+ >>> dict(msk)
+ {'b': 2}
+ """
+
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ # self._match = compose(operator.not_, self._match)
+ self._match = lambda key, orig=self._match: not orig(key)
+
+
+def dict_map(function, dictionary):
+ """
+ Return a new dict with function applied to values of dictionary.
+
+ >>> dict_map(lambda x: x+1, dict(a=1, b=2))
+ {'a': 2, 'b': 3}
+ """
+ return dict((key, function(value)) for key, value in dictionary.items())
+
+
+class RangeMap(Dict[_RangeMapKT, _VT]):
+ """
+ A dictionary-like object that uses the keys as bounds for a range.
+ Inclusion of the value for that range is determined by the
+ key_match_comparator, which defaults to less-than-or-equal.
+ A value is returned for a key if it is the first key that matches in
+ the sorted list of keys.
+
+ One may supply keyword parameters to be passed to the sort function used
+ to sort keys (i.e. key, reverse) as sort_params.
+
+ Create a map that maps 1-3 -> 'a', 4-6 -> 'b'
+
+ >>> r = RangeMap({3: 'a', 6: 'b'}) # boy, that was easy
+ >>> r[1], r[2], r[3], r[4], r[5], r[6]
+ ('a', 'a', 'a', 'b', 'b', 'b')
+
+ Even float values should work so long as the comparison operator
+ supports it.
+
+ >>> r[4.5]
+ 'b'
+
+ Notice that the way rangemap is defined, it must be open-ended
+ on one side.
+
+ >>> r[0]
+ 'a'
+ >>> r[-1]
+ 'a'
+
+ One can close the open-end of the RangeMap by using undefined_value
+
+ >>> r = RangeMap({0: RangeMap.undefined_value, 3: 'a', 6: 'b'})
+ >>> r[0]
+ Traceback (most recent call last):
+ ...
+ KeyError: 0
+
+ One can get the first or last elements in the range by using RangeMap.Item
+
+ >>> last_item = RangeMap.Item(-1)
+ >>> r[last_item]
+ 'b'
+
+ .last_item is a shortcut for Item(-1)
+
+ >>> r[RangeMap.last_item]
+ 'b'
+
+ Sometimes it's useful to find the bounds for a RangeMap
+
+ >>> r.bounds()
+ (0, 6)
+
+ RangeMap supports .get(key, default)
+
+ >>> r.get(0, 'not found')
+ 'not found'
+
+ >>> r.get(7, 'not found')
+ 'not found'
+
+ One often wishes to define the ranges by their left-most values,
+ which requires use of sort params and a key_match_comparator.
+
+ >>> r = RangeMap({1: 'a', 4: 'b'},
+ ... sort_params=dict(reverse=True),
+ ... key_match_comparator=operator.ge)
+ >>> r[1], r[2], r[3], r[4], r[5], r[6]
+ ('a', 'a', 'a', 'b', 'b', 'b')
+
+ That wasn't nearly as easy as before, so an alternate constructor
+ is provided:
+
+ >>> r = RangeMap.left({1: 'a', 4: 'b', 7: RangeMap.undefined_value})
+ >>> r[1], r[2], r[3], r[4], r[5], r[6]
+ ('a', 'a', 'a', 'b', 'b', 'b')
+
+ """
+
+ def __init__(
+ self,
+ source: (
+ SupportsKeysAndGetItem[_RangeMapKT, _VT] | Iterable[tuple[_RangeMapKT, _VT]]
+ ),
+ sort_params: Mapping[str, Any] = {},
+ key_match_comparator: Callable[[_RangeMapKT, _RangeMapKT], bool] = operator.le,
+ ):
+ dict.__init__(self, source)
+ self.sort_params = sort_params
+ self.match = key_match_comparator
+
+ @classmethod
+ def left(
+ cls,
+ source: (
+ SupportsKeysAndGetItem[_RangeMapKT, _VT] | Iterable[tuple[_RangeMapKT, _VT]]
+ ),
+ ) -> Self:
+ return cls(
+ source, sort_params=dict(reverse=True), key_match_comparator=operator.ge
+ )
+
+ def __getitem__(self, item: _RangeMapKT) -> _VT:
+ sorted_keys = sorted(self.keys(), **self.sort_params)
+ if isinstance(item, RangeMap.Item):
+ result = self.__getitem__(sorted_keys[item])
+ else:
+ key = self._find_first_match_(sorted_keys, item)
+ result = dict.__getitem__(self, key)
+ if result is RangeMap.undefined_value:
+ raise KeyError(key)
+ return result
+
+ @overload # type: ignore[override] # Signature simplified over dict and Mapping
+ def get(self, key: _RangeMapKT, default: _T) -> _VT | _T: ...
+ @overload
+ def get(self, key: _RangeMapKT, default: None = None) -> _VT | None: ...
+ def get(self, key: _RangeMapKT, default: _T | None = None) -> _VT | _T | None:
+ """
+ Return the value for key if key is in the dictionary, else default.
+ If default is not given, it defaults to None, so that this method
+ never raises a KeyError.
+ """
+ try:
+ return self[key]
+ except KeyError:
+ return default
+
+ def _find_first_match_(
+ self, keys: Iterable[_RangeMapKT], item: _RangeMapKT
+ ) -> _RangeMapKT:
+ is_match = functools.partial(self.match, item)
+ matches = filter(is_match, keys)
+ try:
+ return next(matches)
+ except StopIteration:
+ raise KeyError(item) from None
+
+ def bounds(self) -> tuple[_RangeMapKT, _RangeMapKT]:
+ sorted_keys = sorted(self.keys(), **self.sort_params)
+ return (sorted_keys[RangeMap.first_item], sorted_keys[RangeMap.last_item])
+
+ # some special values for the RangeMap
+ undefined_value = type('RangeValueUndefined', (), {})()
+
+ class Item(int):
+ """RangeMap Item"""
+
+ first_item = Item(0)
+ last_item = Item(-1)
+
+
+def __identity(x):
+ return x
+
+
+def sorted_items(d, key=__identity, reverse=False):
+ """
+ Return the items of the dictionary sorted by the keys.
+
+ >>> sample = dict(foo=20, bar=42, baz=10)
+ >>> tuple(sorted_items(sample))
+ (('bar', 42), ('baz', 10), ('foo', 20))
+
+ >>> reverse_string = lambda s: ''.join(reversed(s))
+ >>> tuple(sorted_items(sample, key=reverse_string))
+ (('foo', 20), ('bar', 42), ('baz', 10))
+
+ >>> tuple(sorted_items(sample, reverse=True))
+ (('foo', 20), ('baz', 10), ('bar', 42))
+ """
+
+ # wrap the key func so it operates on the first element of each item
+ def pairkey_key(item):
+ return key(item[0])
+
+ return sorted(d.items(), key=pairkey_key, reverse=reverse)
+
+
+class KeyTransformingDict(dict):
+ """
+ A dict subclass that transforms the keys before they're used.
+ Subclasses may override the default transform_key to customize behavior.
+ """
+
+ @staticmethod
+ def transform_key(key): # pragma: nocover
+ return key
+
+ def __init__(self, *args, **kargs):
+ super().__init__()
+ # build a dictionary using the default constructs
+ d = dict(*args, **kargs)
+ # build this dictionary using transformed keys.
+ for item in d.items():
+ self.__setitem__(*item)
+
+ def __setitem__(self, key, val):
+ key = self.transform_key(key)
+ super().__setitem__(key, val)
+
+ def __getitem__(self, key):
+ key = self.transform_key(key)
+ return super().__getitem__(key)
+
+ def __contains__(self, key):
+ key = self.transform_key(key)
+ return super().__contains__(key)
+
+ def __delitem__(self, key):
+ key = self.transform_key(key)
+ return super().__delitem__(key)
+
+ def get(self, key, *args, **kwargs):
+ key = self.transform_key(key)
+ return super().get(key, *args, **kwargs)
+
+ def setdefault(self, key, *args, **kwargs):
+ key = self.transform_key(key)
+ return super().setdefault(key, *args, **kwargs)
+
+ def pop(self, key, *args, **kwargs):
+ key = self.transform_key(key)
+ return super().pop(key, *args, **kwargs)
+
+ def matching_key_for(self, key):
+ """
+ Given a key, return the actual key stored in self that matches.
+ Raise KeyError if the key isn't found.
+ """
+ try:
+ return next(e_key for e_key in self.keys() if e_key == key)
+ except StopIteration as err:
+ raise KeyError(key) from err
+
+
+class FoldedCaseKeyedDict(KeyTransformingDict):
+ """
+ A case-insensitive dictionary (keys are compared as insensitive
+ if they are strings).
+
+ >>> d = FoldedCaseKeyedDict()
+ >>> d['heLlo'] = 'world'
+ >>> list(d.keys()) == ['heLlo']
+ True
+ >>> list(d.values()) == ['world']
+ True
+ >>> d['hello'] == 'world'
+ True
+ >>> 'hello' in d
+ True
+ >>> 'HELLO' in d
+ True
+ >>> print(repr(FoldedCaseKeyedDict({'heLlo': 'world'})))
+ {'heLlo': 'world'}
+ >>> d = FoldedCaseKeyedDict({'heLlo': 'world'})
+ >>> print(d['hello'])
+ world
+ >>> print(d['Hello'])
+ world
+ >>> list(d.keys())
+ ['heLlo']
+ >>> d = FoldedCaseKeyedDict({'heLlo': 'world', 'Hello': 'world'})
+ >>> list(d.values())
+ ['world']
+ >>> key, = d.keys()
+ >>> key in ['heLlo', 'Hello']
+ True
+ >>> del d['HELLO']
+ >>> d
+ {}
+
+ get should work
+
+ >>> d['Sumthin'] = 'else'
+ >>> d.get('SUMTHIN')
+ 'else'
+ >>> d.get('OTHER', 'thing')
+ 'thing'
+ >>> del d['sumthin']
+
+ setdefault should also work
+
+ >>> d['This'] = 'that'
+ >>> print(d.setdefault('this', 'other'))
+ that
+ >>> len(d)
+ 1
+ >>> print(d['this'])
+ that
+ >>> print(d.setdefault('That', 'other'))
+ other
+ >>> print(d['THAT'])
+ other
+
+ Make it pop!
+
+ >>> print(d.pop('THAT'))
+ other
+
+ To retrieve the key in its originally-supplied form, use matching_key_for
+
+ >>> print(d.matching_key_for('this'))
+ This
+
+ >>> d.matching_key_for('missing')
+ Traceback (most recent call last):
+ ...
+ KeyError: 'missing'
+ """
+
+ @staticmethod
+ def transform_key(key):
+ return jaraco.text.FoldedCase(key)
+
+
+class DictAdapter:
+ """
+ Provide a getitem interface for attributes of an object.
+
+ Let's say you want to get at the string.lowercase property in a formatted
+ string. It's easy with DictAdapter.
+
+ >>> import string
+ >>> print("lowercase is %(ascii_lowercase)s" % DictAdapter(string))
+ lowercase is abcdefghijklmnopqrstuvwxyz
+ """
+
+ def __init__(self, wrapped_ob):
+ self.object = wrapped_ob
+
+ def __getitem__(self, name):
+ return getattr(self.object, name)
+
+
+class ItemsAsAttributes:
+ """
+ Mix-in class to enable a mapping object to provide items as
+ attributes.
+
+ >>> C = type('C', (dict, ItemsAsAttributes), dict())
+ >>> i = C()
+ >>> i['foo'] = 'bar'
+ >>> i.foo
+ 'bar'
+
+ Natural attribute access takes precedence
+
+ >>> i.foo = 'henry'
+ >>> i.foo
+ 'henry'
+
+ But as you might expect, the mapping functionality is preserved.
+
+ >>> i['foo']
+ 'bar'
+
+ A normal attribute error should be raised if an attribute is
+ requested that doesn't exist.
+
+ >>> i.missing
+ Traceback (most recent call last):
+ ...
+ AttributeError: 'C' object has no attribute 'missing'
+
+ It also works on dicts that customize __getitem__
+
+ >>> missing_func = lambda self, key: 'missing item'
+ >>> C = type(
+ ... 'C',
+ ... (dict, ItemsAsAttributes),
+ ... dict(__missing__ = missing_func),
+ ... )
+ >>> i = C()
+ >>> i.missing
+ 'missing item'
+ >>> i.foo
+ 'missing item'
+ """
+
+ def __getattr__(self, key):
+ try:
+ return getattr(super(), key)
+ except AttributeError as e:
+ # attempt to get the value from the mapping (return self[key])
+ # but be careful not to lose the original exception context.
+ noval = object()
+
+ def _safe_getitem(cont, key, missing_result):
+ try:
+ return cont[key]
+ except KeyError:
+ return missing_result
+
+ result = _safe_getitem(self, key, noval)
+ if result is not noval:
+ return result
+ # raise the original exception, but use the original class
+ # name, not 'super'.
+ (message,) = e.args
+ message = message.replace('super', self.__class__.__name__, 1)
+ e.args = (message,)
+ raise
+
+
+def invert_map(map):
+ """
+ Given a dictionary, return another dictionary with keys and values
+ switched. If any of the values resolve to the same key, raises
+ a ValueError.
+
+ >>> numbers = dict(a=1, b=2, c=3)
+ >>> letters = invert_map(numbers)
+ >>> letters[1]
+ 'a'
+ >>> numbers['d'] = 3
+ >>> invert_map(numbers)
+ Traceback (most recent call last):
+ ...
+ ValueError: Key conflict in inverted mapping
+ """
+ res = dict((v, k) for k, v in map.items())
+ if not len(res) == len(map):
+ raise ValueError('Key conflict in inverted mapping')
+ return res
+
+
+class IdentityOverrideMap(dict):
+ """
+ A dictionary that by default maps each key to itself, but otherwise
+ acts like a normal dictionary.
+
+ >>> d = IdentityOverrideMap()
+ >>> d[42]
+ 42
+ >>> d['speed'] = 'speedo'
+ >>> print(d['speed'])
+ speedo
+ """
+
+ def __missing__(self, key):
+ return key
+
+
+class DictStack(list, collections.abc.MutableMapping):
+ """
+ A stack of dictionaries that behaves as a view on those dictionaries,
+ giving preference to the last.
+
+ >>> stack = DictStack([dict(a=1, c=2), dict(b=2, a=2)])
+ >>> stack['a']
+ 2
+ >>> stack['b']
+ 2
+ >>> stack['c']
+ 2
+ >>> len(stack)
+ 3
+ >>> stack.push(dict(a=3))
+ >>> stack['a']
+ 3
+ >>> stack['a'] = 4
+ >>> set(stack.keys()) == set(['a', 'b', 'c'])
+ True
+ >>> set(stack.items()) == set([('a', 4), ('b', 2), ('c', 2)])
+ True
+ >>> dict(**stack) == dict(stack) == dict(a=4, c=2, b=2)
+ True
+ >>> d = stack.pop()
+ >>> stack['a']
+ 2
+ >>> d = stack.pop()
+ >>> stack['a']
+ 1
+ >>> stack.get('b', None)
+ >>> 'c' in stack
+ True
+ >>> del stack['c']
+ >>> dict(stack)
+ {'a': 1}
+ """
+
+ def __iter__(self):
+ dicts = list.__iter__(self)
+ return iter(set(itertools.chain.from_iterable(c.keys() for c in dicts)))
+
+ def __getitem__(self, key):
+ for scope in reversed(tuple(list.__iter__(self))):
+ if key in scope:
+ return scope[key]
+ raise KeyError(key)
+
+ push = list.append
+
+ def __contains__(self, other):
+ return collections.abc.Mapping.__contains__(self, other)
+
+ def __len__(self):
+ return len(list(iter(self)))
+
+ def __setitem__(self, key, item):
+ last = list.__getitem__(self, -1)
+ return last.__setitem__(key, item)
+
+ def __delitem__(self, key):
+ last = list.__getitem__(self, -1)
+ return last.__delitem__(key)
+
+ # workaround for mypy confusion
+ def pop(self, *args, **kwargs):
+ return list.pop(self, *args, **kwargs)
+
+
+class BijectiveMap(dict):
+ """
+ A Bijective Map (two-way mapping).
+
+ Implemented as a simple dictionary of 2x the size, mapping values back
+ to keys.
+
+ Note, this implementation may be incomplete. If there's not a test for
+ your use case below, it's likely to fail, so please test and send pull
+ requests or patches for additional functionality needed.
+
+
+ >>> m = BijectiveMap()
+ >>> m['a'] = 'b'
+ >>> m == {'a': 'b', 'b': 'a'}
+ True
+ >>> print(m['b'])
+ a
+
+ >>> m['c'] = 'd'
+ >>> len(m)
+ 2
+
+ Some weird things happen if you map an item to itself or overwrite a
+ single key of a pair, so it's disallowed.
+
+ >>> m['e'] = 'e'
+ Traceback (most recent call last):
+ ValueError: Key cannot map to itself
+
+ >>> m['d'] = 'e'
+ Traceback (most recent call last):
+ ValueError: Key/Value pairs may not overlap
+
+ >>> m['e'] = 'd'
+ Traceback (most recent call last):
+ ValueError: Key/Value pairs may not overlap
+
+ >>> print(m.pop('d'))
+ c
+
+ >>> 'c' in m
+ False
+
+ >>> m = BijectiveMap(dict(a='b'))
+ >>> len(m)
+ 1
+ >>> print(m['b'])
+ a
+
+ >>> m = BijectiveMap()
+ >>> m.update(a='b')
+ >>> m['b']
+ 'a'
+
+ >>> del m['b']
+ >>> len(m)
+ 0
+ >>> 'a' in m
+ False
+ """
+
+ def __init__(self, *args, **kwargs):
+ super().__init__()
+ self.update(*args, **kwargs)
+
+ def __setitem__(self, item, value):
+ if item == value:
+ raise ValueError("Key cannot map to itself")
+ overlap = (
+ item in self
+ and self[item] != value
+ or value in self
+ and self[value] != item
+ )
+ if overlap:
+ raise ValueError("Key/Value pairs may not overlap")
+ super().__setitem__(item, value)
+ super().__setitem__(value, item)
+
+ def __delitem__(self, item):
+ self.pop(item)
+
+ def __len__(self):
+ return super().__len__() // 2
+
+ def pop(self, key, *args, **kwargs):
+ mirror = self[key]
+ super().__delitem__(mirror)
+ return super().pop(key, *args, **kwargs)
+
+ def update(self, *args, **kwargs):
+ # build a dictionary using the default constructs
+ d = dict(*args, **kwargs)
+ # build this dictionary using transformed keys.
+ for item in d.items():
+ self.__setitem__(*item)
+
+
+class FrozenDict(collections.abc.Mapping, collections.abc.Hashable):
+ """
+ An immutable mapping.
+
+ >>> a = FrozenDict(a=1, b=2)
+ >>> b = FrozenDict(a=1, b=2)
+ >>> a == b
+ True
+
+ >>> a == dict(a=1, b=2)
+ True
+ >>> dict(a=1, b=2) == a
+ True
+ >>> 'a' in a
+ True
+ >>> type(hash(a)) is type(0)
+ True
+ >>> set(iter(a)) == {'a', 'b'}
+ True
+ >>> len(a)
+ 2
+ >>> a['a'] == a.get('a') == 1
+ True
+
+ >>> a['c'] = 3
+ Traceback (most recent call last):
+ ...
+ TypeError: 'FrozenDict' object does not support item assignment
+
+ >>> a.update(y=3)
+ Traceback (most recent call last):
+ ...
+ AttributeError: 'FrozenDict' object has no attribute 'update'
+
+ Copies should compare equal
+
+ >>> copy.copy(a) == a
+ True
+
+ Copies should be the same type
+
+ >>> isinstance(copy.copy(a), FrozenDict)
+ True
+
+ FrozenDict supplies .copy(), even though
+ collections.abc.Mapping doesn't demand it.
+
+ >>> a.copy() == a
+ True
+ >>> a.copy() is not a
+ True
+ """
+
+ __slots__ = ['__data']
+
+ def __new__(cls, *args, **kwargs):
+ self = super().__new__(cls)
+ self.__data = dict(*args, **kwargs)
+ return self
+
+ # Container
+ def __contains__(self, key):
+ return key in self.__data
+
+ # Hashable
+ def __hash__(self):
+ return hash(tuple(sorted(self.__data.items())))
+
+ # Mapping
+ def __iter__(self):
+ return iter(self.__data)
+
+ def __len__(self):
+ return len(self.__data)
+
+ def __getitem__(self, key):
+ return self.__data[key]
+
+ # override get for efficiency provided by dict
+ def get(self, *args, **kwargs):
+ return self.__data.get(*args, **kwargs)
+
+ # override eq to recognize underlying implementation
+ def __eq__(self, other):
+ if isinstance(other, FrozenDict):
+ other = other.__data
+ return self.__data.__eq__(other)
+
+ def copy(self):
+ "Return a shallow copy of self"
+ return copy.copy(self)
+
+
+class Enumeration(ItemsAsAttributes, BijectiveMap):
+ """
+ A convenient way to provide enumerated values
+
+ >>> e = Enumeration('a b c')
+ >>> e['a']
+ 0
+
+ >>> e.a
+ 0
+
+ >>> e[1]
+ 'b'
+
+ >>> set(e.names) == set('abc')
+ True
+
+ >>> set(e.codes) == set(range(3))
+ True
+
+ >>> e.get('d') is None
+ True
+
+ Codes need not start with 0
+
+ >>> e = Enumeration('a b c', range(1, 4))
+ >>> e['a']
+ 1
+
+ >>> e[3]
+ 'c'
+ """
+
+ def __init__(self, names, codes=None):
+ if isinstance(names, str):
+ names = names.split()
+ if codes is None:
+ codes = itertools.count()
+ super().__init__(zip(names, codes))
+
+ @property
+ def names(self):
+ return (key for key in self if isinstance(key, str))
+
+ @property
+ def codes(self):
+ return (self[name] for name in self.names)
+
+
+class Everything:
+ """
+ A collection "containing" every possible thing.
+
+ >>> 'foo' in Everything()
+ True
+
+ >>> import random
+ >>> random.randint(1, 999) in Everything()
+ True
+
+ >>> random.choice([None, 'foo', 42, ('a', 'b', 'c')]) in Everything()
+ True
+ """
+
+ def __contains__(self, other):
+ return True
+
+
+class InstrumentedDict(collections.UserDict):
+ """
+ Instrument an existing dictionary with additional
+ functionality, but always reference and mutate
+ the original dictionary.
+
+ >>> orig = {'a': 1, 'b': 2}
+ >>> inst = InstrumentedDict(orig)
+ >>> inst['a']
+ 1
+ >>> inst['c'] = 3
+ >>> orig['c']
+ 3
+ >>> inst.keys() == orig.keys()
+ True
+ """
+
+ def __init__(self, data):
+ super().__init__()
+ self.data = data
+
+
+class Least:
+ """
+ A value that is always lesser than any other
+
+ >>> least = Least()
+ >>> 3 < least
+ False
+ >>> 3 > least
+ True
+ >>> least < 3
+ True
+ >>> least <= 3
+ True
+ >>> least > 3
+ False
+ >>> 'x' > least
+ True
+ >>> None > least
+ True
+ """
+
+ def __le__(self, other):
+ return True
+
+ __lt__ = __le__
+
+ def __ge__(self, other):
+ return False
+
+ __gt__ = __ge__
+
+
+class Greatest:
+ """
+ A value that is always greater than any other
+
+ >>> greatest = Greatest()
+ >>> 3 < greatest
+ True
+ >>> 3 > greatest
+ False
+ >>> greatest < 3
+ False
+ >>> greatest > 3
+ True
+ >>> greatest >= 3
+ True
+ >>> 'x' > greatest
+ False
+ >>> None > greatest
+ False
+ """
+
+ def __ge__(self, other):
+ return True
+
+ __gt__ = __ge__
+
+ def __le__(self, other):
+ return False
+
+ __lt__ = __le__
+
+
+def pop_all(items):
+ """
+ Clear items in place and return a copy of items.
+
+ >>> items = [1, 2, 3]
+ >>> popped = pop_all(items)
+ >>> popped is items
+ False
+ >>> popped
+ [1, 2, 3]
+ >>> items
+ []
+ """
+ result, items[:] = items[:], []
+ return result
+
+
+class FreezableDefaultDict(collections.defaultdict):
+ """
+ Often it is desirable to prevent the mutation of
+ a default dict after its initial construction, such
+ as to prevent mutation during iteration.
+
+ >>> dd = FreezableDefaultDict(list)
+ >>> dd[0].append('1')
+ >>> dd.freeze()
+ >>> dd[1]
+ []
+ >>> len(dd)
+ 1
+ """
+
+ def __missing__(self, key):
+ return getattr(self, '_frozen', super().__missing__)(key)
+
+ def freeze(self):
+ self._frozen = lambda key: self.default_factory()
+
+
+class Accumulator:
+ def __init__(self, initial=0):
+ self.val = initial
+
+ def __call__(self, val):
+ self.val += val
+ return self.val
+
+
+class WeightedLookup(RangeMap):
+ """
+ Given parameters suitable for a dict representing keys
+ and a weighted proportion, return a RangeMap representing
+ spans of values proportial to the weights:
+
+ >>> even = WeightedLookup(a=1, b=1)
+
+ [0, 1) -> a
+ [1, 2) -> b
+
+ >>> lk = WeightedLookup(a=1, b=2)
+
+ [0, 1) -> a
+ [1, 3) -> b
+
+ >>> lk[.5]
+ 'a'
+ >>> lk[1.5]
+ 'b'
+
+ Adds ``.random()`` to select a random weighted value:
+
+ >>> lk.random() in ['a', 'b']
+ True
+
+ >>> choices = [lk.random() for x in range(1000)]
+
+ Statistically speaking, choices should be .5 a:b
+ >>> ratio = choices.count('a') / choices.count('b')
+ >>> .4 < ratio < .6
+ True
+ """
+
+ def __init__(self, *args, **kwargs):
+ raw = dict(*args, **kwargs)
+
+ # allocate keys by weight
+ indexes = map(Accumulator(), raw.values())
+ super().__init__(zip(indexes, raw.keys()), key_match_comparator=operator.lt)
+
+ def random(self):
+ lower, upper = self.bounds()
+ selector = random.random() * upper
+ return self[selector]
diff --git a/contrib/python/jaraco.collections/jaraco/collections/py.typed b/contrib/python/jaraco.collections/jaraco/collections/py.typed
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/contrib/python/jaraco.collections/jaraco/collections/py.typed
diff --git a/contrib/python/jaraco.collections/ya.make b/contrib/python/jaraco.collections/ya.make
new file mode 100644
index 0000000000..bcaca3b818
--- /dev/null
+++ b/contrib/python/jaraco.collections/ya.make
@@ -0,0 +1,27 @@
+# Generated by devtools/yamaker (pypi).
+
+PY3_LIBRARY()
+
+VERSION(5.1.0)
+
+LICENSE(MIT)
+
+PEERDIR(
+ contrib/python/jaraco.text
+)
+
+NO_LINT()
+
+PY_SRCS(
+ TOP_LEVEL
+ jaraco/collections/__init__.py
+)
+
+RESOURCE_FILES(
+ PREFIX contrib/python/jaraco.collections/
+ .dist-info/METADATA
+ .dist-info/top_level.txt
+ jaraco/collections/py.typed
+)
+
+END()
diff --git a/contrib/python/setuptools/py3/.dist-info/METADATA b/contrib/python/setuptools/py3/.dist-info/METADATA
index 11cc0cda4e..d9f4d9351b 100644
--- a/contrib/python/setuptools/py3/.dist-info/METADATA
+++ b/contrib/python/setuptools/py3/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: setuptools
-Version: 74.1.3
+Version: 75.6.0
Summary: Easily download, build, install, upgrade, and uninstall Python packages
Author-email: Python Packaging Authority <distutils-sig@python.org>
Project-URL: Source, https://github.com/pypa/setuptools
@@ -16,68 +16,71 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Archiving :: Packaging
Classifier: Topic :: System :: Systems Administration
Classifier: Topic :: Utilities
-Requires-Python: >=3.8
+Requires-Python: >=3.9
Description-Content-Type: text/x-rst
License-File: LICENSE
+Provides-Extra: test
+Requires-Dist: pytest!=8.1.*,>=6; extra == "test"
+Requires-Dist: virtualenv>=13.0.0; extra == "test"
+Requires-Dist: wheel>=0.44.0; extra == "test"
+Requires-Dist: pip>=19.1; extra == "test"
+Requires-Dist: packaging>=24.2; extra == "test"
+Requires-Dist: jaraco.envs>=2.2; extra == "test"
+Requires-Dist: pytest-xdist>=3; extra == "test"
+Requires-Dist: jaraco.path>=3.2.0; extra == "test"
+Requires-Dist: build[virtualenv]>=1.0.3; extra == "test"
+Requires-Dist: filelock>=3.4.0; extra == "test"
+Requires-Dist: ini2toml[lite]>=0.14; extra == "test"
+Requires-Dist: tomli-w>=1.0.0; extra == "test"
+Requires-Dist: pytest-timeout; extra == "test"
+Requires-Dist: pytest-perf; sys_platform != "cygwin" and extra == "test"
+Requires-Dist: jaraco.develop>=7.21; (python_version >= "3.9" and sys_platform != "cygwin") and extra == "test"
+Requires-Dist: pytest-home>=0.5; extra == "test"
+Requires-Dist: pytest-subprocess; extra == "test"
+Requires-Dist: pyproject-hooks!=1.1; extra == "test"
+Requires-Dist: jaraco.test>=5.5; extra == "test"
+Provides-Extra: doc
+Requires-Dist: sphinx>=3.5; extra == "doc"
+Requires-Dist: jaraco.packaging>=9.3; extra == "doc"
+Requires-Dist: rst.linker>=1.9; extra == "doc"
+Requires-Dist: furo; extra == "doc"
+Requires-Dist: sphinx-lint; extra == "doc"
+Requires-Dist: jaraco.tidelift>=1.4; extra == "doc"
+Requires-Dist: pygments-github-lexers==0.0.5; extra == "doc"
+Requires-Dist: sphinx-favicon; extra == "doc"
+Requires-Dist: sphinx-inline-tabs; extra == "doc"
+Requires-Dist: sphinx-reredirects; extra == "doc"
+Requires-Dist: sphinxcontrib-towncrier; extra == "doc"
+Requires-Dist: sphinx-notfound-page<2,>=1; extra == "doc"
+Requires-Dist: pyproject-hooks!=1.1; extra == "doc"
+Requires-Dist: towncrier<24.7; extra == "doc"
+Provides-Extra: ssl
Provides-Extra: certs
-Provides-Extra: check
-Requires-Dist: pytest-checkdocs >=2.4 ; extra == 'check'
-Requires-Dist: pytest-ruff >=0.2.1 ; (sys_platform != "cygwin") and extra == 'check'
-Requires-Dist: ruff >=0.5.2 ; (sys_platform != "cygwin") and extra == 'check'
Provides-Extra: core
-Requires-Dist: packaging >=24 ; extra == 'core'
-Requires-Dist: more-itertools >=8.8 ; extra == 'core'
-Requires-Dist: jaraco.text >=3.7 ; extra == 'core'
-Requires-Dist: wheel >=0.43.0 ; extra == 'core'
-Requires-Dist: platformdirs >=2.6.2 ; extra == 'core'
-Requires-Dist: importlib-metadata >=6 ; (python_version < "3.10") and extra == 'core'
-Requires-Dist: tomli >=2.0.1 ; (python_version < "3.11") and extra == 'core'
-Requires-Dist: importlib-resources >=5.10.2 ; (python_version < "3.9") and extra == 'core'
+Requires-Dist: packaging>=24.2; extra == "core"
+Requires-Dist: more_itertools>=8.8; extra == "core"
+Requires-Dist: jaraco.text>=3.7; extra == "core"
+Requires-Dist: importlib_metadata>=6; python_version < "3.10" and extra == "core"
+Requires-Dist: tomli>=2.0.1; python_version < "3.11" and extra == "core"
+Requires-Dist: wheel>=0.43.0; extra == "core"
+Requires-Dist: platformdirs>=4.2.2; extra == "core"
+Requires-Dist: jaraco.collections; extra == "core"
+Requires-Dist: jaraco.functools>=4; extra == "core"
+Requires-Dist: packaging; extra == "core"
+Requires-Dist: more_itertools; extra == "core"
+Provides-Extra: check
+Requires-Dist: pytest-checkdocs>=2.4; extra == "check"
+Requires-Dist: pytest-ruff>=0.2.1; sys_platform != "cygwin" and extra == "check"
+Requires-Dist: ruff>=0.7.0; sys_platform != "cygwin" and extra == "check"
Provides-Extra: cover
-Requires-Dist: pytest-cov ; extra == 'cover'
-Provides-Extra: doc
-Requires-Dist: sphinx >=3.5 ; extra == 'doc'
-Requires-Dist: jaraco.packaging >=9.3 ; extra == 'doc'
-Requires-Dist: rst.linker >=1.9 ; extra == 'doc'
-Requires-Dist: furo ; extra == 'doc'
-Requires-Dist: sphinx-lint ; extra == 'doc'
-Requires-Dist: jaraco.tidelift >=1.4 ; extra == 'doc'
-Requires-Dist: pygments-github-lexers ==0.0.5 ; extra == 'doc'
-Requires-Dist: sphinx-favicon ; extra == 'doc'
-Requires-Dist: sphinx-inline-tabs ; extra == 'doc'
-Requires-Dist: sphinx-reredirects ; extra == 'doc'
-Requires-Dist: sphinxcontrib-towncrier ; extra == 'doc'
-Requires-Dist: sphinx-notfound-page <2,>=1 ; extra == 'doc'
-Requires-Dist: pyproject-hooks !=1.1 ; extra == 'doc'
-Requires-Dist: towncrier <24.7 ; extra == 'doc'
+Requires-Dist: pytest-cov; extra == "cover"
Provides-Extra: enabler
-Requires-Dist: pytest-enabler >=2.2 ; extra == 'enabler'
-Provides-Extra: ssl
-Provides-Extra: test
-Requires-Dist: pytest !=8.1.*,>=6 ; extra == 'test'
-Requires-Dist: virtualenv >=13.0.0 ; extra == 'test'
-Requires-Dist: wheel >=0.44.0 ; extra == 'test'
-Requires-Dist: pip >=19.1 ; extra == 'test'
-Requires-Dist: packaging >=23.2 ; extra == 'test'
-Requires-Dist: jaraco.envs >=2.2 ; extra == 'test'
-Requires-Dist: pytest-xdist >=3 ; extra == 'test'
-Requires-Dist: jaraco.path >=3.2.0 ; extra == 'test'
-Requires-Dist: build[virtualenv] >=1.0.3 ; extra == 'test'
-Requires-Dist: filelock >=3.4.0 ; extra == 'test'
-Requires-Dist: ini2toml[lite] >=0.14 ; extra == 'test'
-Requires-Dist: tomli-w >=1.0.0 ; extra == 'test'
-Requires-Dist: pytest-timeout ; extra == 'test'
-Requires-Dist: pytest-home >=0.5 ; extra == 'test'
-Requires-Dist: pytest-subprocess ; extra == 'test'
-Requires-Dist: pyproject-hooks !=1.1 ; extra == 'test'
-Requires-Dist: jaraco.test ; extra == 'test'
-Requires-Dist: jaraco.develop >=7.21 ; (python_version >= "3.9" and sys_platform != "cygwin") and extra == 'test'
-Requires-Dist: pytest-perf ; (sys_platform != "cygwin") and extra == 'test'
+Requires-Dist: pytest-enabler>=2.2; extra == "enabler"
Provides-Extra: type
-Requires-Dist: pytest-mypy ; extra == 'type'
-Requires-Dist: mypy ==1.11.* ; extra == 'type'
-Requires-Dist: importlib-metadata >=7.0.2 ; (python_version < "3.10") and extra == 'type'
-Requires-Dist: jaraco.develop >=7.21 ; (sys_platform != "cygwin") and extra == 'type'
+Requires-Dist: pytest-mypy; extra == "type"
+Requires-Dist: mypy<1.14,>=1.12; extra == "type"
+Requires-Dist: importlib_metadata>=7.0.2; python_version < "3.10" and extra == "type"
+Requires-Dist: jaraco.develop>=7.21; sys_platform != "cygwin" and extra == "type"
.. |pypi-version| image:: https://img.shields.io/pypi/v/setuptools.svg
:target: https://pypi.org/project/setuptools
diff --git a/contrib/python/setuptools/py3/.dist-info/entry_points.txt b/contrib/python/setuptools/py3/.dist-info/entry_points.txt
index abc3b7833c..0db0a6c8f1 100644
--- a/contrib/python/setuptools/py3/.dist-info/entry_points.txt
+++ b/contrib/python/setuptools/py3/.dist-info/entry_points.txt
@@ -20,7 +20,6 @@ rotate = setuptools.command.rotate:rotate
saveopts = setuptools.command.saveopts:saveopts
sdist = setuptools.command.sdist:sdist
setopt = setuptools.command.setopt:setopt
-upload_docs = setuptools.command.upload_docs:upload_docs
[distutils.setup_keywords]
dependency_links = setuptools.dist:assert_string_list
diff --git a/contrib/python/setuptools/py3/.yandex_meta/yamaker.yaml b/contrib/python/setuptools/py3/.yandex_meta/yamaker.yaml
index 4ac46cc577..946363d50f 100644
--- a/contrib/python/setuptools/py3/.yandex_meta/yamaker.yaml
+++ b/contrib/python/setuptools/py3/.yandex_meta/yamaker.yaml
@@ -1,4 +1,5 @@
requirements:
+ - jaraco.collections
- jaraco.context
- jaraco.functools
- jaraco.text
diff --git a/contrib/python/setuptools/py3/_distutils_hack/__init__.py b/contrib/python/setuptools/py3/_distutils_hack/__init__.py
index 30ac3a7403..94f71b99ec 100644
--- a/contrib/python/setuptools/py3/_distutils_hack/__init__.py
+++ b/contrib/python/setuptools/py3/_distutils_hack/__init__.py
@@ -3,8 +3,7 @@ import os
import sys
report_url = (
- "https://github.com/pypa/setuptools/issues/new?"
- "template=distutils-deprecation.yml"
+ "https://github.com/pypa/setuptools/issues/new?template=distutils-deprecation.yml"
)
@@ -91,7 +90,7 @@ def do_override():
class _TrivialRe:
- def __init__(self, *patterns):
+ def __init__(self, *patterns) -> None:
self._patterns = patterns
def match(self, string):
diff --git a/contrib/python/setuptools/py3/pkg_resources/__init__.py b/contrib/python/setuptools/py3/pkg_resources/__init__.py
index 4100470fcc..273bff7a65 100644
--- a/contrib/python/setuptools/py3/pkg_resources/__init__.py
+++ b/contrib/python/setuptools/py3/pkg_resources/__init__.py
@@ -21,8 +21,8 @@ from __future__ import annotations
import sys
-if sys.version_info < (3, 8): # noqa: UP036 # Check for unsupported versions
- raise RuntimeError("Python 3.8 or later is required")
+if sys.version_info < (3, 9): # noqa: UP036 # Check for unsupported versions
+ raise RuntimeError("Python 3.9 or later is required")
import _imp
import collections
@@ -50,22 +50,17 @@ import types
import warnings
import zipfile
import zipimport
+from collections.abc import Iterable, Iterator, Mapping, MutableSequence
from pkgutil import get_importer
from typing import (
TYPE_CHECKING,
Any,
BinaryIO,
Callable,
- Dict,
- Iterable,
- Iterator,
Literal,
- Mapping,
- MutableSequence,
NamedTuple,
NoReturn,
Protocol,
- Tuple,
TypeVar,
Union,
overload,
@@ -172,7 +167,7 @@ def _sget_dict(val):
return val.copy()
-def _sset_dict(key, ob, state):
+def _sset_dict(key, ob, state) -> None:
ob.clear()
ob.update(state)
@@ -181,7 +176,7 @@ def _sget_object(val):
return val.__getstate__()
-def _sset_object(key, ob, state):
+def _sset_object(key, ob, state) -> None:
ob.__setstate__(state)
@@ -387,7 +382,7 @@ class UnknownExtra(ResolutionError):
_provider_factories: dict[type[_ModuleLike], _ProviderFactoryType] = {}
-PY_MAJOR = '{}.{}'.format(*sys.version_info)
+PY_MAJOR = f'{sys.version_info.major}.{sys.version_info.minor}'
EGG_DIST = 3
BINARY_DIST = 2
SOURCE_DIST = 1
@@ -424,7 +419,7 @@ def get_provider(moduleOrReq: str | Requirement) -> IResourceProvider | Distribu
return _find_adapter(_provider_factories, loader)(module)
-@functools.lru_cache(maxsize=None)
+@functools.cache
def _macos_vers():
version = platform.mac_ver()[0]
# fallback for MacPorts
@@ -1090,7 +1085,7 @@ class WorkingSet:
for dist in self:
callback(dist)
- def _added_new(self, dist):
+ def _added_new(self, dist) -> None:
for callback in self.callbacks:
callback(dist)
@@ -1120,7 +1115,7 @@ class WorkingSet:
self.callbacks = callbacks[:]
-class _ReqExtras(Dict["Requirement", Tuple[str, ...]]):
+class _ReqExtras(dict["Requirement", tuple[str, ...]]):
"""
Map each requirement to the extras that demanded it.
"""
@@ -1463,7 +1458,7 @@ class ResourceManager:
return target_path
@staticmethod
- def _warn_unsafe_extraction_path(path):
+ def _warn_unsafe_extraction_path(path) -> None:
"""
If the default extraction path is overridden and set to an insecure
location, such as /tmp, it opens up an opportunity for an attacker to
@@ -1577,7 +1572,7 @@ def safe_version(version: str) -> str:
return re.sub('[^A-Za-z0-9.]+', '-', version)
-def _forgiving_version(version):
+def _forgiving_version(version) -> str:
"""Fallback when ``safe_version`` is not safe enough
>>> parse_version(_forgiving_version('0.23ubuntu1'))
<Version('0.23.dev0+sanitized.ubuntu1')>
@@ -1779,7 +1774,7 @@ class NullProvider:
return base
@staticmethod
- def _validate_resource_path(path):
+ def _validate_resource_path(path) -> None:
"""
Validate the resource paths according to the docs.
https://setuptools.pypa.io/en/latest/pkg_resources.html#basic-resource-access
@@ -1890,7 +1885,7 @@ class EggProvider(NullProvider):
egg = next(eggs, None)
egg and self._set_egg(egg)
- def _set_egg(self, path: str):
+ def _set_egg(self, path: str) -> None:
self.egg_name = os.path.basename(path)
self.egg_info = os.path.join(path, 'EGG-INFO')
self.egg_root = path
@@ -1918,7 +1913,7 @@ class DefaultProvider(EggProvider):
return stream.read()
@classmethod
- def _register(cls):
+ def _register(cls) -> None:
loader_names = (
'SourceFileLoader',
'SourcelessFileLoader',
@@ -1952,7 +1947,7 @@ class EmptyProvider(NullProvider):
empty_provider = EmptyProvider()
-class ZipManifests(Dict[str, "MemoizedZipManifests.manifest_mod"]):
+class ZipManifests(dict[str, "MemoizedZipManifests.manifest_mod"]):
"""
zip manifest builder
"""
@@ -2069,7 +2064,7 @@ class ZipProvider(EggProvider):
# return the extracted directory name
return os.path.dirname(last)
- timestamp, size = self._get_date_and_size(self.zipinfo[zip_path])
+ timestamp, _size = self._get_date_and_size(self.zipinfo[zip_path])
if not WRITE_SUPPORT:
raise OSError(
@@ -2208,7 +2203,7 @@ class FileMetadata(EmptyProvider):
self._warn_on_replacement(metadata)
return metadata
- def _warn_on_replacement(self, metadata):
+ def _warn_on_replacement(self, metadata) -> None:
replacement_char = '�'
if replacement_char in metadata:
tmpl = "{self.path} could not be properly decoded in UTF-8"
@@ -2505,7 +2500,7 @@ def _handle_ns(packageName, path_item):
return subpath
-def _rebuild_mod_path(orig_path, package_name, module: types.ModuleType):
+def _rebuild_mod_path(orig_path, package_name, module: types.ModuleType) -> None:
"""
Rebuild module.__path__ ensuring that all entries are ordered
corresponding to their sys.path order
@@ -2624,7 +2619,7 @@ def null_ns_handler(
path_item: str | None,
packageName: str | None,
module: _ModuleLike | None,
-):
+) -> None:
return None
@@ -2635,7 +2630,7 @@ register_namespace_handler(object, null_ns_handler)
def normalize_path(filename: StrPath) -> str: ...
@overload
def normalize_path(filename: BytesPath) -> bytes: ...
-def normalize_path(filename: StrOrBytesPath):
+def normalize_path(filename: StrOrBytesPath) -> str | bytes:
"""Normalize a file/dir name for comparison purposes"""
return os.path.normcase(os.path.realpath(os.path.normpath(_cygwin_patch(filename))))
@@ -2662,7 +2657,7 @@ if TYPE_CHECKING:
else:
- @functools.lru_cache(maxsize=None)
+ @functools.cache
def _normalize_cached(filename):
return normalize_path(filename)
@@ -2691,7 +2686,7 @@ def _is_unpacked_egg(path):
)
-def _set_parent_ns(packageName):
+def _set_parent_ns(packageName) -> None:
parts = packageName.split('.')
name = parts.pop()
if parts:
@@ -2777,7 +2772,7 @@ class EntryPoint:
if require:
# We could pass `env` and `installer` directly,
# but keeping `*args` and `**kwargs` for backwards compatibility
- self.require(*args, **kwargs) # type: ignore
+ self.require(*args, **kwargs) # type: ignore[arg-type]
return self.resolve()
def resolve(self) -> _ResolvedEntryPoint:
@@ -3336,7 +3331,7 @@ class Distribution:
" to sys.path" % (modname, fn, self.location),
)
- def has_version(self):
+ def has_version(self) -> bool:
try:
self.version
except ValueError:
@@ -3552,7 +3547,7 @@ def ensure_directory(path: StrOrBytesPath) -> None:
os.makedirs(dirname, exist_ok=True)
-def _bypass_ensure_directory(path):
+def _bypass_ensure_directory(path) -> None:
"""Sandbox-bypassing version of ensure_directory()"""
if not WRITE_SUPPORT:
raise OSError('"os.mkdir" not supported on this platform.')
@@ -3784,7 +3779,7 @@ def _call_aside(f, *args, **kwargs):
@_call_aside
-def _initialize(g=globals()):
+def _initialize(g=globals()) -> None:
"Set up global resource manager (deliberately not state-saved)"
manager = ResourceManager()
g['_manager'] = manager
@@ -3796,7 +3791,7 @@ def _initialize(g=globals()):
@_call_aside
-def _initialize_master_working_set():
+def _initialize_master_working_set() -> None:
"""
Prepare the master working set and make the ``require()``
API available.
diff --git a/contrib/python/setuptools/py3/setuptools/__init__.py b/contrib/python/setuptools/py3/setuptools/__init__.py
index ab373c51d6..4f5c01708a 100644
--- a/contrib/python/setuptools/py3/setuptools/__init__.py
+++ b/contrib/python/setuptools/py3/setuptools/__init__.py
@@ -60,7 +60,7 @@ def _install_setup_requires(attrs):
fetch_build_eggs interface.
"""
- def __init__(self, attrs: Mapping[str, object]):
+ def __init__(self, attrs: Mapping[str, object]) -> None:
_incl = 'dependency_links', 'setup_requires'
filtered = {k: attrs[k] for k in set(_incl) & set(attrs)}
super().__init__(filtered)
@@ -70,7 +70,7 @@ def _install_setup_requires(attrs):
def _get_project_config_files(self, filenames=None):
"""Ignore ``pyproject.toml``, they are not related to setup_requires"""
try:
- cfg, toml = super()._split_standard_project_metadata(filenames)
+ cfg, _toml = super()._split_standard_project_metadata(filenames)
except Exception:
return filenames, ()
return cfg, ()
@@ -89,7 +89,7 @@ def _install_setup_requires(attrs):
_fetch_build_eggs(dist)
-def _fetch_build_eggs(dist):
+def _fetch_build_eggs(dist: Distribution):
try:
dist.fetch_build_eggs(dist.setup_requires)
except Exception as ex:
@@ -111,8 +111,8 @@ def _fetch_build_eggs(dist):
def setup(**attrs):
- # Make sure we have any requirements needed to interpret 'attrs'.
logging.configure()
+ # Make sure we have any requirements needed to interpret 'attrs'.
_install_setup_requires(attrs)
return distutils.core.setup(**attrs)
@@ -120,10 +120,8 @@ def setup(**attrs):
setup.__doc__ = distutils.core.setup.__doc__
if TYPE_CHECKING:
- from typing_extensions import TypeAlias
-
# Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962
- _Command: TypeAlias = distutils.core.Command
+ from distutils.core import Command as _Command
else:
_Command = monkey.get_unpatched(distutils.core.Command)
@@ -169,7 +167,7 @@ class Command(_Command):
command_consumes_arguments = False
distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution
- def __init__(self, dist: Distribution, **kw):
+ def __init__(self, dist: Distribution, **kw) -> None:
"""
Construct the command for dist, updating
vars(self) with any keyword parameters.
@@ -188,7 +186,7 @@ class Command(_Command):
)
return val
- def ensure_string_list(self, option):
+ def ensure_string_list(self, option: str) -> None:
r"""Ensure that 'option' is a list of strings. If 'option' is
currently a string, we split it either on /,\s*/ or /\s+/, so
"foo bar baz", "foo,bar,baz", and "foo, bar baz" all become
@@ -228,7 +226,7 @@ class Command(_Command):
) -> _Command:
cmd = _Command.reinitialize_command(self, command, reinit_subcommands)
vars(cmd).update(kw)
- return cmd
+ return cmd # pyright: ignore[reportReturnType] # pypa/distutils#307
@abstractmethod
def initialize_options(self) -> None:
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_collections.py b/contrib/python/setuptools/py3/setuptools/_distutils/_collections.py
deleted file mode 100644
index 863030b3cf..0000000000
--- a/contrib/python/setuptools/py3/setuptools/_distutils/_collections.py
+++ /dev/null
@@ -1,58 +0,0 @@
-from __future__ import annotations
-
-import collections
-import itertools
-
-
-# from jaraco.collections 3.5.1
-class DictStack(list, collections.abc.Mapping):
- """
- A stack of dictionaries that behaves as a view on those dictionaries,
- giving preference to the last.
-
- >>> stack = DictStack([dict(a=1, c=2), dict(b=2, a=2)])
- >>> stack['a']
- 2
- >>> stack['b']
- 2
- >>> stack['c']
- 2
- >>> len(stack)
- 3
- >>> stack.push(dict(a=3))
- >>> stack['a']
- 3
- >>> set(stack.keys()) == set(['a', 'b', 'c'])
- True
- >>> set(stack.items()) == set([('a', 3), ('b', 2), ('c', 2)])
- True
- >>> dict(**stack) == dict(stack) == dict(a=3, c=2, b=2)
- True
- >>> d = stack.pop()
- >>> stack['a']
- 2
- >>> d = stack.pop()
- >>> stack['a']
- 1
- >>> stack.get('b', None)
- >>> 'c' in stack
- True
- """
-
- def __iter__(self):
- dicts = list.__iter__(self)
- return iter(set(itertools.chain.from_iterable(c.keys() for c in dicts)))
-
- def __getitem__(self, key):
- for scope in reversed(tuple(list.__iter__(self))):
- if key in scope:
- return scope[key]
- raise KeyError(key)
-
- push = list.append
-
- def __contains__(self, other):
- return collections.abc.Mapping.__contains__(self, other)
-
- def __len__(self):
- return len(list(iter(self)))
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_functools.py b/contrib/python/setuptools/py3/setuptools/_distutils/_functools.py
deleted file mode 100644
index e03365eafa..0000000000
--- a/contrib/python/setuptools/py3/setuptools/_distutils/_functools.py
+++ /dev/null
@@ -1,73 +0,0 @@
-import collections.abc
-import functools
-
-
-# from jaraco.functools 3.5
-def pass_none(func):
- """
- Wrap func so it's not called if its first param is None
-
- >>> print_text = pass_none(print)
- >>> print_text('text')
- text
- >>> print_text(None)
- """
-
- @functools.wraps(func)
- def wrapper(param, *args, **kwargs):
- if param is not None:
- return func(param, *args, **kwargs)
-
- return wrapper
-
-
-# from jaraco.functools 4.0
-@functools.singledispatch
-def _splat_inner(args, func):
- """Splat args to func."""
- return func(*args)
-
-
-@_splat_inner.register
-def _(args: collections.abc.Mapping, func):
- """Splat kargs to func as kwargs."""
- return func(**args)
-
-
-def splat(func):
- """
- Wrap func to expect its parameters to be passed positionally in a tuple.
-
- Has a similar effect to that of ``itertools.starmap`` over
- simple ``map``.
-
- >>> import itertools, operator
- >>> pairs = [(-1, 1), (0, 2)]
- >>> _ = tuple(itertools.starmap(print, pairs))
- -1 1
- 0 2
- >>> _ = tuple(map(splat(print), pairs))
- -1 1
- 0 2
-
- The approach generalizes to other iterators that don't have a "star"
- equivalent, such as a "starfilter".
-
- >>> list(filter(splat(operator.add), pairs))
- [(0, 2)]
-
- Splat also accepts a mapping argument.
-
- >>> def is_nice(msg, code):
- ... return "smile" in msg or code == 0
- >>> msgs = [
- ... dict(msg='smile!', code=20),
- ... dict(msg='error :(', code=1),
- ... dict(msg='unknown', code=0),
- ... ]
- >>> for msg in filter(splat(is_nice), msgs):
- ... print(msg)
- {'msg': 'smile!', 'code': 20}
- {'msg': 'unknown', 'code': 0}
- """
- return functools.wraps(func)(functools.partial(_splat_inner, func=func))
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_itertools.py b/contrib/python/setuptools/py3/setuptools/_distutils/_itertools.py
deleted file mode 100644
index 85b2951186..0000000000
--- a/contrib/python/setuptools/py3/setuptools/_distutils/_itertools.py
+++ /dev/null
@@ -1,52 +0,0 @@
-# from more_itertools 10.2
-def always_iterable(obj, base_type=(str, bytes)):
- """If *obj* is iterable, return an iterator over its items::
-
- >>> obj = (1, 2, 3)
- >>> list(always_iterable(obj))
- [1, 2, 3]
-
- If *obj* is not iterable, return a one-item iterable containing *obj*::
-
- >>> obj = 1
- >>> list(always_iterable(obj))
- [1]
-
- If *obj* is ``None``, return an empty iterable:
-
- >>> obj = None
- >>> list(always_iterable(None))
- []
-
- By default, binary and text strings are not considered iterable::
-
- >>> obj = 'foo'
- >>> list(always_iterable(obj))
- ['foo']
-
- If *base_type* is set, objects for which ``isinstance(obj, base_type)``
- returns ``True`` won't be considered iterable.
-
- >>> obj = {'a': 1}
- >>> list(always_iterable(obj)) # Iterate over the dict's keys
- ['a']
- >>> list(always_iterable(obj, base_type=dict)) # Treat dicts as a unit
- [{'a': 1}]
-
- Set *base_type* to ``None`` to avoid any special handling and treat objects
- Python considers iterable as iterable:
-
- >>> obj = 'foo'
- >>> list(always_iterable(obj, base_type=None))
- ['f', 'o', 'o']
- """
- if obj is None:
- return iter(())
-
- if (base_type is not None) and isinstance(obj, base_type):
- return iter((obj,))
-
- try:
- return iter(obj)
- except TypeError:
- return iter((obj,))
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_modified.py b/contrib/python/setuptools/py3/setuptools/_distutils/_modified.py
index b7bdaa2943..7cdca9398f 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/_modified.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/_modified.py
@@ -3,7 +3,8 @@
import functools
import os.path
-from ._functools import splat
+from jaraco.functools import splat
+
from .compat.py39 import zip_strict
from .errors import DistutilsFileError
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/_msvccompiler.py b/contrib/python/setuptools/py3/setuptools/_distutils/_msvccompiler.py
index bf10ae2365..97b067c686 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/_msvccompiler.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/_msvccompiler.py
@@ -159,7 +159,7 @@ def _get_vc_env(plat_spec):
stderr=subprocess.STDOUT,
).decode('utf-16le', errors='replace')
except subprocess.CalledProcessError as exc:
- log.error(exc.output) # noqa: RUF100, TRY400
+ log.error(exc.output)
raise DistutilsPlatformError(f"Error executing {exc.cmd}")
env = {
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py b/contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py
index cc4699b1a3..5bb6df763d 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/archive_util.py
@@ -4,8 +4,6 @@ Utility functions for creating archive files (tarballs, zip files,
that sort of thing)."""
import os
-import sys
-from warnings import warn
try:
import zipfile
@@ -67,8 +65,7 @@ def make_tarball(
"""Create a (possibly compressed) tar file from all the files under
'base_dir'.
- 'compress' must be "gzip" (the default), "bzip2", "xz", "compress", or
- None. ("compress" will be deprecated in Python 3.2)
+ 'compress' must be "gzip" (the default), "bzip2", "xz", or None.
'owner' and 'group' can be used to define an owner and a group for the
archive that is being built. If not provided, the current owner and group
@@ -84,20 +81,17 @@ def make_tarball(
'bzip2': 'bz2',
'xz': 'xz',
None: '',
- 'compress': '',
}
- compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz', 'compress': '.Z'}
+ compress_ext = {'gzip': '.gz', 'bzip2': '.bz2', 'xz': '.xz'}
# flags for compression program, each element of list will be an argument
if compress is not None and compress not in compress_ext.keys():
raise ValueError(
- "bad value for 'compress': must be None, 'gzip', 'bzip2', "
- "'xz' or 'compress'"
+ "bad value for 'compress': must be None, 'gzip', 'bzip2', 'xz'"
)
archive_name = base_name + '.tar'
- if compress != 'compress':
- archive_name += compress_ext.get(compress, '')
+ archive_name += compress_ext.get(compress, '')
mkpath(os.path.dirname(archive_name), dry_run=dry_run)
@@ -125,18 +119,6 @@ def make_tarball(
finally:
tar.close()
- # compression using `compress`
- if compress == 'compress':
- warn("'compress' is deprecated.", DeprecationWarning)
- # the option varies depending on the platform
- compressed_name = archive_name + compress_ext[compress]
- if sys.platform == 'win32':
- cmd = [compress, archive_name, compressed_name]
- else:
- cmd = [compress, '-f', archive_name]
- spawn(cmd, dry_run=dry_run)
- return compressed_name
-
return archive_name
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/bcppcompiler.py b/contrib/python/setuptools/py3/setuptools/_distutils/bcppcompiler.py
deleted file mode 100644
index 9157b43328..0000000000
--- a/contrib/python/setuptools/py3/setuptools/_distutils/bcppcompiler.py
+++ /dev/null
@@ -1,396 +0,0 @@
-"""distutils.bcppcompiler
-
-Contains BorlandCCompiler, an implementation of the abstract CCompiler class
-for the Borland C++ compiler.
-"""
-
-# This implementation by Lyle Johnson, based on the original msvccompiler.py
-# module and using the directions originally published by Gordon Williams.
-
-# XXX looks like there's a LOT of overlap between these two classes:
-# someone should sit down and factor out the common code as
-# WindowsCCompiler! --GPW
-
-import os
-import warnings
-
-from ._log import log
-from ._modified import newer
-from .ccompiler import CCompiler, gen_preprocess_options
-from .errors import (
- CompileError,
- DistutilsExecError,
- LibError,
- LinkError,
- UnknownFileError,
-)
-from .file_util import write_file
-
-warnings.warn(
- "bcppcompiler is deprecated and slated to be removed "
- "in the future. Please discontinue use or file an issue "
- "with pypa/distutils describing your use case.",
- DeprecationWarning,
-)
-
-
-class BCPPCompiler(CCompiler):
- """Concrete class that implements an interface to the Borland C/C++
- compiler, as defined by the CCompiler abstract class.
- """
-
- compiler_type = 'bcpp'
-
- # Just set this so CCompiler's constructor doesn't barf. We currently
- # don't use the 'set_executables()' bureaucracy provided by CCompiler,
- # as it really isn't necessary for this sort of single-compiler class.
- # Would be nice to have a consistent interface with UnixCCompiler,
- # though, so it's worth thinking about.
- executables = {}
-
- # Private class data (need to distinguish C from C++ source for compiler)
- _c_extensions = ['.c']
- _cpp_extensions = ['.cc', '.cpp', '.cxx']
-
- # Needed for the filename generation methods provided by the
- # base class, CCompiler.
- src_extensions = _c_extensions + _cpp_extensions
- obj_extension = '.obj'
- static_lib_extension = '.lib'
- shared_lib_extension = '.dll'
- static_lib_format = shared_lib_format = '%s%s'
- exe_extension = '.exe'
-
- def __init__(self, verbose=False, dry_run=False, force=False):
- super().__init__(verbose, dry_run, force)
-
- # These executables are assumed to all be in the path.
- # Borland doesn't seem to use any special registry settings to
- # indicate their installation locations.
-
- self.cc = "bcc32.exe"
- self.linker = "ilink32.exe"
- self.lib = "tlib.exe"
-
- self.preprocess_options = None
- self.compile_options = ['/tWM', '/O2', '/q', '/g0']
- self.compile_options_debug = ['/tWM', '/Od', '/q', '/g0']
-
- self.ldflags_shared = ['/Tpd', '/Gn', '/q', '/x']
- self.ldflags_shared_debug = ['/Tpd', '/Gn', '/q', '/x']
- self.ldflags_static = []
- self.ldflags_exe = ['/Gn', '/q', '/x']
- self.ldflags_exe_debug = ['/Gn', '/q', '/x', '/r']
-
- # -- Worker methods ------------------------------------------------
-
- def compile(
- self,
- sources,
- output_dir=None,
- macros=None,
- include_dirs=None,
- debug=False,
- extra_preargs=None,
- extra_postargs=None,
- depends=None,
- ):
- macros, objects, extra_postargs, pp_opts, build = self._setup_compile(
- output_dir, macros, include_dirs, sources, depends, extra_postargs
- )
- compile_opts = extra_preargs or []
- compile_opts.append('-c')
- if debug:
- compile_opts.extend(self.compile_options_debug)
- else:
- compile_opts.extend(self.compile_options)
-
- for obj in objects:
- try:
- src, ext = build[obj]
- except KeyError:
- continue
- # XXX why do the normpath here?
- src = os.path.normpath(src)
- obj = os.path.normpath(obj)
- # XXX _setup_compile() did a mkpath() too but before the normpath.
- # Is it possible to skip the normpath?
- self.mkpath(os.path.dirname(obj))
-
- if ext == '.res':
- # This is already a binary file -- skip it.
- continue # the 'for' loop
- if ext == '.rc':
- # This needs to be compiled to a .res file -- do it now.
- try:
- self.spawn(["brcc32", "-fo", obj, src])
- except DistutilsExecError as msg:
- raise CompileError(msg)
- continue # the 'for' loop
-
- # The next two are both for the real compiler.
- if ext in self._c_extensions:
- input_opt = ""
- elif ext in self._cpp_extensions:
- input_opt = "-P"
- else:
- # Unknown file type -- no extra options. The compiler
- # will probably fail, but let it just in case this is a
- # file the compiler recognizes even if we don't.
- input_opt = ""
-
- output_opt = "-o" + obj
-
- # Compiler command line syntax is: "bcc32 [options] file(s)".
- # Note that the source file names must appear at the end of
- # the command line.
- try:
- self.spawn(
- [self.cc]
- + compile_opts
- + pp_opts
- + [input_opt, output_opt]
- + extra_postargs
- + [src]
- )
- except DistutilsExecError as msg:
- raise CompileError(msg)
-
- return objects
-
- # compile ()
-
- def create_static_lib(
- self, objects, output_libname, output_dir=None, debug=False, target_lang=None
- ):
- (objects, output_dir) = self._fix_object_args(objects, output_dir)
- output_filename = self.library_filename(output_libname, output_dir=output_dir)
-
- if self._need_link(objects, output_filename):
- lib_args = [output_filename, '/u'] + objects
- if debug:
- pass # XXX what goes here?
- try:
- self.spawn([self.lib] + lib_args)
- except DistutilsExecError as msg:
- raise LibError(msg)
- else:
- log.debug("skipping %s (up-to-date)", output_filename)
-
- # create_static_lib ()
-
- def link( # noqa: C901
- self,
- target_desc,
- objects,
- output_filename,
- output_dir=None,
- libraries=None,
- library_dirs=None,
- runtime_library_dirs=None,
- export_symbols=None,
- debug=False,
- extra_preargs=None,
- extra_postargs=None,
- build_temp=None,
- target_lang=None,
- ):
- # XXX this ignores 'build_temp'! should follow the lead of
- # msvccompiler.py
-
- (objects, output_dir) = self._fix_object_args(objects, output_dir)
- (libraries, library_dirs, runtime_library_dirs) = self._fix_lib_args(
- libraries, library_dirs, runtime_library_dirs
- )
-
- if runtime_library_dirs:
- log.warning(
- "I don't know what to do with 'runtime_library_dirs': %s",
- str(runtime_library_dirs),
- )
-
- if output_dir is not None:
- output_filename = os.path.join(output_dir, output_filename)
-
- if self._need_link(objects, output_filename):
- # Figure out linker args based on type of target.
- if target_desc == CCompiler.EXECUTABLE:
- startup_obj = 'c0w32'
- if debug:
- ld_args = self.ldflags_exe_debug[:]
- else:
- ld_args = self.ldflags_exe[:]
- else:
- startup_obj = 'c0d32'
- if debug:
- ld_args = self.ldflags_shared_debug[:]
- else:
- ld_args = self.ldflags_shared[:]
-
- # Create a temporary exports file for use by the linker
- if export_symbols is None:
- def_file = ''
- else:
- head, tail = os.path.split(output_filename)
- modname, ext = os.path.splitext(tail)
- temp_dir = os.path.dirname(objects[0]) # preserve tree structure
- def_file = os.path.join(temp_dir, f'{modname}.def')
- contents = ['EXPORTS']
- contents.extend(f' {sym}=_{sym}' for sym in export_symbols)
- self.execute(write_file, (def_file, contents), f"writing {def_file}")
-
- # Borland C++ has problems with '/' in paths
- objects2 = map(os.path.normpath, objects)
- # split objects in .obj and .res files
- # Borland C++ needs them at different positions in the command line
- objects = [startup_obj]
- resources = []
- for file in objects2:
- (base, ext) = os.path.splitext(os.path.normcase(file))
- if ext == '.res':
- resources.append(file)
- else:
- objects.append(file)
-
- for ell in library_dirs:
- ld_args.append(f"/L{os.path.normpath(ell)}")
- ld_args.append("/L.") # we sometimes use relative paths
-
- # list of object files
- ld_args.extend(objects)
-
- # XXX the command-line syntax for Borland C++ is a bit wonky;
- # certain filenames are jammed together in one big string, but
- # comma-delimited. This doesn't mesh too well with the
- # Unix-centric attitude (with a DOS/Windows quoting hack) of
- # 'spawn()', so constructing the argument list is a bit
- # awkward. Note that doing the obvious thing and jamming all
- # the filenames and commas into one argument would be wrong,
- # because 'spawn()' would quote any filenames with spaces in
- # them. Arghghh!. Apparently it works fine as coded...
-
- # name of dll/exe file
- ld_args.extend([',', output_filename])
- # no map file and start libraries
- ld_args.append(',,')
-
- for lib in libraries:
- # see if we find it and if there is a bcpp specific lib
- # (xxx_bcpp.lib)
- libfile = self.find_library_file(library_dirs, lib, debug)
- if libfile is None:
- ld_args.append(lib)
- # probably a BCPP internal library -- don't warn
- else:
- # full name which prefers bcpp_xxx.lib over xxx.lib
- ld_args.append(libfile)
-
- # some default libraries
- ld_args.extend(('import32', 'cw32mt'))
-
- # def file for export symbols
- ld_args.extend([',', def_file])
- # add resource files
- ld_args.append(',')
- ld_args.extend(resources)
-
- if extra_preargs:
- ld_args[:0] = extra_preargs
- if extra_postargs:
- ld_args.extend(extra_postargs)
-
- self.mkpath(os.path.dirname(output_filename))
- try:
- self.spawn([self.linker] + ld_args)
- except DistutilsExecError as msg:
- raise LinkError(msg)
-
- else:
- log.debug("skipping %s (up-to-date)", output_filename)
-
- # link ()
-
- # -- Miscellaneous methods -----------------------------------------
-
- def find_library_file(self, dirs, lib, debug=False):
- # List of effective library names to try, in order of preference:
- # xxx_bcpp.lib is better than xxx.lib
- # and xxx_d.lib is better than xxx.lib if debug is set
- #
- # The "_bcpp" suffix is to handle a Python installation for people
- # with multiple compilers (primarily Distutils hackers, I suspect
- # ;-). The idea is they'd have one static library for each
- # compiler they care about, since (almost?) every Windows compiler
- # seems to have a different format for static libraries.
- if debug:
- dlib = lib + "_d"
- try_names = (dlib + "_bcpp", lib + "_bcpp", dlib, lib)
- else:
- try_names = (lib + "_bcpp", lib)
-
- for dir in dirs:
- for name in try_names:
- libfile = os.path.join(dir, self.library_filename(name))
- if os.path.exists(libfile):
- return libfile
- else:
- # Oops, didn't find it in *any* of 'dirs'
- return None
-
- # overwrite the one from CCompiler to support rc and res-files
- def object_filenames(self, source_filenames, strip_dir=False, output_dir=''):
- if output_dir is None:
- output_dir = ''
- obj_names = []
- for src_name in source_filenames:
- # use normcase to make sure '.rc' is really '.rc' and not '.RC'
- (base, ext) = os.path.splitext(os.path.normcase(src_name))
- if ext not in (self.src_extensions + ['.rc', '.res']):
- raise UnknownFileError(f"unknown file type '{ext}' (from '{src_name}')")
- if strip_dir:
- base = os.path.basename(base)
- if ext == '.res':
- # these can go unchanged
- obj_names.append(os.path.join(output_dir, base + ext))
- elif ext == '.rc':
- # these need to be compiled to .res-files
- obj_names.append(os.path.join(output_dir, base + '.res'))
- else:
- obj_names.append(os.path.join(output_dir, base + self.obj_extension))
- return obj_names
-
- # object_filenames ()
-
- def preprocess(
- self,
- source,
- output_file=None,
- macros=None,
- include_dirs=None,
- extra_preargs=None,
- extra_postargs=None,
- ):
- (_, macros, include_dirs) = self._fix_compile_args(None, macros, include_dirs)
- pp_opts = gen_preprocess_options(macros, include_dirs)
- pp_args = ['cpp32.exe'] + pp_opts
- if output_file is not None:
- pp_args.append('-o' + output_file)
- if extra_preargs:
- pp_args[:0] = extra_preargs
- if extra_postargs:
- pp_args.extend(extra_postargs)
- pp_args.append(source)
-
- # We need to preprocess: either we're being forced to, or the
- # source file is newer than the target (or the target doesn't
- # exist).
- if self.force or output_file is None or newer(source, output_file):
- if output_file:
- self.mkpath(os.path.dirname(output_file))
- try:
- self.spawn(pp_args)
- except DistutilsExecError as msg:
- print(msg)
- raise CompileError(msg)
-
- # preprocess()
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/ccompiler.py b/contrib/python/setuptools/py3/setuptools/_distutils/ccompiler.py
index bc4743bcbf..5e73e56d02 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/ccompiler.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/ccompiler.py
@@ -9,7 +9,8 @@ import sys
import types
import warnings
-from ._itertools import always_iterable
+from more_itertools import always_iterable
+
from ._log import log
from ._modified import newer_group
from .dir_util import mkpath
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/__init__.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/__init__.py
index 1e8fbe60c2..0f8a1692ba 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/__init__.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/__init__.py
@@ -16,10 +16,8 @@ __all__ = [
'install_scripts',
'install_data',
'sdist',
- 'register',
'bdist',
'bdist_dumb',
'bdist_rpm',
'check',
- 'upload',
]
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/install.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/install.py
index b83e061e02..ceb453e041 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/install.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/install.py
@@ -10,7 +10,8 @@ import sysconfig
from distutils._log import log
from site import USER_BASE, USER_SITE
-from .. import _collections
+import jaraco.collections
+
from ..core import Command
from ..debug import DEBUG
from ..errors import DistutilsOptionError, DistutilsPlatformError
@@ -428,7 +429,7 @@ class install(Command):
local_vars['userbase'] = self.install_userbase
local_vars['usersite'] = self.install_usersite
- self.config_vars = _collections.DictStack([
+ self.config_vars = jaraco.collections.DictStack([
fw.vars(),
compat_vars,
sysconfig.get_config_vars(),
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/register.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/register.py
deleted file mode 100644
index c1acd27b54..0000000000
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/register.py
+++ /dev/null
@@ -1,322 +0,0 @@
-"""distutils.command.register
-
-Implements the Distutils 'register' command (register with the repository).
-"""
-
-# created 2002/10/21, Richard Jones
-
-import getpass
-import io
-import logging
-import urllib.parse
-import urllib.request
-from distutils._log import log
-from warnings import warn
-
-from .._itertools import always_iterable
-from ..core import PyPIRCCommand
-
-
-class register(PyPIRCCommand):
- description = "register the distribution with the Python package index"
- user_options = PyPIRCCommand.user_options + [
- ('list-classifiers', None, 'list the valid Trove classifiers'),
- (
- 'strict',
- None,
- 'Will stop the registering if the meta-data are not fully compliant',
- ),
- ]
- boolean_options = PyPIRCCommand.boolean_options + [
- 'verify',
- 'list-classifiers',
- 'strict',
- ]
-
- sub_commands = [('check', lambda self: True)]
-
- def initialize_options(self):
- PyPIRCCommand.initialize_options(self)
- self.list_classifiers = False
- self.strict = False
-
- def finalize_options(self):
- PyPIRCCommand.finalize_options(self)
- # setting options for the `check` subcommand
- check_options = {
- 'strict': ('register', self.strict),
- 'restructuredtext': ('register', 1),
- }
- self.distribution.command_options['check'] = check_options
-
- def run(self):
- self.finalize_options()
- self._set_config()
-
- # Run sub commands
- for cmd_name in self.get_sub_commands():
- self.run_command(cmd_name)
-
- if self.dry_run:
- self.verify_metadata()
- elif self.list_classifiers:
- self.classifiers()
- else:
- self.send_metadata()
-
- def check_metadata(self):
- """Deprecated API."""
- warn(
- "distutils.command.register.check_metadata is deprecated; "
- "use the check command instead",
- DeprecationWarning,
- )
- check = self.distribution.get_command_obj('check')
- check.ensure_finalized()
- check.strict = self.strict
- check.restructuredtext = True
- check.run()
-
- def _set_config(self):
- """Reads the configuration file and set attributes."""
- config = self._read_pypirc()
- if config != {}:
- self.username = config['username']
- self.password = config['password']
- self.repository = config['repository']
- self.realm = config['realm']
- self.has_config = True
- else:
- if self.repository not in ('pypi', self.DEFAULT_REPOSITORY):
- raise ValueError(f'{self.repository} not found in .pypirc')
- if self.repository == 'pypi':
- self.repository = self.DEFAULT_REPOSITORY
- self.has_config = False
-
- def classifiers(self):
- """Fetch the list of classifiers from the server."""
- url = self.repository + '?:action=list_classifiers'
- response = urllib.request.urlopen(url)
- log.info(self._read_pypi_response(response))
-
- def verify_metadata(self):
- """Send the metadata to the package index server to be checked."""
- # send the info to the server and report the result
- (code, result) = self.post_to_server(self.build_post_data('verify'))
- log.info('Server response (%s): %s', code, result)
-
- def send_metadata(self): # noqa: C901
- """Send the metadata to the package index server.
-
- Well, do the following:
- 1. figure who the user is, and then
- 2. send the data as a Basic auth'ed POST.
-
- First we try to read the username/password from $HOME/.pypirc,
- which is a ConfigParser-formatted file with a section
- [distutils] containing username and password entries (both
- in clear text). Eg:
-
- [distutils]
- index-servers =
- pypi
-
- [pypi]
- username: fred
- password: sekrit
-
- Otherwise, to figure who the user is, we offer the user three
- choices:
-
- 1. use existing login,
- 2. register as a new user, or
- 3. set the password to a random string and email the user.
-
- """
- # see if we can short-cut and get the username/password from the
- # config
- if self.has_config:
- choice = '1'
- username = self.username
- password = self.password
- else:
- choice = 'x'
- username = password = ''
-
- # get the user's login info
- choices = '1 2 3 4'.split()
- while choice not in choices:
- self.announce(
- """\
-We need to know who you are, so please choose either:
- 1. use your existing login,
- 2. register as a new user,
- 3. have the server generate a new password for you (and email it to you), or
- 4. quit
-Your selection [default 1]: """,
- logging.INFO,
- )
- choice = input()
- if not choice:
- choice = '1'
- elif choice not in choices:
- print('Please choose one of the four options!')
-
- if choice == '1':
- # get the username and password
- while not username:
- username = input('Username: ')
- while not password:
- password = getpass.getpass('Password: ')
-
- # set up the authentication
- auth = urllib.request.HTTPPasswordMgr()
- host = urllib.parse.urlparse(self.repository)[1]
- auth.add_password(self.realm, host, username, password)
- # send the info to the server and report the result
- code, result = self.post_to_server(self.build_post_data('submit'), auth)
- self.announce(f'Server response ({code}): {result}', logging.INFO)
-
- # possibly save the login
- if code == 200:
- if self.has_config:
- # sharing the password in the distribution instance
- # so the upload command can reuse it
- self.distribution.password = password
- else:
- self.announce(
- (
- 'I can store your PyPI login so future '
- 'submissions will be faster.'
- ),
- logging.INFO,
- )
- self.announce(
- f'(the login will be stored in {self._get_rc_file()})',
- logging.INFO,
- )
- choice = 'X'
- while choice.lower() not in 'yn':
- choice = input('Save your login (y/N)?')
- if not choice:
- choice = 'n'
- if choice.lower() == 'y':
- self._store_pypirc(username, password)
-
- elif choice == '2':
- data = {':action': 'user'}
- data['name'] = data['password'] = data['email'] = ''
- data['confirm'] = None
- while not data['name']:
- data['name'] = input('Username: ')
- while data['password'] != data['confirm']:
- while not data['password']:
- data['password'] = getpass.getpass('Password: ')
- while not data['confirm']:
- data['confirm'] = getpass.getpass(' Confirm: ')
- if data['password'] != data['confirm']:
- data['password'] = ''
- data['confirm'] = None
- print("Password and confirm don't match!")
- while not data['email']:
- data['email'] = input(' EMail: ')
- code, result = self.post_to_server(data)
- if code != 200:
- log.info('Server response (%s): %s', code, result)
- else:
- log.info('You will receive an email shortly.')
- log.info('Follow the instructions in it to complete registration.')
- elif choice == '3':
- data = {':action': 'password_reset'}
- data['email'] = ''
- while not data['email']:
- data['email'] = input('Your email address: ')
- code, result = self.post_to_server(data)
- log.info('Server response (%s): %s', code, result)
-
- def build_post_data(self, action):
- # figure the data to send - the metadata plus some additional
- # information used by the package server
- meta = self.distribution.metadata
- data = {
- ':action': action,
- 'metadata_version': '1.0',
- 'name': meta.get_name(),
- 'version': meta.get_version(),
- 'summary': meta.get_description(),
- 'home_page': meta.get_url(),
- 'author': meta.get_contact(),
- 'author_email': meta.get_contact_email(),
- 'license': meta.get_licence(),
- 'description': meta.get_long_description(),
- 'keywords': meta.get_keywords(),
- 'platform': meta.get_platforms(),
- 'classifiers': meta.get_classifiers(),
- 'download_url': meta.get_download_url(),
- # PEP 314
- 'provides': meta.get_provides(),
- 'requires': meta.get_requires(),
- 'obsoletes': meta.get_obsoletes(),
- }
- if data['provides'] or data['requires'] or data['obsoletes']:
- data['metadata_version'] = '1.1'
- return data
-
- def post_to_server(self, data, auth=None): # noqa: C901
- """Post a query to the server, and return a string response."""
- if 'name' in data:
- self.announce(
- 'Registering {} to {}'.format(data['name'], self.repository),
- logging.INFO,
- )
- # Build up the MIME payload for the urllib2 POST data
- boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
- sep_boundary = '\n--' + boundary
- end_boundary = sep_boundary + '--'
- body = io.StringIO()
- for key, values in data.items():
- for value in map(str, make_iterable(values)):
- body.write(sep_boundary)
- body.write(f'\nContent-Disposition: form-data; name="{key}"')
- body.write("\n\n")
- body.write(value)
- if value and value[-1] == '\r':
- body.write('\n') # write an extra newline (lurve Macs)
- body.write(end_boundary)
- body.write("\n")
- body = body.getvalue().encode("utf-8")
-
- # build the Request
- headers = {
- 'Content-type': f'multipart/form-data; boundary={boundary}; charset=utf-8',
- 'Content-length': str(len(body)),
- }
- req = urllib.request.Request(self.repository, body, headers)
-
- # handle HTTP and include the Basic Auth handler
- opener = urllib.request.build_opener(
- urllib.request.HTTPBasicAuthHandler(password_mgr=auth)
- )
- data = ''
- try:
- result = opener.open(req)
- except urllib.error.HTTPError as e:
- if self.show_response:
- data = e.fp.read()
- result = e.code, e.msg
- except urllib.error.URLError as e:
- result = 500, str(e)
- else:
- if self.show_response:
- data = self._read_pypi_response(result)
- result = 200, 'OK'
- if self.show_response:
- msg = '\n'.join(('-' * 75, data, '-' * 75))
- self.announce(msg, logging.INFO)
- return result
-
-
-def make_iterable(values):
- if values is None:
- return [None]
- return always_iterable(values)
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py
index eda6afe811..d723a1c9fb 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/command/sdist.py
@@ -8,7 +8,6 @@ from distutils import archive_util, dir_util, file_util
from distutils._log import log
from glob import glob
from itertools import filterfalse
-from warnings import warn
from ..core import Command
from ..errors import DistutilsOptionError, DistutilsTemplateError
@@ -177,17 +176,6 @@ class sdist(Command):
# or zipfile, or whatever.
self.make_distribution()
- def check_metadata(self):
- """Deprecated API."""
- warn(
- "distutils.command.sdist.check_metadata is deprecated, \
- use the check command instead",
- PendingDeprecationWarning,
- )
- check = self.distribution.get_command_obj('check')
- check.ensure_finalized()
- check.run()
-
def get_file_list(self):
"""Figure out the list of files to include in the source
distribution, and put it in 'self.filelist'. This might involve
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/command/upload.py b/contrib/python/setuptools/py3/setuptools/_distutils/command/upload.py
deleted file mode 100644
index a2461e089f..0000000000
--- a/contrib/python/setuptools/py3/setuptools/_distutils/command/upload.py
+++ /dev/null
@@ -1,208 +0,0 @@
-"""
-distutils.command.upload
-
-Implements the Distutils 'upload' subcommand (upload package to a package
-index).
-"""
-
-import hashlib
-import io
-import logging
-import os
-from base64 import standard_b64encode
-from urllib.parse import urlparse
-from urllib.request import HTTPError, Request, urlopen
-
-from .._itertools import always_iterable
-from ..core import PyPIRCCommand
-from ..errors import DistutilsError, DistutilsOptionError
-from ..spawn import spawn
-
-# PyPI Warehouse supports MD5, SHA256, and Blake2 (blake2-256)
-# https://bugs.python.org/issue40698
-_FILE_CONTENT_DIGESTS = {
- "md5_digest": getattr(hashlib, "md5", None),
- "sha256_digest": getattr(hashlib, "sha256", None),
- "blake2_256_digest": getattr(hashlib, "blake2b", None),
-}
-
-
-class upload(PyPIRCCommand):
- description = "upload binary package to PyPI"
-
- user_options = PyPIRCCommand.user_options + [
- ('sign', 's', 'sign files to upload using gpg'),
- ('identity=', 'i', 'GPG identity used to sign files'),
- ]
-
- boolean_options = PyPIRCCommand.boolean_options + ['sign']
-
- def initialize_options(self):
- PyPIRCCommand.initialize_options(self)
- self.username = ''
- self.password = ''
- self.show_response = False
- self.sign = False
- self.identity = None
-
- def finalize_options(self):
- PyPIRCCommand.finalize_options(self)
- if self.identity and not self.sign:
- raise DistutilsOptionError("Must use --sign for --identity to have meaning")
- config = self._read_pypirc()
- if config != {}:
- self.username = config['username']
- self.password = config['password']
- self.repository = config['repository']
- self.realm = config['realm']
-
- # getting the password from the distribution
- # if previously set by the register command
- if not self.password and self.distribution.password:
- self.password = self.distribution.password
-
- def run(self):
- if not self.distribution.dist_files:
- msg = (
- "Must create and upload files in one command "
- "(e.g. setup.py sdist upload)"
- )
- raise DistutilsOptionError(msg)
- for command, pyversion, filename in self.distribution.dist_files:
- self.upload_file(command, pyversion, filename)
-
- def upload_file(self, command, pyversion, filename): # noqa: C901
- # Makes sure the repository URL is compliant
- schema, netloc, url, params, query, fragments = urlparse(self.repository)
- if params or query or fragments:
- raise AssertionError(f"Incompatible url {self.repository}")
-
- if schema not in ('http', 'https'):
- raise AssertionError("unsupported schema " + schema)
-
- # Sign if requested
- if self.sign:
- gpg_args = ["gpg", "--detach-sign", "-a", filename]
- if self.identity:
- gpg_args[2:2] = ["--local-user", self.identity]
- spawn(gpg_args, dry_run=self.dry_run)
-
- # Fill in the data - send all the meta-data in case we need to
- # register a new release
- f = open(filename, 'rb')
- try:
- content = f.read()
- finally:
- f.close()
-
- meta = self.distribution.metadata
- data = {
- # action
- ':action': 'file_upload',
- 'protocol_version': '1',
- # identify release
- 'name': meta.get_name(),
- 'version': meta.get_version(),
- # file content
- 'content': (os.path.basename(filename), content),
- 'filetype': command,
- 'pyversion': pyversion,
- # additional meta-data
- 'metadata_version': '1.0',
- 'summary': meta.get_description(),
- 'home_page': meta.get_url(),
- 'author': meta.get_contact(),
- 'author_email': meta.get_contact_email(),
- 'license': meta.get_licence(),
- 'description': meta.get_long_description(),
- 'keywords': meta.get_keywords(),
- 'platform': meta.get_platforms(),
- 'classifiers': meta.get_classifiers(),
- 'download_url': meta.get_download_url(),
- # PEP 314
- 'provides': meta.get_provides(),
- 'requires': meta.get_requires(),
- 'obsoletes': meta.get_obsoletes(),
- }
-
- data['comment'] = ''
-
- # file content digests
- for digest_name, digest_cons in _FILE_CONTENT_DIGESTS.items():
- if digest_cons is None:
- continue
- try:
- data[digest_name] = digest_cons(content).hexdigest()
- except ValueError:
- # hash digest not available or blocked by security policy
- pass
-
- if self.sign:
- with open(filename + ".asc", "rb") as f:
- data['gpg_signature'] = (os.path.basename(filename) + ".asc", f.read())
-
- # set up the authentication
- user_pass = (self.username + ":" + self.password).encode('ascii')
- # The exact encoding of the authentication string is debated.
- # Anyway PyPI only accepts ascii for both username or password.
- auth = "Basic " + standard_b64encode(user_pass).decode('ascii')
-
- # Build up the MIME payload for the POST data
- boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
- sep_boundary = b'\r\n--' + boundary.encode('ascii')
- end_boundary = sep_boundary + b'--\r\n'
- body = io.BytesIO()
- for key, values in data.items():
- title = f'\r\nContent-Disposition: form-data; name="{key}"'
- for value in make_iterable(values):
- if type(value) is tuple:
- title += f'; filename="{value[0]}"'
- value = value[1]
- else:
- value = str(value).encode('utf-8')
- body.write(sep_boundary)
- body.write(title.encode('utf-8'))
- body.write(b"\r\n\r\n")
- body.write(value)
- body.write(end_boundary)
- body = body.getvalue()
-
- msg = f"Submitting {filename} to {self.repository}"
- self.announce(msg, logging.INFO)
-
- # build the Request
- headers = {
- 'Content-type': f'multipart/form-data; boundary={boundary}',
- 'Content-length': str(len(body)),
- 'Authorization': auth,
- }
-
- request = Request(self.repository, data=body, headers=headers)
- # send the data
- try:
- result = urlopen(request)
- status = result.getcode()
- reason = result.msg
- except HTTPError as e:
- status = e.code
- reason = e.msg
- except OSError as e:
- self.announce(str(e), logging.ERROR)
- raise
-
- if status == 200:
- self.announce(f'Server response ({status}): {reason}', logging.INFO)
- if self.show_response:
- text = self._read_pypi_response(result)
- msg = '\n'.join(('-' * 75, text, '-' * 75))
- self.announce(msg, logging.INFO)
- else:
- msg = f'Upload failed ({status}): {reason}'
- self.announce(msg, logging.ERROR)
- raise DistutilsError(msg)
-
-
-def make_iterable(values):
- if values is None:
- return [None]
- return always_iterable(values, base_type=(bytes, str, tuple))
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/compat/py38.py b/contrib/python/setuptools/py3/setuptools/_distutils/compat/py38.py
index 03ec73ef0e..afe5345553 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/compat/py38.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/compat/py38.py
@@ -26,7 +26,7 @@ else:
def aix_platform(osname, version, release):
try:
- import _aix_support # type: ignore
+ import _aix_support
return _aix_support.aix_platform()
except ImportError:
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/config.py b/contrib/python/setuptools/py3/setuptools/_distutils/config.py
deleted file mode 100644
index ebd2e11da3..0000000000
--- a/contrib/python/setuptools/py3/setuptools/_distutils/config.py
+++ /dev/null
@@ -1,151 +0,0 @@
-"""distutils.pypirc
-
-Provides the PyPIRCCommand class, the base class for the command classes
-that uses .pypirc in the distutils.command package.
-"""
-
-import email.message
-import os
-from configparser import RawConfigParser
-
-from .cmd import Command
-
-DEFAULT_PYPIRC = """\
-[distutils]
-index-servers =
- pypi
-
-[pypi]
-username:%s
-password:%s
-"""
-
-
-class PyPIRCCommand(Command):
- """Base command that knows how to handle the .pypirc file"""
-
- DEFAULT_REPOSITORY = 'https://upload.pypi.org/legacy/'
- DEFAULT_REALM = 'pypi'
- repository = None
- realm = None
-
- user_options = [
- ('repository=', 'r', f"url of repository [default: {DEFAULT_REPOSITORY}]"),
- ('show-response', None, 'display full response text from server'),
- ]
-
- boolean_options = ['show-response']
-
- def _get_rc_file(self):
- """Returns rc file path."""
- return os.path.join(os.path.expanduser('~'), '.pypirc')
-
- def _store_pypirc(self, username, password):
- """Creates a default .pypirc file."""
- rc = self._get_rc_file()
- raw = os.open(rc, os.O_CREAT | os.O_WRONLY, 0o600)
- with os.fdopen(raw, 'w', encoding='utf-8') as f:
- f.write(DEFAULT_PYPIRC % (username, password))
-
- def _read_pypirc(self): # noqa: C901
- """Reads the .pypirc file."""
- rc = self._get_rc_file()
- if os.path.exists(rc):
- self.announce(f'Using PyPI login from {rc}')
- repository = self.repository or self.DEFAULT_REPOSITORY
-
- config = RawConfigParser()
- config.read(rc, encoding='utf-8')
- sections = config.sections()
- if 'distutils' in sections:
- # let's get the list of servers
- index_servers = config.get('distutils', 'index-servers')
- _servers = [
- server.strip()
- for server in index_servers.split('\n')
- if server.strip() != ''
- ]
- if _servers == []:
- # nothing set, let's try to get the default pypi
- if 'pypi' in sections:
- _servers = ['pypi']
- else:
- # the file is not properly defined, returning
- # an empty dict
- return {}
- for server in _servers:
- current = {'server': server}
- current['username'] = config.get(server, 'username')
-
- # optional params
- for key, default in (
- ('repository', self.DEFAULT_REPOSITORY),
- ('realm', self.DEFAULT_REALM),
- ('password', None),
- ):
- if config.has_option(server, key):
- current[key] = config.get(server, key)
- else:
- current[key] = default
-
- # work around people having "repository" for the "pypi"
- # section of their config set to the HTTP (rather than
- # HTTPS) URL
- if server == 'pypi' and repository in (
- self.DEFAULT_REPOSITORY,
- 'pypi',
- ):
- current['repository'] = self.DEFAULT_REPOSITORY
- return current
-
- if (
- current['server'] == repository
- or current['repository'] == repository
- ):
- return current
- elif 'server-login' in sections:
- # old format
- server = 'server-login'
- if config.has_option(server, 'repository'):
- repository = config.get(server, 'repository')
- else:
- repository = self.DEFAULT_REPOSITORY
- return {
- 'username': config.get(server, 'username'),
- 'password': config.get(server, 'password'),
- 'repository': repository,
- 'server': server,
- 'realm': self.DEFAULT_REALM,
- }
-
- return {}
-
- def _read_pypi_response(self, response):
- """Read and decode a PyPI HTTP response."""
- content_type = response.getheader('content-type', 'text/plain')
- return response.read().decode(_extract_encoding(content_type))
-
- def initialize_options(self):
- """Initialize options."""
- self.repository = None
- self.realm = None
- self.show_response = False
-
- def finalize_options(self):
- """Finalizes options."""
- if self.repository is None:
- self.repository = self.DEFAULT_REPOSITORY
- if self.realm is None:
- self.realm = self.DEFAULT_REALM
-
-
-def _extract_encoding(content_type):
- """
- >>> _extract_encoding('text/plain')
- 'ascii'
- >>> _extract_encoding('text/html; charset="utf8"')
- 'utf8'
- """
- msg = email.message.EmailMessage()
- msg['content-type'] = content_type
- return msg['content-type'].params.get('charset', 'ascii')
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/core.py b/contrib/python/setuptools/py3/setuptools/_distutils/core.py
index 82113c47c1..bc06091abb 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/core.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/core.py
@@ -11,7 +11,6 @@ import sys
import tokenize
from .cmd import Command
-from .config import PyPIRCCommand
from .debug import DEBUG
# Mainly import these so setup scripts can "from distutils.core import" them.
@@ -24,7 +23,7 @@ from .errors import (
)
from .extension import Extension
-__all__ = ['Distribution', 'Command', 'PyPIRCCommand', 'Extension', 'setup']
+__all__ = ['Distribution', 'Command', 'Extension', 'setup']
# This is a barebones help message generated displayed when the user
# runs the setup script with no arguments at all. More useful help
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/cygwinccompiler.py b/contrib/python/setuptools/py3/setuptools/_distutils/cygwinccompiler.py
index 18b1b3557b..3c67524e6d 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/cygwinccompiler.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/cygwinccompiler.py
@@ -21,6 +21,7 @@ from .errors import (
DistutilsPlatformError,
)
from .file_util import write_file
+from .sysconfig import get_config_vars
from .unixccompiler import UnixCCompiler
from .version import LooseVersion, suppress_known_deprecation
@@ -61,8 +62,12 @@ class CygwinCCompiler(UnixCCompiler):
"Compiling may fail because of undefined preprocessor macros."
)
- self.cc = os.environ.get('CC', 'gcc')
- self.cxx = os.environ.get('CXX', 'g++')
+ self.cc, self.cxx = get_config_vars('CC', 'CXX')
+
+ # Override 'CC' and 'CXX' environment variables for
+ # building using MINGW compiler for MSVC python.
+ self.cc = os.environ.get('CC', self.cc or 'gcc')
+ self.cxx = os.environ.get('CXX', self.cxx or 'g++')
self.linker_dll = self.cc
self.linker_dll_cxx = self.cxx
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/dir_util.py b/contrib/python/setuptools/py3/setuptools/_distutils/dir_util.py
index 724afeff6f..d9782602cf 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/dir_util.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/dir_util.py
@@ -2,85 +2,78 @@
Utility functions for manipulating directories and directory trees."""
-import errno
+import functools
+import itertools
import os
+import pathlib
+from . import file_util
from ._log import log
from .errors import DistutilsFileError, DistutilsInternalError
-# cache for by mkpath() -- in addition to cheapening redundant calls,
-# eliminates redundant "creating /foo/bar/baz" messages in dry-run mode
-_path_created = set()
+class SkipRepeatAbsolutePaths(set):
+ """
+ Cache for mkpath.
-def mkpath(name, mode=0o777, verbose=True, dry_run=False): # noqa: C901
- """Create a directory and any missing ancestor directories.
+ In addition to cheapening redundant calls, eliminates redundant
+ "creating /foo/bar/baz" messages in dry-run mode.
+ """
- If the directory already exists (or if 'name' is the empty string, which
- means the current directory, which of course exists), then do nothing.
- Raise DistutilsFileError if unable to create some directory along the way
- (eg. some sub-path exists, but is a file rather than a directory).
- If 'verbose' is true, print a one-line summary of each mkdir to stdout.
- Return the list of directories actually created.
+ def __init__(self):
+ SkipRepeatAbsolutePaths.instance = self
- os.makedirs is not used because:
+ @classmethod
+ def clear(cls):
+ super(cls, cls.instance).clear()
- a) It's new to Python 1.5.2, and
- b) it blows up if the directory already exists (in which case it should
- silently succeed).
- """
+ def wrap(self, func):
+ @functools.wraps(func)
+ def wrapper(path, *args, **kwargs):
+ if path.absolute() in self:
+ return
+ result = func(path, *args, **kwargs)
+ self.add(path.absolute())
+ return result
- global _path_created
+ return wrapper
- # Detect a common bug -- name is None
- if not isinstance(name, str):
- raise DistutilsInternalError(f"mkpath: 'name' must be a string (got {name!r})")
- # XXX what's the better way to handle verbosity? print as we create
- # each directory in the path (the current behaviour), or only announce
- # the creation of the whole path? (quite easy to do the latter since
- # we're not using a recursive algorithm)
+# Python 3.8 compatibility
+wrapper = SkipRepeatAbsolutePaths().wrap
- name = os.path.normpath(name)
- created_dirs = []
- if os.path.isdir(name) or name == '':
- return created_dirs
- if os.path.abspath(name) in _path_created:
- return created_dirs
- (head, tail) = os.path.split(name)
- tails = [tail] # stack of lone dirs to create
+@functools.singledispatch
+@wrapper
+def mkpath(name: pathlib.Path, mode=0o777, verbose=True, dry_run=False) -> None:
+ """Create a directory and any missing ancestor directories.
- while head and tail and not os.path.isdir(head):
- (head, tail) = os.path.split(head)
- tails.insert(0, tail) # push next higher dir onto stack
+ If the directory already exists (or if 'name' is the empty string, which
+ means the current directory, which of course exists), then do nothing.
+ Raise DistutilsFileError if unable to create some directory along the way
+ (eg. some sub-path exists, but is a file rather than a directory).
+ If 'verbose' is true, log the directory created.
+ """
+ if verbose and not name.is_dir():
+ log.info("creating %s", name)
- # now 'head' contains the deepest directory that already exists
- # (that is, the child of 'head' in 'name' is the highest directory
- # that does *not* exist)
- for d in tails:
- # print "head = %s, d = %s: " % (head, d),
- head = os.path.join(head, d)
- abs_head = os.path.abspath(head)
+ try:
+ dry_run or name.mkdir(mode=mode, parents=True, exist_ok=True)
+ except OSError as exc:
+ raise DistutilsFileError(f"could not create '{name}': {exc.args[-1]}")
- if abs_head in _path_created:
- continue
- if verbose >= 1:
- log.info("creating %s", head)
+@mkpath.register
+def _(name: str, *args, **kwargs):
+ return mkpath(pathlib.Path(name), *args, **kwargs)
- if not dry_run:
- try:
- os.mkdir(head, mode)
- except OSError as exc:
- if not (exc.errno == errno.EEXIST and os.path.isdir(head)):
- raise DistutilsFileError(
- f"could not create '{head}': {exc.args[-1]}"
- )
- created_dirs.append(head)
- _path_created.add(abs_head)
- return created_dirs
+@mkpath.register
+def _(name: None, *args, **kwargs):
+ """
+ Detect a common bug -- name is None.
+ """
+ raise DistutilsInternalError(f"mkpath: 'name' must be a string (got {name!r})")
def create_tree(base_dir, files, mode=0o777, verbose=True, dry_run=False):
@@ -101,7 +94,7 @@ def create_tree(base_dir, files, mode=0o777, verbose=True, dry_run=False):
mkpath(dir, mode, verbose=verbose, dry_run=dry_run)
-def copy_tree( # noqa: C901
+def copy_tree(
src,
dst,
preserve_mode=True,
@@ -130,8 +123,6 @@ def copy_tree( # noqa: C901
(the default), the destination of the symlink will be copied.
'update' and 'verbose' are the same as for 'copy_file'.
"""
- from distutils.file_util import copy_file
-
if not dry_run and not os.path.isdir(src):
raise DistutilsFileError(f"cannot copy tree '{src}': not a directory")
try:
@@ -145,50 +136,69 @@ def copy_tree( # noqa: C901
if not dry_run:
mkpath(dst, verbose=verbose)
- outputs = []
-
- for n in names:
- src_name = os.path.join(src, n)
- dst_name = os.path.join(dst, n)
-
- if n.startswith('.nfs'):
- # skip NFS rename files
- continue
-
- if preserve_symlinks and os.path.islink(src_name):
- link_dest = os.readlink(src_name)
- if verbose >= 1:
- log.info("linking %s -> %s", dst_name, link_dest)
- if not dry_run:
- os.symlink(link_dest, dst_name)
- outputs.append(dst_name)
-
- elif os.path.isdir(src_name):
- outputs.extend(
- copy_tree(
- src_name,
- dst_name,
- preserve_mode,
- preserve_times,
- preserve_symlinks,
- update,
- verbose=verbose,
- dry_run=dry_run,
- )
- )
- else:
- copy_file(
- src_name,
- dst_name,
- preserve_mode,
- preserve_times,
- update,
- verbose=verbose,
- dry_run=dry_run,
- )
- outputs.append(dst_name)
+ copy_one = functools.partial(
+ _copy_one,
+ src=src,
+ dst=dst,
+ preserve_symlinks=preserve_symlinks,
+ verbose=verbose,
+ dry_run=dry_run,
+ preserve_mode=preserve_mode,
+ preserve_times=preserve_times,
+ update=update,
+ )
+ return list(itertools.chain.from_iterable(map(copy_one, names)))
+
+
+def _copy_one(
+ name,
+ *,
+ src,
+ dst,
+ preserve_symlinks,
+ verbose,
+ dry_run,
+ preserve_mode,
+ preserve_times,
+ update,
+):
+ src_name = os.path.join(src, name)
+ dst_name = os.path.join(dst, name)
- return outputs
+ if name.startswith('.nfs'):
+ # skip NFS rename files
+ return
+
+ if preserve_symlinks and os.path.islink(src_name):
+ link_dest = os.readlink(src_name)
+ if verbose >= 1:
+ log.info("linking %s -> %s", dst_name, link_dest)
+ if not dry_run:
+ os.symlink(link_dest, dst_name)
+ yield dst_name
+
+ elif os.path.isdir(src_name):
+ yield from copy_tree(
+ src_name,
+ dst_name,
+ preserve_mode,
+ preserve_times,
+ preserve_symlinks,
+ update,
+ verbose=verbose,
+ dry_run=dry_run,
+ )
+ else:
+ file_util.copy_file(
+ src_name,
+ dst_name,
+ preserve_mode,
+ preserve_times,
+ update,
+ verbose=verbose,
+ dry_run=dry_run,
+ )
+ yield dst_name
def _build_cmdtuple(path, cmdtuples):
@@ -208,8 +218,6 @@ def remove_tree(directory, verbose=True, dry_run=False):
Any errors are ignored (apart from being reported to stdout if 'verbose'
is true).
"""
- global _path_created
-
if verbose >= 1:
log.info("removing '%s' (and everything under it)", directory)
if dry_run:
@@ -219,10 +227,8 @@ def remove_tree(directory, verbose=True, dry_run=False):
for cmd in cmdtuples:
try:
cmd[0](cmd[1])
- # remove dir from cache if it's already there
- abspath = os.path.abspath(cmd[1])
- if abspath in _path_created:
- _path_created.remove(abspath)
+ # Clear the cache
+ SkipRepeatAbsolutePaths.clear()
except OSError as exc:
log.warning("error removing %s: %s", directory, exc)
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/errors.py b/contrib/python/setuptools/py3/setuptools/_distutils/errors.py
index 626254c321..3196a4f097 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/errors.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/errors.py
@@ -1,12 +1,9 @@
-"""distutils.errors
+"""
+Exceptions used by the Distutils modules.
-Provides exceptions used by the Distutils modules. Note that Distutils
-modules may raise standard exceptions; in particular, SystemExit is
-usually raised for errors that are obviously the end-user's fault
-(eg. bad command-line arguments).
-
-This module is safe to use in "from ... import *" mode; it only exports
-symbols whose names start with "Distutils" and end with "Error"."""
+Distutils modules may raise these or standard exceptions,
+including :exc:`SystemExit`.
+"""
class DistutilsError(Exception):
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/sysconfig.py b/contrib/python/setuptools/py3/setuptools/_distutils/sysconfig.py
index 28a7c571dc..da1eecbe7e 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/sysconfig.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/sysconfig.py
@@ -16,7 +16,8 @@ import re
import sys
import sysconfig
-from ._functools import pass_none
+from jaraco.functools import pass_none
+
from .compat import py39
from .errors import DistutilsPlatformError
from .util import is_mingw
diff --git a/contrib/python/setuptools/py3/setuptools/_distutils/util.py b/contrib/python/setuptools/py3/setuptools/_distutils/util.py
index 4cc6bd283c..609c1a50cd 100644
--- a/contrib/python/setuptools/py3/setuptools/_distutils/util.py
+++ b/contrib/python/setuptools/py3/setuptools/_distutils/util.py
@@ -17,7 +17,8 @@ import sys
import sysconfig
import tempfile
-from ._functools import pass_none
+from jaraco.functools import pass_none
+
from ._log import log
from ._modified import newer
from .errors import DistutilsByteCompileError, DistutilsPlatformError
diff --git a/contrib/python/setuptools/py3/setuptools/_importlib.py b/contrib/python/setuptools/py3/setuptools/_importlib.py
index 5317be0fa0..ce0fd52653 100644
--- a/contrib/python/setuptools/py3/setuptools/_importlib.py
+++ b/contrib/python/setuptools/py3/setuptools/_importlib.py
@@ -6,7 +6,4 @@ else:
import importlib.metadata as metadata # noqa: F401
-if sys.version_info < (3, 9):
- import importlib_resources as resources # pragma: no cover
-else:
- import importlib.resources as resources # noqa: F401
+import importlib.resources as resources # noqa: F401
diff --git a/contrib/python/setuptools/py3/setuptools/_path.py b/contrib/python/setuptools/py3/setuptools/_path.py
index dd4a9db8cb..0d99b0f539 100644
--- a/contrib/python/setuptools/py3/setuptools/_path.py
+++ b/contrib/python/setuptools/py3/setuptools/_path.py
@@ -3,18 +3,15 @@ from __future__ import annotations
import contextlib
import os
import sys
-from typing import TYPE_CHECKING, Union
+from typing import TYPE_CHECKING, TypeVar, Union
+
+from more_itertools import unique_everseen
if TYPE_CHECKING:
from typing_extensions import TypeAlias
-
-from more_itertools import unique_everseen
-
-if sys.version_info >= (3, 9):
- StrPath: TypeAlias = Union[str, os.PathLike[str]] # Same as _typeshed.StrPath
-else:
- StrPath: TypeAlias = Union[str, os.PathLike]
+StrPath: TypeAlias = Union[str, os.PathLike[str]] # Same as _typeshed.StrPath
+StrPathT = TypeVar("StrPathT", bound=Union[str, os.PathLike[str]])
def ensure_directory(path):
diff --git a/contrib/python/setuptools/py3/setuptools/_reqs.py b/contrib/python/setuptools/py3/setuptools/_reqs.py
index 71ea23dea9..c793be4d6e 100644
--- a/contrib/python/setuptools/py3/setuptools/_reqs.py
+++ b/contrib/python/setuptools/py3/setuptools/_reqs.py
@@ -1,7 +1,8 @@
from __future__ import annotations
+from collections.abc import Iterable, Iterator
from functools import lru_cache
-from typing import TYPE_CHECKING, Callable, Iterable, Iterator, TypeVar, Union, overload
+from typing import TYPE_CHECKING, Callable, TypeVar, Union, overload
import jaraco.text as text
from packaging.requirements import Requirement
diff --git a/contrib/python/setuptools/py3/setuptools/_shutil.py b/contrib/python/setuptools/py3/setuptools/_shutil.py
new file mode 100644
index 0000000000..6acbb4281f
--- /dev/null
+++ b/contrib/python/setuptools/py3/setuptools/_shutil.py
@@ -0,0 +1,53 @@
+"""Convenience layer on top of stdlib's shutil and os"""
+
+import os
+import stat
+from typing import Callable, TypeVar
+
+from .compat import py311
+
+from distutils import log
+
+try:
+ from os import chmod # pyright: ignore[reportAssignmentType]
+ # Losing type-safety w/ pyright, but that's ok
+except ImportError: # pragma: no cover
+ # Jython compatibility
+ def chmod(*args: object, **kwargs: object) -> None: # type: ignore[misc] # Mypy reuses the imported definition anyway
+ pass
+
+
+_T = TypeVar("_T")
+
+
+def attempt_chmod_verbose(path, mode):
+ log.debug("changing mode of %s to %o", path, mode)
+ try:
+ chmod(path, mode)
+ except OSError as e: # pragma: no cover
+ log.debug("chmod failed: %s", e)
+
+
+# Must match shutil._OnExcCallback
+def _auto_chmod(
+ func: Callable[..., _T], arg: str, exc: BaseException
+) -> _T: # pragma: no cover
+ """shutils onexc callback to automatically call chmod for certain functions."""
+ # Only retry for scenarios known to have an issue
+ if func in [os.unlink, os.remove] and os.name == 'nt':
+ attempt_chmod_verbose(arg, stat.S_IWRITE)
+ return func(arg)
+ raise exc
+
+
+def rmtree(path, ignore_errors=False, onexc=_auto_chmod):
+ """
+ Similar to ``shutil.rmtree`` but automatically executes ``chmod``
+ for well know Windows failure scenarios.
+ """
+ return py311.shutil_rmtree(path, ignore_errors, onexc)
+
+
+def rmdir(path, **opts):
+ if os.path.isdir(path):
+ rmtree(path, **opts)
diff --git a/contrib/python/setuptools/py3/setuptools/archive_util.py b/contrib/python/setuptools/py3/setuptools/archive_util.py
index e4acd75f9b..cd9cf9c08f 100644
--- a/contrib/python/setuptools/py3/setuptools/archive_util.py
+++ b/contrib/python/setuptools/py3/setuptools/archive_util.py
@@ -31,7 +31,9 @@ def default_filter(src, dst):
return dst
-def unpack_archive(filename, extract_dir, progress_filter=default_filter, drivers=None):
+def unpack_archive(
+ filename, extract_dir, progress_filter=default_filter, drivers=None
+) -> None:
"""Unpack `filename` to `extract_dir`, or raise ``UnrecognizedFormat``
`progress_filter` is a function taking two arguments: a source path
@@ -63,7 +65,7 @@ def unpack_archive(filename, extract_dir, progress_filter=default_filter, driver
raise UnrecognizedFormat("Not a recognized archive type: %s" % filename)
-def unpack_directory(filename, extract_dir, progress_filter=default_filter):
+def unpack_directory(filename, extract_dir, progress_filter=default_filter) -> None:
""" "Unpack" a directory, using the same interface as for archives
Raises ``UnrecognizedFormat`` if `filename` is not a directory
@@ -90,7 +92,7 @@ def unpack_directory(filename, extract_dir, progress_filter=default_filter):
shutil.copystat(f, target)
-def unpack_zipfile(filename, extract_dir, progress_filter=default_filter):
+def unpack_zipfile(filename, extract_dir, progress_filter=default_filter) -> None:
"""Unpack zip `filename` to `extract_dir`
Raises ``UnrecognizedFormat`` if `filename` is not a zipfile (as determined
@@ -185,7 +187,7 @@ def _iter_open_tar(tar_obj, extract_dir, progress_filter):
yield member, final_dst
-def unpack_tarfile(filename, extract_dir, progress_filter=default_filter):
+def unpack_tarfile(filename, extract_dir, progress_filter=default_filter) -> bool:
"""Unpack tar/tar.gz/tar.bz2 `filename` to `extract_dir`
Raises ``UnrecognizedFormat`` if `filename` is not a tarfile (as determined
diff --git a/contrib/python/setuptools/py3/setuptools/build_meta.py b/contrib/python/setuptools/py3/setuptools/build_meta.py
index a6b85afc42..00fa5e1f70 100644
--- a/contrib/python/setuptools/py3/setuptools/build_meta.py
+++ b/contrib/python/setuptools/py3/setuptools/build_meta.py
@@ -37,8 +37,9 @@ import sys
import tempfile
import tokenize
import warnings
+from collections.abc import Iterable, Iterator, Mapping
from pathlib import Path
-from typing import TYPE_CHECKING, Dict, Iterable, Iterator, List, Union
+from typing import TYPE_CHECKING, Union
import setuptools
@@ -53,7 +54,6 @@ from distutils.util import strtobool
if TYPE_CHECKING:
from typing_extensions import TypeAlias
-
__all__ = [
'get_requires_for_build_sdist',
'get_requires_for_build_wheel',
@@ -72,7 +72,7 @@ LEGACY_EDITABLE = "legacy-editable" in SETUPTOOLS_ENABLE_FEATURES.replace("_", "
class SetupRequirementsError(BaseException):
- def __init__(self, specifiers):
+ def __init__(self, specifiers) -> None:
self.specifiers = specifiers
@@ -91,11 +91,11 @@ class Distribution(setuptools.dist.Distribution):
for the duration of this context.
"""
orig = distutils.core.Distribution
- distutils.core.Distribution = cls
+ distutils.core.Distribution = cls # type: ignore[misc] # monkeypatching
try:
yield
finally:
- distutils.core.Distribution = orig
+ distutils.core.Distribution = orig # type: ignore[misc] # monkeypatching
@contextlib.contextmanager
@@ -147,7 +147,7 @@ def suppress_known_deprecation():
yield
-_ConfigSettings: TypeAlias = Union[Dict[str, Union[str, List[str], None]], None]
+_ConfigSettings: TypeAlias = Union[Mapping[str, Union[str, list[str], None]], None]
"""
Currently the user can run::
@@ -291,7 +291,9 @@ class _ConfigSettingsTranslator:
class _BuildMetaBackend(_ConfigSettingsTranslator):
- def _get_build_requires(self, config_settings, requirements):
+ def _get_build_requires(
+ self, config_settings: _ConfigSettings, requirements: list[str]
+ ):
sys.argv = [
*sys.argv[:1],
*self._global_args(config_settings),
@@ -305,7 +307,7 @@ class _BuildMetaBackend(_ConfigSettingsTranslator):
return requirements
- def run_setup(self, setup_script='setup.py'):
+ def run_setup(self, setup_script: str = 'setup.py'):
# Note that we can reuse our build directory between calls
# Correctness comes first, then optimization later
__file__ = os.path.abspath(setup_script)
@@ -328,13 +330,15 @@ class _BuildMetaBackend(_ConfigSettingsTranslator):
"setup-py-deprecated.html",
)
- def get_requires_for_build_wheel(self, config_settings=None):
+ def get_requires_for_build_wheel(self, config_settings: _ConfigSettings = None):
return self._get_build_requires(config_settings, requirements=[])
- def get_requires_for_build_sdist(self, config_settings=None):
+ def get_requires_for_build_sdist(self, config_settings: _ConfigSettings = None):
return self._get_build_requires(config_settings, requirements=[])
- def _bubble_up_info_directory(self, metadata_directory: str, suffix: str) -> str:
+ def _bubble_up_info_directory(
+ self, metadata_directory: StrPath, suffix: str
+ ) -> str:
"""
PEP 517 requires that the .dist-info directory be placed in the
metadata_directory. To comply, we MUST copy the directory to the root.
@@ -347,7 +351,7 @@ class _BuildMetaBackend(_ConfigSettingsTranslator):
# PEP 517 allow other files and dirs to exist in metadata_directory
return info_dir.name
- def _find_info_directory(self, metadata_directory: str, suffix: str) -> Path:
+ def _find_info_directory(self, metadata_directory: StrPath, suffix: str) -> Path:
for parent, dirs, _ in os.walk(metadata_directory):
candidates = [f for f in dirs if f.endswith(suffix)]
@@ -359,14 +363,14 @@ class _BuildMetaBackend(_ConfigSettingsTranslator):
raise errors.InternalError(msg)
def prepare_metadata_for_build_wheel(
- self, metadata_directory, config_settings=None
+ self, metadata_directory: StrPath, config_settings: _ConfigSettings = None
):
sys.argv = [
*sys.argv[:1],
*self._global_args(config_settings),
"dist_info",
"--output-dir",
- metadata_directory,
+ str(metadata_directory),
"--keep-egg-info",
]
with no_install_setup_requires():
@@ -417,14 +421,27 @@ class _BuildMetaBackend(_ConfigSettingsTranslator):
config_settings: _ConfigSettings = None,
metadata_directory: StrPath | None = None,
):
- with suppress_known_deprecation():
- return self._build_with_temp_dir(
- ['bdist_wheel'],
- '.whl',
- wheel_directory,
- config_settings,
- self._arbitrary_args(config_settings),
- )
+ def _build(cmd: list[str]):
+ with suppress_known_deprecation():
+ return self._build_with_temp_dir(
+ cmd,
+ '.whl',
+ wheel_directory,
+ config_settings,
+ self._arbitrary_args(config_settings),
+ )
+
+ if metadata_directory is None:
+ return _build(['bdist_wheel'])
+
+ try:
+ return _build(['bdist_wheel', '--dist-info-dir', str(metadata_directory)])
+ except SystemExit as ex: # pragma: nocover
+ # pypa/setuptools#4683
+ if "--dist-info-dir not recognized" not in str(ex):
+ raise
+ _IncompatibleBdistWheel.emit()
+ return _build(['bdist_wheel'])
def build_sdist(
self, sdist_directory: StrPath, config_settings: _ConfigSettings = None
@@ -449,7 +466,7 @@ class _BuildMetaBackend(_ConfigSettingsTranslator):
self,
wheel_directory: StrPath,
config_settings: _ConfigSettings = None,
- metadata_directory: str | None = None,
+ metadata_directory: StrPath | None = None,
):
# XXX can or should we hide our editable_wheel command normally?
info_dir = self._get_dist_info_dir(metadata_directory)
@@ -460,11 +477,13 @@ class _BuildMetaBackend(_ConfigSettingsTranslator):
cmd, ".whl", wheel_directory, config_settings
)
- def get_requires_for_build_editable(self, config_settings=None):
+ def get_requires_for_build_editable(
+ self, config_settings: _ConfigSettings = None
+ ):
return self.get_requires_for_build_wheel(config_settings)
def prepare_metadata_for_build_editable(
- self, metadata_directory, config_settings=None
+ self, metadata_directory: StrPath, config_settings: _ConfigSettings = None
):
return self.prepare_metadata_for_build_wheel(
metadata_directory, config_settings
@@ -483,7 +502,7 @@ class _BuildMetaLegacyBackend(_BuildMetaBackend):
and will eventually be removed.
"""
- def run_setup(self, setup_script='setup.py'):
+ def run_setup(self, setup_script: str = 'setup.py'):
# In order to maintain compatibility with scripts assuming that
# the setup.py script is in a directory on the PYTHONPATH, inject
# '' into sys.path. (pypa/setuptools#1642)
@@ -511,6 +530,17 @@ class _BuildMetaLegacyBackend(_BuildMetaBackend):
sys.argv[0] = sys_argv_0
+class _IncompatibleBdistWheel(SetuptoolsDeprecationWarning):
+ _SUMMARY = "wheel.bdist_wheel is deprecated, please import it from setuptools"
+ _DETAILS = """
+ Ensure that any custom bdist_wheel implementation is a subclass of
+ setuptools.command.bdist_wheel.bdist_wheel.
+ """
+ _DUE_DATE = (2025, 10, 15)
+ # Initially introduced in 2024/10/15, but maybe too disruptive to be enforced?
+ _SEE_URL = "https://github.com/pypa/wheel/pull/631"
+
+
# The primary backend
_BACKEND = _BuildMetaBackend()
diff --git a/contrib/python/setuptools/py3/setuptools/command/__init__.py b/contrib/python/setuptools/py3/setuptools/command/__init__.py
index bf011e896d..50e6c2f54f 100644
--- a/contrib/python/setuptools/py3/setuptools/command/__init__.py
+++ b/contrib/python/setuptools/py3/setuptools/command/__init__.py
@@ -1,12 +1,20 @@
+# mypy: disable_error_code=call-overload
+# pyright: reportCallIssue=false, reportArgumentType=false
+# Can't disable on the exact line because distutils doesn't exists on Python 3.12
+# and type-checkers aren't aware of distutils_hack,
+# causing distutils.command.bdist.bdist.format_commands to be Any.
+
import sys
from distutils.command.bdist import bdist
if 'egg' not in bdist.format_commands:
try:
+ # format_commands is a dict in vendored distutils
+ # It used to be a list in older (stdlib) distutils
+ # We support both for backwards compatibility
bdist.format_commands['egg'] = ('bdist_egg', "Python .egg file")
except TypeError:
- # For backward compatibility with older distutils (stdlib)
bdist.format_command['egg'] = ('bdist_egg', "Python .egg file")
bdist.format_commands.append('egg')
diff --git a/contrib/python/setuptools/py3/setuptools/command/_requirestxt.py b/contrib/python/setuptools/py3/setuptools/command/_requirestxt.py
index b87476d6f4..171f41b87e 100644
--- a/contrib/python/setuptools/py3/setuptools/command/_requirestxt.py
+++ b/contrib/python/setuptools/py3/setuptools/command/_requirestxt.py
@@ -11,19 +11,19 @@ from __future__ import annotations
import io
from collections import defaultdict
+from collections.abc import Mapping
from itertools import filterfalse
-from typing import Dict, Mapping, TypeVar
+from typing import TypeVar
from jaraco.text import yield_lines
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
+_Ordered = dict[_T, None]
def _prepare(
diff --git a/contrib/python/setuptools/py3/setuptools/command/alias.py b/contrib/python/setuptools/py3/setuptools/command/alias.py
index 4eed652381..388830d7a6 100644
--- a/contrib/python/setuptools/py3/setuptools/command/alias.py
+++ b/contrib/python/setuptools/py3/setuptools/command/alias.py
@@ -30,15 +30,14 @@ class alias(option_base):
self.args = None
self.remove = None
- def finalize_options(self):
+ def finalize_options(self) -> None:
option_base.finalize_options(self)
if self.remove and len(self.args) != 1:
raise DistutilsOptionError(
- "Must specify exactly one argument (the alias name) when "
- "using --remove"
+ "Must specify exactly one argument (the alias name) when using --remove"
)
- def run(self):
+ def run(self) -> None:
aliases = self.distribution.get_option_dict('aliases')
if not self.args:
diff --git a/contrib/python/setuptools/py3/setuptools/command/bdist_egg.py b/contrib/python/setuptools/py3/setuptools/command/bdist_egg.py
index f3b7150208..ac3e6ef1f9 100644
--- a/contrib/python/setuptools/py3/setuptools/command/bdist_egg.py
+++ b/contrib/python/setuptools/py3/setuptools/command/bdist_egg.py
@@ -2,6 +2,8 @@
Build .egg distributions"""
+from __future__ import annotations
+
import marshal
import os
import re
@@ -9,15 +11,22 @@ import sys
import textwrap
from sysconfig import get_path, get_python_version
from types import CodeType
+from typing import TYPE_CHECKING, Literal
from setuptools import Command
from setuptools.extension import Library
-from .._path import ensure_directory
+from .._path import StrPathT, ensure_directory
from distutils import log
from distutils.dir_util import mkpath, remove_tree
+if TYPE_CHECKING:
+ from typing_extensions import TypeAlias
+
+# Same as zipfile._ZipFileMode from typeshed
+_ZipFileMode: TypeAlias = Literal["r", "w", "x", "a"]
+
def _get_purelib():
return get_path("purelib")
@@ -41,7 +50,7 @@ def sorted_walk(dir):
yield base, dirs, files
-def write_stub(resource, pyfile):
+def write_stub(resource, pyfile) -> None:
_stub_template = textwrap.dedent(
"""
def __bootstrap__():
@@ -92,7 +101,7 @@ class bdist_egg(Command):
self.egg_output = None
self.exclude_source_files = None
- def finalize_options(self):
+ def finalize_options(self) -> None:
ei_cmd = self.ei_cmd = self.get_finalized_command("egg_info")
self.egg_info = ei_cmd.egg_info
@@ -116,7 +125,7 @@ class bdist_egg(Command):
self.egg_output = os.path.join(self.dist_dir, basename + '.egg')
- def do_install_data(self):
+ def do_install_data(self) -> None:
# Hack for packages that install data to install's --install-lib
self.get_finalized_command('install').install_lib = self.bdist_dir
@@ -172,7 +181,7 @@ class bdist_egg(Command):
self.stubs = []
to_compile = []
for p, ext_name in enumerate(ext_outputs):
- filename, ext = os.path.splitext(ext_name)
+ filename, _ext = os.path.splitext(ext_name)
pyfile = os.path.join(self.bdist_dir, strip_module(filename) + '.py')
self.stubs.append(pyfile)
log.info("creating stub loader for %s", ext_name)
@@ -268,10 +277,10 @@ class bdist_egg(Command):
log.warn("zip_safe flag not set; analyzing archive contents...")
return analyze_egg(self.bdist_dir, self.stubs)
- def gen_header(self):
+ def gen_header(self) -> Literal["w"]:
return 'w'
- def copy_metadata_to(self, target_dir):
+ def copy_metadata_to(self, target_dir) -> None:
"Copy metadata (egg info) to the target_dir"
# normalize the path (so that a forward-slash in egg_info will
# match using startswith below)
@@ -313,7 +322,7 @@ class bdist_egg(Command):
return all_outputs, ext_outputs
-NATIVE_EXTENSIONS = dict.fromkeys('.dll .so .dylib .pyd'.split())
+NATIVE_EXTENSIONS: dict[str, None] = dict.fromkeys('.dll .so .dylib .pyd'.split())
def walk_egg(egg_dir):
@@ -344,7 +353,7 @@ def analyze_egg(egg_dir, stubs):
return safe
-def write_safety_flag(egg_dir, safe):
+def write_safety_flag(egg_dir, safe) -> None:
# Write or remove zip safety flag file(s)
for flag, fn in safety_flags.items():
fn = os.path.join(egg_dir, fn)
@@ -412,7 +421,7 @@ def iter_symbols(code):
yield from iter_symbols(const)
-def can_scan():
+def can_scan() -> bool:
if not sys.platform.startswith('java') and sys.platform != 'cli':
# CPython, PyPy, etc.
return True
@@ -431,8 +440,13 @@ INSTALL_DIRECTORY_ATTRS = ['install_lib', 'install_dir', 'install_data', 'instal
def make_zipfile(
- zip_filename, base_dir, verbose=False, dry_run=False, compress=True, mode='w'
-):
+ zip_filename: StrPathT,
+ base_dir,
+ verbose: bool = False,
+ dry_run: bool = False,
+ compress=True,
+ mode: _ZipFileMode = 'w',
+) -> StrPathT:
"""Create a zip file from all the files under 'base_dir'. The output
zip file will be named 'base_dir' + ".zip". Uses either the "zipfile"
Python module (if available) or the InfoZIP "zip" utility (if installed
@@ -441,7 +455,7 @@ def make_zipfile(
"""
import zipfile
- mkpath(os.path.dirname(zip_filename), dry_run=dry_run)
+ mkpath(os.path.dirname(zip_filename), dry_run=dry_run) # type: ignore[arg-type] # python/mypy#18075
log.info("creating '%s' and adding '%s' to it", zip_filename, base_dir)
def visit(z, dirname, names):
diff --git a/contrib/python/setuptools/py3/setuptools/command/bdist_rpm.py b/contrib/python/setuptools/py3/setuptools/command/bdist_rpm.py
index e0d4caf2e9..6dbb27002a 100644
--- a/contrib/python/setuptools/py3/setuptools/command/bdist_rpm.py
+++ b/contrib/python/setuptools/py3/setuptools/command/bdist_rpm.py
@@ -15,7 +15,7 @@ class bdist_rpm(orig.bdist_rpm):
distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution
- def run(self):
+ def run(self) -> None:
SetuptoolsDeprecationWarning.emit(
"Deprecated command",
"""
diff --git a/contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py b/contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py
index 8f06786659..234df2a7c7 100644
--- a/contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py
+++ b/contrib/python/setuptools/py3/setuptools/command/bdist_wheel.py
@@ -9,30 +9,25 @@ from __future__ import annotations
import os
import re
import shutil
-import stat
import struct
import sys
import sysconfig
import warnings
-from email.generator import BytesGenerator, Generator
-from email.policy import EmailPolicy
+from collections.abc import Iterable, Sequence
+from email.generator import BytesGenerator
from glob import iglob
-from shutil import rmtree
-from typing import TYPE_CHECKING, Callable, Iterable, Literal, Sequence, cast
+from typing import Literal, cast
from zipfile import ZIP_DEFLATED, ZIP_STORED
from packaging import tags, version as _packaging_version
-from wheel.metadata import pkginfo_to_metadata
from wheel.wheelfile import WheelFile
-from .. import Command, __version__
+from .. import Command, __version__, _shutil
+from ..warnings import SetuptoolsDeprecationWarning
from .egg_info import egg_info as egg_info_cls
from distutils import log
-if TYPE_CHECKING:
- from _typeshed import ExcInfo
-
def safe_name(name: str) -> str:
"""Convert an arbitrary string to a standard distribution name
@@ -63,7 +58,7 @@ def _is_32bit_interpreter() -> bool:
def python_tag() -> str:
- return f"py{sys.version_info[0]}"
+ return f"py{sys.version_info.major}"
def get_platform(archive_root: str | None) -> str:
@@ -109,25 +104,20 @@ def get_abi_tag() -> str | None:
impl = tags.interpreter_name()
if not soabi and impl in ("cp", "pp") and hasattr(sys, "maxunicode"):
d = ""
- m = ""
u = ""
if get_flag("Py_DEBUG", hasattr(sys, "gettotalrefcount"), warn=(impl == "cp")):
d = "d"
- if get_flag(
- "WITH_PYMALLOC",
- impl == "cp",
- warn=(impl == "cp" and sys.version_info < (3, 8)),
- ) and sys.version_info < (3, 8):
- m = "m"
-
- abi = f"{impl}{tags.interpreter_version()}{d}{m}{u}"
+ abi = f"{impl}{tags.interpreter_version()}{d}{u}"
elif soabi and impl == "cp" and soabi.startswith("cpython"):
# non-Windows
abi = "cp" + soabi.split("-")[1]
elif soabi and impl == "cp" and soabi.startswith("cp"):
# Windows
abi = soabi.split("-")[0]
+ if hasattr(sys, "gettotalrefcount"):
+ # using debug build; append "d" flag
+ abi += "d"
elif soabi and impl == "pp":
# we want something like pypy36-pp73
abi = "-".join(soabi.split("-")[:2])
@@ -151,21 +141,6 @@ def safer_version(version: str) -> str:
return safe_version(version).replace("-", "_")
-def remove_readonly(
- func: Callable[..., object],
- path: str,
- excinfo: ExcInfo,
-) -> None:
- remove_readonly_exc(func, path, excinfo[1])
-
-
-def remove_readonly_exc(
- func: Callable[..., object], path: str, exc: BaseException
-) -> None:
- os.chmod(path, stat.S_IWRITE)
- func(path)
-
-
class bdist_wheel(Command):
description = "create a wheel distribution"
@@ -205,7 +180,7 @@ class bdist_wheel(Command):
"g",
"Group name used when creating a tar file [default: current group]",
),
- ("universal", None, "make a universal wheel [default: false]"),
+ ("universal", None, "*DEPRECATED* make a universal wheel [default: false]"),
(
"compression=",
None,
@@ -230,27 +205,35 @@ class bdist_wheel(Command):
None,
"Python tag (cp32|cp33|cpNN) for abi3 wheel tag [default: false]",
),
+ (
+ "dist-info-dir=",
+ None,
+ "directory where a pre-generated dist-info can be found (e.g. as a "
+ "result of calling the PEP517 'prepare_metadata_for_build_wheel' "
+ "method)",
+ ),
]
boolean_options = ["keep-temp", "skip-build", "relative", "universal"]
def initialize_options(self) -> None:
self.bdist_dir: str | None = None
- self.data_dir: str | None = None
+ self.data_dir = ""
self.plat_name: str | None = None
self.plat_tag: str | None = None
self.format = "zip"
self.keep_temp = False
self.dist_dir: str | None = None
+ self.dist_info_dir = None
self.egginfo_dir: str | None = None
self.root_is_pure: bool | None = None
self.skip_build = False
self.relative = False
self.owner = None
self.group = None
- self.universal: bool = False
- self.compression: int | str = "deflated"
- self.python_tag: str = python_tag()
+ self.universal = False
+ self.compression: str | int = "deflated"
+ self.python_tag = python_tag()
self.build_number: str | None = None
self.py_limited_api: str | Literal[False] = False
self.plat_name_supplied = False
@@ -260,8 +243,9 @@ class bdist_wheel(Command):
bdist_base = self.get_finalized_command("bdist").bdist_base
self.bdist_dir = os.path.join(bdist_base, "wheel")
- egg_info = cast(egg_info_cls, self.distribution.get_command_obj("egg_info"))
- egg_info.ensure_finalized() # needed for correct `wheel_dist_name`
+ if self.dist_info_dir is None:
+ egg_info = cast(egg_info_cls, self.distribution.get_command_obj("egg_info"))
+ egg_info.ensure_finalized() # needed for correct `wheel_dist_name`
self.data_dir = self.wheel_dist_name + ".data"
self.plat_name_supplied = bool(self.plat_name)
@@ -278,13 +262,26 @@ class bdist_wheel(Command):
# Support legacy [wheel] section for setting universal
wheel = self.distribution.get_option_dict("wheel")
- if "universal" in wheel:
+ if "universal" in wheel: # pragma: no cover
# please don't define this in your global configs
log.warn("The [wheel] section is deprecated. Use [bdist_wheel] instead.")
val = wheel["universal"][1].strip()
if val.lower() in ("1", "true", "yes"):
self.universal = True
+ if self.universal:
+ SetuptoolsDeprecationWarning.emit(
+ "bdist_wheel.universal is deprecated",
+ """
+ With Python 2.7 end-of-life, support for building universal wheels
+ (i.e., wheels that support both Python 2 and Python 3)
+ is being obviated.
+ Please discontinue using this option, or if you still need it,
+ file an issue with pypa/setuptools describing your use case.
+ """,
+ due_date=(2025, 8, 30), # Introduced in 2024-08-30
+ )
+
if self.build_number is not None and not self.build_number[:1].isdigit():
raise ValueError("Build tag (build-number) must start with a digit.")
@@ -367,9 +364,9 @@ class bdist_wheel(Command):
supported_tags = [
(t.interpreter, t.abi, plat_name) for t in tags.sys_tags()
]
- assert (
- tag in supported_tags
- ), f"would build wheel with unsupported tag {tag}"
+ assert tag in supported_tags, (
+ f"would build wheel with unsupported tag {tag}"
+ )
return tag
def run(self):
@@ -433,7 +430,16 @@ class bdist_wheel(Command):
f"{safer_version(self.distribution.get_version())}.dist-info"
)
distinfo_dir = os.path.join(self.bdist_dir, distinfo_dirname)
- self.egg2dist(self.egginfo_dir, distinfo_dir)
+ if self.dist_info_dir:
+ # Use the given dist-info directly.
+ log.debug(f"reusing {self.dist_info_dir}")
+ shutil.copytree(self.dist_info_dir, distinfo_dir)
+ # Egg info is still generated, so remove it now to avoid it getting
+ # copied into the wheel.
+ _shutil.rmtree(self.egginfo_dir)
+ else:
+ # Convert the generated egg-info into dist-info.
+ self.egg2dist(self.egginfo_dir, distinfo_dir)
self.write_wheelfile(distinfo_dir)
@@ -448,17 +454,14 @@ class bdist_wheel(Command):
# Add to 'Distribution.dist_files' so that the "upload" command works
getattr(self.distribution, "dist_files", []).append((
"bdist_wheel",
- "{}.{}".format(*sys.version_info[:2]), # like 3.7
+ f"{sys.version_info.major}.{sys.version_info.minor}",
wheel_path,
))
if not self.keep_temp:
log.info(f"removing {self.bdist_dir}")
if not self.dry_run:
- if sys.version_info < (3, 12):
- rmtree(self.bdist_dir, onerror=remove_readonly)
- else:
- rmtree(self.bdist_dir, onexc=remove_readonly_exc)
+ _shutil.rmtree(self.bdist_dir)
def write_wheelfile(
self, wheelfile_base: str, generator: str = f"setuptools ({__version__})"
@@ -542,7 +545,7 @@ class bdist_wheel(Command):
def adios(p: str) -> None:
"""Appropriately delete directory, file or link."""
if os.path.exists(p) and not os.path.islink(p) and os.path.isdir(p):
- shutil.rmtree(p)
+ _shutil.rmtree(p)
elif os.path.exists(p):
os.unlink(p)
@@ -564,42 +567,30 @@ class bdist_wheel(Command):
raise ValueError(err)
- if os.path.isfile(egginfo_path):
- # .egg-info is a single file
- pkg_info = pkginfo_to_metadata(egginfo_path, egginfo_path)
- os.mkdir(distinfo_path)
- else:
- # .egg-info is a directory
- pkginfo_path = os.path.join(egginfo_path, "PKG-INFO")
- pkg_info = pkginfo_to_metadata(egginfo_path, pkginfo_path)
-
- # ignore common egg metadata that is useless to wheel
- shutil.copytree(
- egginfo_path,
- distinfo_path,
- ignore=lambda x, y: {
- "PKG-INFO",
- "requires.txt",
- "SOURCES.txt",
- "not-zip-safe",
- },
- )
-
- # delete dependency_links if it is only whitespace
- dependency_links_path = os.path.join(distinfo_path, "dependency_links.txt")
- with open(dependency_links_path, encoding="utf-8") as dependency_links_file:
- dependency_links = dependency_links_file.read().strip()
- if not dependency_links:
- adios(dependency_links_path)
-
- pkg_info_path = os.path.join(distinfo_path, "METADATA")
- serialization_policy = EmailPolicy(
- utf8=True,
- mangle_from_=False,
- max_line_length=0,
+ # .egg-info is a directory
+ pkginfo_path = os.path.join(egginfo_path, "PKG-INFO")
+
+ # ignore common egg metadata that is useless to wheel
+ shutil.copytree(
+ egginfo_path,
+ distinfo_path,
+ ignore=lambda x, y: {
+ "PKG-INFO",
+ "requires.txt",
+ "SOURCES.txt",
+ "not-zip-safe",
+ },
)
- with open(pkg_info_path, "w", encoding="utf-8") as out:
- Generator(out, policy=serialization_policy).flatten(pkg_info)
+
+ # delete dependency_links if it is only whitespace
+ dependency_links_path = os.path.join(distinfo_path, "dependency_links.txt")
+ with open(dependency_links_path, encoding="utf-8") as dependency_links_file:
+ dependency_links = dependency_links_file.read().strip()
+ if not dependency_links:
+ adios(dependency_links_path)
+
+ metadata_path = os.path.join(distinfo_path, "METADATA")
+ shutil.copy(pkginfo_path, metadata_path)
for license_path in self.license_paths:
filename = os.path.basename(license_path)
diff --git a/contrib/python/setuptools/py3/setuptools/command/build.py b/contrib/python/setuptools/py3/setuptools/command/build.py
index f60fcbda15..54cbb8d2e7 100644
--- a/contrib/python/setuptools/py3/setuptools/command/build.py
+++ b/contrib/python/setuptools/py3/setuptools/command/build.py
@@ -85,15 +85,15 @@ class SubCommand(Protocol):
...
"""
- def initialize_options(self):
+ def initialize_options(self) -> None:
"""(Required by the original :class:`setuptools.Command` interface)"""
...
- def finalize_options(self):
+ def finalize_options(self) -> None:
"""(Required by the original :class:`setuptools.Command` interface)"""
...
- def run(self):
+ def run(self) -> None:
"""(Required by the original :class:`setuptools.Command` interface)"""
...
diff --git a/contrib/python/setuptools/py3/setuptools/command/build_clib.py b/contrib/python/setuptools/py3/setuptools/command/build_clib.py
index d532762ebe..bee3d58c03 100644
--- a/contrib/python/setuptools/py3/setuptools/command/build_clib.py
+++ b/contrib/python/setuptools/py3/setuptools/command/build_clib.py
@@ -1,17 +1,10 @@
from ..dist import Distribution
+from ..modified import newer_pairwise_group
import distutils.command.build_clib as orig
from distutils import log
from distutils.errors import DistutilsSetupError
-try:
- from distutils._modified import ( # pyright: ignore[reportMissingImports]
- newer_pairwise_group,
- )
-except ImportError:
- # fallback for SETUPTOOLS_USE_DISTUTILS=stdlib
- from .._distutils._modified import newer_pairwise_group
-
class build_clib(orig.build_clib):
"""
@@ -31,7 +24,7 @@ class build_clib(orig.build_clib):
distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution
- def build_libraries(self, libraries):
+ def build_libraries(self, libraries) -> None:
for lib_name, build_info in libraries:
sources = build_info.get('sources')
if sources is None or not isinstance(sources, (list, tuple)):
diff --git a/contrib/python/setuptools/py3/setuptools/command/build_ext.py b/contrib/python/setuptools/py3/setuptools/command/build_ext.py
index e2a88ce218..e5c6b76b38 100644
--- a/contrib/python/setuptools/py3/setuptools/command/build_ext.py
+++ b/contrib/python/setuptools/py3/setuptools/command/build_ext.py
@@ -3,10 +3,11 @@ from __future__ import annotations
import itertools
import os
import sys
+from collections.abc import Iterator
from importlib.machinery import EXTENSION_SUFFIXES
from importlib.util import cache_from_source as _compiled_file_name
from pathlib import Path
-from typing import TYPE_CHECKING, Iterator
+from typing import TYPE_CHECKING
from setuptools.dist import Distribution
from setuptools.errors import BaseError
@@ -89,12 +90,12 @@ def get_abi3_suffix():
class build_ext(_build_ext):
distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution
- editable_mode: bool = False
- inplace: bool = False
+ editable_mode = False
+ inplace = False
def run(self):
"""Build extensions in build directory, then copy if --inplace"""
- old_inplace, self.inplace = self.inplace, 0
+ old_inplace, self.inplace = self.inplace, False
_build_ext.run(self)
self.inplace = old_inplace
if old_inplace:
@@ -110,7 +111,7 @@ class build_ext(_build_ext):
regular_file = os.path.join(self.build_lib, filename)
return (inplace_file, regular_file)
- def copy_extensions_to_source(self):
+ def copy_extensions_to_source(self) -> None:
build_py = self.get_finalized_command('build_py')
for ext in self.extensions:
inplace_file, regular_file = self._get_inplace_equivalent(build_py, ext)
@@ -191,7 +192,7 @@ class build_ext(_build_ext):
self.ext_map = {}
self.editable_mode = False
- def finalize_options(self):
+ def finalize_options(self) -> None:
_build_ext.finalize_options(self)
self.extensions = self.extensions or []
self.check_extensions_list(self.extensions)
@@ -247,14 +248,14 @@ class build_ext(_build_ext):
compiler.set_link_objects(self.link_objects)
# hack so distutils' build_extension() builds a library instead
- compiler.link_shared_object = link_shared_object.__get__(compiler)
+ compiler.link_shared_object = link_shared_object.__get__(compiler) # type: ignore[method-assign]
def get_export_symbols(self, ext):
if isinstance(ext, Library):
return ext.export_symbols
return _build_ext.get_export_symbols(self, ext)
- def build_extension(self, ext):
+ def build_extension(self, ext) -> None:
ext._convert_pyx_sources_to_lang()
_compiler = self.compiler
try:
@@ -344,7 +345,7 @@ class build_ext(_build_ext):
if self.get_finalized_command('build_py').optimize:
yield '.pyo'
- def write_stub(self, output_dir, ext, compile=False):
+ def write_stub(self, output_dir, ext, compile=False) -> None:
stub_file = os.path.join(output_dir, *ext._full_name.split('.')) + '.py'
self._write_stub_file(stub_file, ext, compile)
@@ -410,12 +411,12 @@ if use_stubs or os.name == 'nt':
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
- debug=False,
+ debug: bool = False,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None,
- ):
+ ) -> None:
self.link(
self.SHARED_LIBRARY,
objects,
@@ -445,12 +446,12 @@ else:
library_dirs=None,
runtime_library_dirs=None,
export_symbols=None,
- debug=False,
+ debug: bool = False,
extra_preargs=None,
extra_postargs=None,
build_temp=None,
target_lang=None,
- ):
+ ) -> None:
# XXX we need to either disallow these attrs on Library instances,
# or warn/abort here if set, or something...
# libraries=None, library_dirs=None, runtime_library_dirs=None,
@@ -459,7 +460,7 @@ else:
assert output_dir is None # distutils build_ext doesn't pass this
output_dir, filename = os.path.split(output_libname)
- basename, ext = os.path.splitext(filename)
+ basename, _ext = os.path.splitext(filename)
if self.library_filename("x").startswith('lib'):
# strip 'lib' prefix; this is kludgy if some platform uses
# a different prefix
diff --git a/contrib/python/setuptools/py3/setuptools/command/build_py.py b/contrib/python/setuptools/py3/setuptools/command/build_py.py
index 584d2c15ac..e7d60c6440 100644
--- a/contrib/python/setuptools/py3/setuptools/command/build_py.py
+++ b/contrib/python/setuptools/py3/setuptools/command/build_py.py
@@ -5,13 +5,14 @@ import itertools
import os
import stat
import textwrap
+from collections.abc import Iterable, Iterator
from functools import partial
from glob import glob
from pathlib import Path
-from typing import Iterable, Iterator
from more_itertools import unique_everseen
+from .._path import StrPath, StrPathT
from ..dist import Distribution
from ..warnings import SetuptoolsDeprecationWarning
@@ -22,7 +23,7 @@ from distutils.util import convert_path
_IMPLICIT_DATA_FILES = ('*.pyi', 'py.typed')
-def make_writable(target):
+def make_writable(target) -> None:
os.chmod(target, os.stat(target).st_mode | stat.S_IWRITE)
@@ -38,7 +39,7 @@ class build_py(orig.build_py):
distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution
editable_mode: bool = False
- existing_egg_info_dir: str | None = None #: Private API, internal use only.
+ existing_egg_info_dir: StrPath | None = None #: Private API, internal use only.
def finalize_options(self):
orig.build_py.finalize_options(self)
@@ -46,26 +47,25 @@ class build_py(orig.build_py):
self.exclude_package_data = self.distribution.exclude_package_data or {}
if 'data_files' in self.__dict__:
del self.__dict__['data_files']
- self.__updated_files = []
- def copy_file(
+ def copy_file( # type: ignore[override] # No overload, no bytes support
self,
- infile,
- outfile,
- preserve_mode=True,
- preserve_times=True,
- link=None,
- level=1,
- ):
+ infile: StrPath,
+ outfile: StrPathT,
+ preserve_mode: bool = True,
+ preserve_times: bool = True,
+ link: str | None = None,
+ level: object = 1,
+ ) -> tuple[StrPathT | str, bool]:
# Overwrite base class to allow using links
if link:
infile = str(Path(infile).resolve())
- outfile = str(Path(outfile).resolve())
- return super().copy_file(
+ outfile = str(Path(outfile).resolve()) # type: ignore[assignment] # Re-assigning a str when outfile is StrPath is ok
+ return super().copy_file( # pyright: ignore[reportReturnType] # pypa/distutils#309
infile, outfile, preserve_mode, preserve_times, link, level
)
- def run(self):
+ def run(self) -> None:
"""Build modules, packages, and copy data files to build directory"""
if not (self.py_modules or self.packages) or self.editable_mode:
return
@@ -88,12 +88,6 @@ class build_py(orig.build_py):
return self.data_files
return orig.build_py.__getattr__(self, attr)
- def build_module(self, module, module_file, package):
- outfile, copied = orig.build_py.build_module(self, module, module_file, package)
- if copied:
- self.__updated_files.append(outfile)
- return outfile, copied
-
def _get_data_files(self):
"""Generate list of '(package,src_dir,build_dir,filenames)' tuples"""
self.analyze_manifest()
@@ -141,7 +135,7 @@ class build_py(orig.build_py):
)
return self.exclude_data_files(package, src_dir, files)
- def get_outputs(self, include_bytecode=True) -> list[str]:
+ def get_outputs(self, include_bytecode: bool = True) -> list[str]: # type: ignore[override] # Using a real boolean instead of 0|1
"""See :class:`setuptools.commands.build.SubCommand`"""
if self.editable_mode:
return list(self.get_output_mapping().keys())
@@ -170,24 +164,24 @@ class build_py(orig.build_py):
srcfile = os.path.join(src_dir, filename)
yield (target, srcfile)
- def build_package_data(self):
+ def build_package_data(self) -> None:
"""Copy data files into build directory"""
for target, srcfile in self._get_package_data_output_mapping():
self.mkpath(os.path.dirname(target))
_outf, _copied = self.copy_file(srcfile, target)
make_writable(target)
- def analyze_manifest(self):
- self.manifest_files = mf = {}
+ def analyze_manifest(self) -> None:
+ self.manifest_files: dict[str, list[str]] = {}
if not self.distribution.include_package_data:
return
- src_dirs = {}
+ src_dirs: dict[str, str] = {}
for package in self.packages or ():
# Locate package source directory
src_dirs[assert_relative(self.get_package_dir(package))] = package
if (
- getattr(self, 'existing_egg_info_dir', None)
+ self.existing_egg_info_dir
and Path(self.existing_egg_info_dir, "SOURCES.txt").exists()
):
egg_info_dir = self.existing_egg_info_dir
@@ -216,9 +210,11 @@ class build_py(orig.build_py):
importable = check.importable_subpackage(src_dirs[d], f)
if importable:
check.warn(importable)
- mf.setdefault(src_dirs[d], []).append(path)
+ self.manifest_files.setdefault(src_dirs[d], []).append(path)
- def _filter_build_files(self, files: Iterable[str], egg_info: str) -> Iterator[str]:
+ def _filter_build_files(
+ self, files: Iterable[str], egg_info: StrPath
+ ) -> Iterator[str]:
"""
``build_meta`` may try to create egg_info outside of the project directory,
and this can be problematic for certain plugins (reported in issue #3500).
@@ -237,7 +233,7 @@ class build_py(orig.build_py):
if not os.path.isabs(file) or all(d not in norm_path for d in norm_dirs):
yield file
- def get_data_files(self):
+ def get_data_files(self) -> None:
pass # Lazily compute data files in _get_data_files() function.
def check_package(self, package, package_dir):
diff --git a/contrib/python/setuptools/py3/setuptools/command/develop.py b/contrib/python/setuptools/py3/setuptools/command/develop.py
index 4ecbd5a1e8..7eee29d491 100644
--- a/contrib/python/setuptools/py3/setuptools/command/develop.py
+++ b/contrib/python/setuptools/py3/setuptools/command/develop.py
@@ -42,7 +42,7 @@ class develop(namespaces.DevelopInstaller, easy_install):
self.setup_path = None
self.always_copy_from = '.' # always copy eggs installed in curdir
- def finalize_options(self):
+ def finalize_options(self) -> None:
import pkg_resources
ei = self.get_finalized_command("egg_info")
@@ -104,7 +104,7 @@ class develop(namespaces.DevelopInstaller, easy_install):
)
return path_to_setup
- def install_for_development(self):
+ def install_for_development(self) -> None:
self.run_command('egg_info')
# Build extensions in-place
@@ -126,7 +126,7 @@ class develop(namespaces.DevelopInstaller, easy_install):
# and handling requirements
self.process_distribution(None, self.dist, not self.no_deps)
- def uninstall_link(self):
+ def uninstall_link(self) -> None:
if os.path.exists(self.egg_link):
log.info("Removing %s (link to %s)", self.egg_link, self.egg_base)
@@ -185,7 +185,7 @@ class VersionlessRequirement:
'foo'
"""
- def __init__(self, dist):
+ def __init__(self, dist) -> None:
self.__dist = dist
def __getattr__(self, name: str):
diff --git a/contrib/python/setuptools/py3/setuptools/command/dist_info.py b/contrib/python/setuptools/py3/setuptools/command/dist_info.py
index 1db3fbf6bd..0192ebb260 100644
--- a/contrib/python/setuptools/py3/setuptools/command/dist_info.py
+++ b/contrib/python/setuptools/py3/setuptools/command/dist_info.py
@@ -10,6 +10,7 @@ from pathlib import Path
from typing import cast
from .. import _normalization
+from .._shutil import rmdir as _rm
from .egg_info import egg_info as egg_info_cls
from distutils import log
@@ -48,7 +49,7 @@ class dist_info(Command):
self.tag_build = None
self.keep_egg_info = False
- def finalize_options(self):
+ def finalize_options(self) -> None:
dist = self.distribution
project_dir = dist.src_root or os.curdir
self.output_dir = Path(self.output_dir or project_dir)
@@ -88,7 +89,7 @@ class dist_info(Command):
else:
yield
- def run(self):
+ def run(self) -> None:
self.output_dir.mkdir(parents=True, exist_ok=True)
self.egg_info.run()
egg_info_dir = self.egg_info.egg_info
@@ -100,8 +101,3 @@ class dist_info(Command):
# TODO: if bdist_wheel if merged into setuptools, just add "keep_egg_info" there
with self._maybe_bkp_dir(egg_info_dir, self.keep_egg_info):
bdist_wheel.egg2dist(egg_info_dir, self.dist_info_dir)
-
-
-def _rm(dir_name, **opts):
- if os.path.isdir(dir_name):
- shutil.rmtree(dir_name, **opts)
diff --git a/contrib/python/setuptools/py3/setuptools/command/easy_install.py b/contrib/python/setuptools/py3/setuptools/command/easy_install.py
index 3f7fc17a88..66fe68f7a9 100644
--- a/contrib/python/setuptools/py3/setuptools/command/easy_install.py
+++ b/contrib/python/setuptools/py3/setuptools/command/easy_install.py
@@ -34,7 +34,7 @@ import zipimport
from collections.abc import Iterable
from glob import glob
from sysconfig import get_path
-from typing import TYPE_CHECKING, Callable, TypeVar
+from typing import TYPE_CHECKING, NoReturn, TypedDict
from jaraco.text import yield_lines
@@ -63,7 +63,8 @@ from setuptools.warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning
from setuptools.wheel import Wheel
from .._path import ensure_directory
-from ..compat import py39, py311, py312
+from .._shutil import attempt_chmod_verbose as chmod, rmtree as _rmtree
+from ..compat import py39, py312
from distutils import dir_util, log
from distutils.command import install
@@ -89,8 +90,6 @@ __all__ = [
'get_exe_prefixes',
]
-_T = TypeVar("_T")
-
def is_64bit():
return struct.calcsize("P") == 8
@@ -212,7 +211,7 @@ class easy_install(Command):
self, self.distribution.get_option_dict('easy_install')
)
- def delete_blockers(self, blockers):
+ def delete_blockers(self, blockers) -> None:
extant_blockers = (
filename
for filename in blockers
@@ -234,13 +233,12 @@ class easy_install(Command):
"""
Render the Setuptools version and installation details, then exit.
"""
- ver = '{}.{}'.format(*sys.version_info)
+ ver = f'{sys.version_info.major}.{sys.version_info.minor}'
dist = get_distribution('setuptools')
- tmpl = 'setuptools {dist.version} from {dist.location} (Python {ver})'
- print(tmpl.format(**locals()))
+ print(f'setuptools {dist.version} from {dist.location} (Python {ver})')
raise SystemExit
- def finalize_options(self): # noqa: C901 # is too complex (25) # FIXME
+ def finalize_options(self) -> None: # noqa: C901 # is too complex (25) # FIXME
self.version and self._render_version()
py_version = sys.version.split()[0]
@@ -356,7 +354,7 @@ class easy_install(Command):
"No urls, filenames, or requirements specified (see --help)"
)
- self.outputs = []
+ self.outputs: list[str] = []
@staticmethod
def _process_site_dirs(site_dirs):
@@ -408,12 +406,12 @@ class easy_install(Command):
val = subst_vars(val, self.config_vars)
setattr(self, attr, val)
- def expand_basedirs(self):
+ def expand_basedirs(self) -> None:
"""Calls `os.path.expanduser` on install_base, install_platbase and
root."""
self._expand_attrs(['install_base', 'install_platbase', 'root'])
- def expand_dirs(self):
+ def expand_dirs(self) -> None:
"""Calls `os.path.expanduser` on install dirs."""
dirs = [
'install_purelib',
@@ -425,7 +423,7 @@ class easy_install(Command):
]
self._expand_attrs(dirs)
- def run(self, show_deprecation=True):
+ def run(self, show_deprecation: bool = True) -> None:
if show_deprecation:
self.announce(
"WARNING: The easy_install command is deprecated "
@@ -465,10 +463,10 @@ class easy_install(Command):
pid = random.randint(0, sys.maxsize)
return os.path.join(self.install_dir, "test-easy-install-%s" % pid)
- def warn_deprecated_options(self):
+ def warn_deprecated_options(self) -> None:
pass
- def check_site_dir(self): # is too complex (12) # FIXME
+ def check_site_dir(self) -> None: # is too complex (12) # FIXME
"""Verify that self.install_dir is .pth-capable dir, if needed"""
instdir = normalize_path(self.install_dir)
@@ -555,7 +553,7 @@ class easy_install(Command):
"""
).lstrip()
- def cant_write_to_target(self):
+ def cant_write_to_target(self) -> NoReturn:
msg = self.__cant_write_msg % (
sys.exc_info()[1],
self.install_dir,
@@ -627,7 +625,7 @@ class easy_install(Command):
log.warn("TEST FAILED: %s does NOT support .pth files", instdir)
return False
- def install_egg_scripts(self, dist):
+ def install_egg_scripts(self, dist) -> None:
"""Write all the scripts for `dist`, unless scripts are excluded"""
if not self.exclude_scripts and dist.metadata_isdir('scripts'):
for script_name in dist.metadata_listdir('scripts'):
@@ -640,7 +638,7 @@ class easy_install(Command):
)
self.install_wrapper_scripts(dist)
- def add_output(self, path):
+ def add_output(self, path) -> None:
if os.path.isdir(path):
for base, dirs, files in os.walk(path):
for filename in files:
@@ -648,14 +646,14 @@ class easy_install(Command):
else:
self.outputs.append(path)
- def not_editable(self, spec):
+ def not_editable(self, spec) -> None:
if self.editable:
raise DistutilsArgError(
"Invalid argument %r: you can't use filenames or URLs "
"with --editable (except via the --find-links option)." % (spec,)
)
- def check_editable(self, spec):
+ def check_editable(self, spec) -> None:
if not self.editable:
return
@@ -674,7 +672,7 @@ class easy_install(Command):
finally:
os.path.exists(tmpdir) and _rmtree(tmpdir)
- def easy_install(self, spec, deps=False):
+ def easy_install(self, spec, deps: bool = False) -> Distribution | None:
with self._tmpdir() as tmpdir:
if not isinstance(spec, Requirement):
if URL_SCHEME(spec):
@@ -711,9 +709,11 @@ class easy_install(Command):
else:
return self.install_item(spec, dist.location, tmpdir, deps)
- def install_item(self, spec, download, tmpdir, deps, install_needed=False):
+ def install_item(
+ self, spec, download, tmpdir, deps, install_needed: bool = False
+ ) -> Distribution | None:
# Installation is also needed if file in tmpdir or is not an egg
- install_needed = install_needed or self.always_copy
+ install_needed = install_needed or bool(self.always_copy)
install_needed = install_needed or os.path.dirname(download) == tmpdir
install_needed = install_needed or not download.endswith('.egg')
install_needed = install_needed or (
@@ -759,9 +759,9 @@ class easy_install(Command):
self,
requirement,
dist,
- deps=True,
+ deps: bool = True,
*info,
- ):
+ ) -> None:
self.update_pth(dist)
self.package_index.add(dist)
if dist in self.local_index[dist.key]:
@@ -799,7 +799,7 @@ class easy_install(Command):
self.easy_install(dist.as_requirement())
log.info("Finished processing dependencies for %s", requirement)
- def should_unzip(self, dist):
+ def should_unzip(self, dist) -> bool:
if self.zip_ok is not None:
return not self.zip_ok
if dist.has_metadata('not-zip-safe'):
@@ -829,13 +829,13 @@ class easy_install(Command):
shutil.move(setup_base, dst)
return dst
- def install_wrapper_scripts(self, dist):
+ def install_wrapper_scripts(self, dist) -> None:
if self.exclude_scripts:
return
for args in ScriptWriter.best().get_args(dist):
self.write_script(*args)
- def install_script(self, dist, script_name, script_text, dev_path=None):
+ def install_script(self, dist, script_name, script_text, dev_path=None) -> None:
"""Generate a legacy script wrapper and install it"""
spec = str(dist.as_requirement())
is_script = is_python_script(script_text, script_name)
@@ -860,7 +860,7 @@ class easy_install(Command):
raw_bytes = resource_string('setuptools', name)
return raw_bytes.decode('utf-8')
- def write_script(self, script_name, contents, mode="t", blockers=()):
+ def write_script(self, script_name, contents, mode: str = "t", blockers=()) -> None:
"""Write an executable file to the scripts directory"""
self.delete_blockers( # clean up old .py/.pyw w/o a script
[os.path.join(self.script_dir, x) for x in blockers]
@@ -882,7 +882,7 @@ class easy_install(Command):
f.write(contents)
chmod(target, 0o777 - mask)
- def install_eggs(self, spec, dist_filename, tmpdir):
+ def install_eggs(self, spec, dist_filename, tmpdir) -> list[Distribution]:
# .egg dirs or files are already built, so just return them
installer_map = {
'.egg': self.install_egg,
@@ -1043,7 +1043,7 @@ class easy_install(Command):
return self.install_egg(egg_path, tmpdir)
# FIXME: 'easy_install.exe_to_egg' is too complex (12)
- def exe_to_egg(self, dist_filename, egg_tmp): # noqa: C901
+ def exe_to_egg(self, dist_filename, egg_tmp) -> None: # noqa: C901
"""Extract a bdist_wininst to the directories an egg would use"""
# Check for .pth file and set up prefix translations
prefixes = get_exe_prefixes(dist_filename)
@@ -1143,7 +1143,7 @@ class easy_install(Command):
"""
)
- def installation_report(self, req, dist, what="Installed"):
+ def installation_report(self, req, dist, what: str = "Installed") -> str:
"""Helpful installation message for display to package users"""
msg = "\n%(what)s %(eggloc)s%(extras)s"
if self.multi_version and not self.no_report:
@@ -1175,7 +1175,7 @@ class easy_install(Command):
python = sys.executable
return '\n' + self.__editable_msg % locals()
- def run_setup(self, setup_script, setup_base, args):
+ def run_setup(self, setup_script, setup_base, args) -> None:
sys.modules.setdefault('distutils.command.bdist_egg', bdist_egg)
sys.modules.setdefault('distutils.command.egg_info', egg_info)
@@ -1244,7 +1244,7 @@ class easy_install(Command):
cfg_filename = os.path.join(base, 'setup.cfg')
setopt.edit_config(cfg_filename, settings)
- def update_pth(self, dist): # noqa: C901 # is too complex (11) # FIXME
+ def update_pth(self, dist) -> None: # noqa: C901 # is too complex (11) # FIXME
if self.pth_file is None:
return
@@ -1293,7 +1293,7 @@ class easy_install(Command):
log.debug("Unpacking %s to %s", src, dst)
return dst # only unpack-and-compile skips files for dry run
- def unpack_and_compile(self, egg_path, destination):
+ def unpack_and_compile(self, egg_path, destination) -> None:
to_compile = []
to_chmod = []
@@ -1312,7 +1312,7 @@ class easy_install(Command):
mode = ((os.stat(f)[stat.ST_MODE]) | 0o555) & 0o7755
chmod(f, mode)
- def byte_compile(self, to_compile):
+ def byte_compile(self, to_compile) -> None:
if sys.dont_write_bytecode:
return
@@ -1367,7 +1367,7 @@ class easy_install(Command):
"""
).strip()
- def create_home_path(self):
+ def create_home_path(self) -> None:
"""Create directories under ~."""
if not self.user:
return
@@ -1441,7 +1441,7 @@ def get_site_dirs():
os.path.join(
prefix,
"lib",
- "python{}.{}".format(*sys.version_info),
+ f"python{sys.version_info.major}.{sys.version_info.minor}",
"site-packages",
),
os.path.join(prefix, "lib", "site-python"),
@@ -1468,7 +1468,7 @@ def get_site_dirs():
home,
'Library',
'Python',
- '{}.{}'.format(*sys.version_info),
+ f'{sys.version_info.major}.{sys.version_info.minor}',
'site-packages',
)
sitedirs.append(home_sp)
@@ -1546,7 +1546,7 @@ def extract_wininst_cfg(dist_filename):
return None
f.seek(prepended - 12)
- tag, cfglen, bmlen = struct.unpack("<iii", f.read(12))
+ tag, cfglen, _bmlen = struct.unpack("<iii", f.read(12))
if tag not in (0x1234567A, 0x1234567B):
return None # not a valid tag
@@ -1611,7 +1611,7 @@ def get_exe_prefixes(exe_filename):
class PthDistributions(Environment):
"""A .pth file with Distribution paths in it"""
- def __init__(self, filename, sitedirs=()):
+ def __init__(self, filename, sitedirs=()) -> None:
self.filename = filename
self.sitedirs = list(map(normalize_path, sitedirs))
self.basedir = normalize_path(os.path.dirname(self.filename))
@@ -1657,7 +1657,7 @@ class PthDistributions(Environment):
return self._load_raw()
return [], False
- def save(self):
+ def save(self) -> None:
"""Write changed .pth file back to disk"""
# first reload the file
last_paths, last_dirty = self._load()
@@ -1711,7 +1711,7 @@ class PthDistributions(Environment):
def _wrap_lines(lines):
return lines
- def add(self, dist):
+ def add(self, dist) -> None:
"""Add `dist` to the distribution map"""
new_path = dist.location not in self.paths and (
dist.location not in self.sitedirs
@@ -1724,7 +1724,7 @@ class PthDistributions(Environment):
self.dirty = True
super().add(dist)
- def remove(self, dist):
+ def remove(self, dist) -> None:
"""Remove `dist` from the distribution map"""
while dist.location in self.paths:
self.paths.remove(dist.location)
@@ -1788,16 +1788,6 @@ def _first_line_re():
return re.compile(first_line_re.pattern.decode())
-# Must match shutil._OnExcCallback
-def auto_chmod(func: Callable[..., _T], arg: str, exc: BaseException) -> _T:
- """shutils onexc callback to automatically call chmod for certain functions."""
- # Only retry for scenarios known to have an issue
- if func in [os.unlink, os.remove] and os.name == 'nt':
- chmod(arg, stat.S_IWRITE)
- return func(arg)
- raise exc
-
-
def update_dist_caches(dist_path, fix_zipimporter_caches):
"""
Fix any globally cached `dist_path` related data
@@ -2020,22 +2010,9 @@ def is_python_script(script_text, filename):
return False # Not any Python I can recognize
-try:
- from os import (
- chmod as _chmod, # pyright: ignore[reportAssignmentType] # Loosing type-safety w/ pyright, but that's ok
- )
-except ImportError:
- # Jython compatibility
- def _chmod(*args: object, **kwargs: object) -> None: # type: ignore[misc] # Mypy re-uses the imported definition anyway
- pass
-
-
-def chmod(path, mode):
- log.debug("changing mode of %s to %o", path, mode)
- try:
- _chmod(path, mode)
- except OSError as e:
- log.debug("chmod failed: %s", e)
+class _SplitArgs(TypedDict, total=False):
+ comments: bool
+ posix: bool
class CommandSpec(list):
@@ -2045,7 +2022,7 @@ class CommandSpec(list):
"""
options: list[str] = []
- split_args: dict[str, bool] = dict()
+ split_args = _SplitArgs()
@classmethod
def best(cls):
@@ -2080,7 +2057,7 @@ class CommandSpec(list):
return cls([cls._sys_executable()])
@classmethod
- def from_string(cls, string):
+ def from_string(cls, string: str) -> Self:
"""
Construct a command spec from a simple string representing a command
line parseable by shlex.split.
@@ -2088,7 +2065,7 @@ class CommandSpec(list):
items = shlex.split(string, **cls.split_args)
return cls(items)
- def install_options(self, script_text):
+ def install_options(self, script_text: str):
self.options = shlex.split(self._extract_options(script_text))
cmdline = subprocess.list2cmdline(self)
if not isascii(cmdline):
@@ -2128,7 +2105,7 @@ sys_executable = CommandSpec._sys_executable()
class WindowsCommandSpec(CommandSpec):
- split_args = dict(posix=False)
+ split_args = _SplitArgs(posix=False)
class ScriptWriter:
@@ -2187,7 +2164,7 @@ class ScriptWriter:
spec = str(dist.as_requirement())
for type_ in 'console', 'gui':
group = type_ + '_scripts'
- for name, ep in dist.get_entry_map(group).items():
+ for name in dist.get_entry_map(group).keys():
cls._ensure_safe_name(name)
script_text = cls.template % locals()
args = cls._get_script_args(type_, name, header, script_text)
@@ -2218,7 +2195,11 @@ class ScriptWriter:
yield (name, header + script_text)
@classmethod
- def get_header(cls, script_text="", executable=None):
+ def get_header(
+ cls,
+ script_text: str = "",
+ executable: str | CommandSpec | Iterable[str] | None = None,
+ ) -> str:
"""Create a #! line, getting options (if any) from script_text"""
cmd = cls.command_spec_class.best().from_param(executable)
cmd.install_options(script_text)
@@ -2340,10 +2321,6 @@ def load_launcher_manifest(name):
return manifest.decode('utf-8') % vars()
-def _rmtree(path, ignore_errors=False, onexc=auto_chmod):
- return py311.shutil_rmtree(path, ignore_errors, onexc)
-
-
def current_umask():
tmp = os.umask(0o022)
os.umask(tmp)
diff --git a/contrib/python/setuptools/py3/setuptools/command/editable_wheel.py b/contrib/python/setuptools/py3/setuptools/command/editable_wheel.py
index 2b21eacbad..6d23d11fad 100644
--- a/contrib/python/setuptools/py3/setuptools/command/editable_wheel.py
+++ b/contrib/python/setuptools/py3/setuptools/command/editable_wheel.py
@@ -17,6 +17,7 @@ import logging
import os
import shutil
import traceback
+from collections.abc import Iterable, Iterator, Mapping
from contextlib import suppress
from enum import Enum
from inspect import cleandoc
@@ -24,9 +25,9 @@ from itertools import chain, starmap
from pathlib import Path
from tempfile import TemporaryDirectory
from types import TracebackType
-from typing import TYPE_CHECKING, Iterable, Iterator, Mapping, Protocol, TypeVar, cast
+from typing import TYPE_CHECKING, Protocol, TypeVar, cast
-from .. import Command, _normalization, _path, errors, namespaces
+from .. import Command, _normalization, _path, _shutil, errors, namespaces
from .._path import StrPath
from ..compat import py312
from ..discovery import find_package_path
@@ -119,13 +120,13 @@ class editable_wheel(Command):
self.project_dir = None
self.mode = None
- def finalize_options(self):
+ def finalize_options(self) -> None:
dist = self.distribution
self.project_dir = dist.src_root or os.curdir
self.package_dir = dist.package_dir or {}
self.dist_dir = Path(self.dist_dir or os.path.join(self.project_dir, "dist"))
- def run(self):
+ def run(self) -> None:
try:
self.dist_dir.mkdir(exist_ok=True)
self._ensure_dist_info()
@@ -138,7 +139,6 @@ class editable_wheel(Command):
self._create_wheel_file(bdist_wheel)
except Exception:
traceback.print_exc()
- # TODO: Fix false-positive [attr-defined] in typeshed
project = self.distribution.name or self.distribution.get_name()
_DebuggingTips.emit(project=project)
raise
@@ -231,7 +231,6 @@ class editable_wheel(Command):
"""Set the ``editable_mode`` flag in the build sub-commands"""
dist = self.distribution
build = dist.get_command_obj("build")
- # TODO: Update typeshed distutils stubs to overload non-None return type by default
for cmd_name in build.get_sub_commands():
cmd = dist.get_command_obj(cmd_name)
if hasattr(cmd, "editable_mode"):
@@ -380,26 +379,25 @@ class editable_wheel(Command):
class EditableStrategy(Protocol):
- def __call__(self, wheel: WheelFile, files: list[str], mapping: dict[str, str]): ...
-
+ def __call__(
+ self, wheel: WheelFile, files: list[str], mapping: Mapping[str, str]
+ ) -> object: ...
def __enter__(self) -> Self: ...
-
def __exit__(
self,
- exc_type: type[BaseException] | None,
- exc_value: BaseException | None,
- traceback: TracebackType | None,
- /,
+ _exc_type: type[BaseException] | None,
+ _exc_value: BaseException | None,
+ _traceback: TracebackType | None,
) -> object: ...
class _StaticPth:
- def __init__(self, dist: Distribution, name: str, path_entries: list[Path]):
+ def __init__(self, dist: Distribution, name: str, path_entries: list[Path]) -> None:
self.dist = dist
self.name = name
self.path_entries = path_entries
- def __call__(self, wheel: WheelFile, files: list[str], mapping: dict[str, str]):
+ def __call__(self, wheel: WheelFile, files: list[str], mapping: Mapping[str, str]):
entries = "\n".join(str(p.resolve()) for p in self.path_entries)
contents = _encode_pth(f"{entries}\n")
wheel.writestr(f"__editable__.{self.name}.pth", contents)
@@ -438,13 +436,13 @@ class _LinkTree(_StaticPth):
name: str,
auxiliary_dir: StrPath,
build_lib: StrPath,
- ):
+ ) -> None:
self.auxiliary_dir = Path(auxiliary_dir)
self.build_lib = Path(build_lib).resolve()
self._file = dist.get_command_obj("build_py").copy_file
super().__init__(dist, name, [self.auxiliary_dir])
- def __call__(self, wheel: WheelFile, files: list[str], mapping: dict[str, str]):
+ def __call__(self, wheel: WheelFile, files: list[str], mapping: Mapping[str, str]):
self._create_links(files, mapping)
super().__call__(wheel, files, mapping)
@@ -461,7 +459,7 @@ class _LinkTree(_StaticPth):
dest.parent.mkdir(parents=True)
self._file(src_file, dest, link=link)
- def _create_links(self, outputs, output_mapping):
+ def _create_links(self, outputs, output_mapping: Mapping[str, str]):
self.auxiliary_dir.mkdir(parents=True, exist_ok=True)
link_type = "sym" if _can_symlink_files(self.auxiliary_dir) else "hard"
normalised = ((self._normalize_output(k), v) for k, v in output_mapping.items())
@@ -498,7 +496,7 @@ class _LinkTree(_StaticPth):
class _TopLevelFinder:
- def __init__(self, dist: Distribution, name: str):
+ def __init__(self, dist: Distribution, name: str) -> None:
self.dist = dist
self.name = name
@@ -538,7 +536,7 @@ class _TopLevelFinder:
content = _encode_pth(f"import {finder}; {finder}.install()")
yield (f"__editable__.{self.name}.pth", content)
- def __call__(self, wheel: WheelFile, files: list[str], mapping: dict[str, str]):
+ def __call__(self, wheel: WheelFile, files: list[str], mapping: Mapping[str, str]):
for file, content in self.get_implementation():
wheel.writestr(file, content)
@@ -775,18 +773,18 @@ def _is_nested(pkg: str, pkg_path: str, parent: str, parent_path: str) -> bool:
def _empty_dir(dir_: _P) -> _P:
"""Create a directory ensured to be empty. Existing files may be removed."""
- shutil.rmtree(dir_, ignore_errors=True)
+ _shutil.rmtree(dir_, ignore_errors=True)
os.makedirs(dir_)
return dir_
class _NamespaceInstaller(namespaces.Installer):
- def __init__(self, distribution, installation_dir, editable_name, src_root):
+ def __init__(self, distribution, installation_dir, editable_name, src_root) -> None:
self.distribution = distribution
self.src_root = src_root
self.installation_dir = installation_dir
self.editable_name = editable_name
- self.outputs = []
+ self.outputs: list[str] = []
self.dry_run = False
def _get_nspkg_file(self):
diff --git a/contrib/python/setuptools/py3/setuptools/command/egg_info.py b/contrib/python/setuptools/py3/setuptools/command/egg_info.py
index 280eb5e807..a300356d33 100644
--- a/contrib/python/setuptools/py3/setuptools/command/egg_info.py
+++ b/contrib/python/setuptools/py3/setuptools/command/egg_info.py
@@ -2,12 +2,12 @@
Create a distribution's .egg-info directory and contents"""
-import collections
import functools
import os
import re
import sys
import time
+from collections.abc import Callable
import packaging
import packaging.requirements
@@ -32,7 +32,7 @@ from distutils.errors import DistutilsInternalError
from distutils.filelist import FileList as _FileList
from distutils.util import convert_path
-PY_MAJOR = '{}.{}'.format(*sys.version_info)
+PY_MAJOR = f'{sys.version_info.major}.{sys.version_info.minor}'
def translate_pattern(glob): # noqa: C901 # is too complex (14) # FIXME
@@ -196,7 +196,7 @@ class egg_info(InfoCommon, Command):
# allow the 'tag_svn_revision' to be detected and
# set, supporting sdists built on older Setuptools.
@property
- def tag_svn_revision(self):
+ def tag_svn_revision(self) -> None:
pass
@tag_svn_revision.setter
@@ -205,20 +205,18 @@ class egg_info(InfoCommon, Command):
####################################
- def save_version_info(self, filename):
+ def save_version_info(self, filename) -> None:
"""
Materialize the value of date into the
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):
+ def finalize_options(self) -> None:
# Note: we need to capture the current value returned
# by `self.tagged_version()`, so we can later update
# `self.distribution.metadata.version` without
@@ -255,7 +253,7 @@ class egg_info(InfoCommon, Command):
"""Compute filename of the output egg. Private API."""
return _egg_basename(self.egg_name, self.egg_version, py_version, platform)
- def write_or_delete_file(self, what, filename, data, force=False):
+ def write_or_delete_file(self, what, filename, data, force: bool = False) -> None:
"""Write `data` to `filename` or delete if empty
If `data` is non-empty, this routine is the same as ``write_file()``.
@@ -273,7 +271,7 @@ class egg_info(InfoCommon, Command):
else:
self.delete_file(filename)
- def write_file(self, what, filename, data):
+ def write_file(self, what, filename, data) -> None:
"""Write `data` to `filename` (if not a dry run) after announcing it
`what` is used in a log message to identify what is being written
@@ -286,20 +284,24 @@ class egg_info(InfoCommon, Command):
f.write(data)
f.close()
- def delete_file(self, filename):
+ def delete_file(self, filename) -> None:
"""Delete `filename` (if not a dry run) after announcing it"""
log.info("deleting %s", filename)
if not self.dry_run:
os.unlink(filename)
- def run(self):
+ def run(self) -> None:
+ # Pre-load to avoid iterating over entry-points while an empty .egg-info
+ # exists in sys.path. See pypa/pyproject-hooks#206
+ writers = list(metadata.entry_points(group='egg_info.writers'))
+
self.mkpath(self.egg_info)
try:
os.utime(self.egg_info, None)
except OSError as e:
msg = f"Cannot update time stamp of directory '{self.egg_info}'"
raise distutils.errors.DistutilsFileError(msg) from e
- for ep in metadata.entry_points(group='egg_info.writers'):
+ for ep in writers:
writer = ep.load()
writer(self, ep.name, os.path.join(self.egg_info, ep.name))
@@ -310,7 +312,7 @@ class egg_info(InfoCommon, Command):
self.find_sources()
- def find_sources(self):
+ def find_sources(self) -> None:
"""Generate SOURCES.txt manifest file"""
manifest_filename = os.path.join(self.egg_info, "SOURCES.txt")
mm = manifest_maker(self.distribution)
@@ -323,11 +325,13 @@ class egg_info(InfoCommon, Command):
class FileList(_FileList):
# Implementations of the various MANIFEST.in commands
- def __init__(self, warn=None, debug_print=None, ignore_egg_info_dir=False):
+ def __init__(
+ self, warn=None, debug_print=None, ignore_egg_info_dir: bool = False
+ ) -> None:
super().__init__(warn, debug_print)
self.ignore_egg_info_dir = ignore_egg_info_dir
- def process_template_line(self, line):
+ def process_template_line(self, line) -> None:
# Parse the line: split it up, make sure the right number of words
# is there, and return the relevant words. 'action' is always
# defined: it's the first word of the line. Which of the other
@@ -335,7 +339,7 @@ class FileList(_FileList):
# patterns, (dir and patterns), or (dir_pattern).
(action, patterns, dir, dir_pattern) = self._parse_template_line(line)
- action_map = {
+ action_map: dict[str, Callable] = {
'include': self.include,
'exclude': self.exclude,
'global-include': self.global_include,
@@ -471,7 +475,7 @@ class FileList(_FileList):
match = translate_pattern(os.path.join('**', pattern))
return self._remove_files(match.match)
- def append(self, item):
+ def append(self, item) -> None:
if item.endswith('\r'): # Fix older sdists built on Windows
item = item[:-1]
path = convert_path(item)
@@ -479,7 +483,7 @@ class FileList(_FileList):
if self._safe_path(path):
self.files.append(path)
- def extend(self, paths):
+ def extend(self, paths) -> None:
self.files.extend(filter(self._safe_path, paths))
def _repair(self):
@@ -523,17 +527,17 @@ class FileList(_FileList):
class manifest_maker(sdist):
template = "MANIFEST.in"
- def initialize_options(self):
+ def initialize_options(self) -> None:
self.use_defaults = True
self.prune = True
self.manifest_only = True
self.force_manifest = True
self.ignore_egg_info_dir = False
- def finalize_options(self):
+ def finalize_options(self) -> None:
pass
- def run(self):
+ def run(self) -> None:
self.filelist = FileList(ignore_egg_info_dir=self.ignore_egg_info_dir)
if not os.path.exists(self.manifest):
self.write_manifest() # it must exist so it'll get in the list
@@ -551,7 +555,7 @@ class manifest_maker(sdist):
path = unicode_utils.filesys_decode(path)
return path.replace(os.sep, '/')
- def write_manifest(self):
+ def write_manifest(self) -> None:
"""
Write the file list in 'self.filelist' to the manifest file
named by 'self.manifest'.
@@ -563,7 +567,7 @@ class manifest_maker(sdist):
msg = "writing manifest file '%s'" % self.manifest
self.execute(write_file, (self.manifest, files), msg)
- def warn(self, msg):
+ def warn(self, msg) -> None:
if not self._should_suppress_warning(msg):
sdist.warn(self, msg)
@@ -574,7 +578,7 @@ class manifest_maker(sdist):
"""
return re.match(r"standard file .*not found", msg)
- def add_defaults(self):
+ def add_defaults(self) -> None:
sdist.add_defaults(self)
self.filelist.append(self.template)
self.filelist.append(self.manifest)
@@ -592,7 +596,7 @@ class manifest_maker(sdist):
ei_cmd = self.get_finalized_command('egg_info')
self.filelist.graft(ei_cmd.egg_info)
- def add_license_files(self):
+ def add_license_files(self) -> None:
license_files = self.distribution.metadata.license_files or []
for lf in license_files:
log.info("adding license file '%s'", lf)
@@ -631,7 +635,7 @@ class manifest_maker(sdist):
return build_py.get_data_files()
-def write_file(filename, contents):
+def write_file(filename, contents) -> None:
"""Create a file with the specified name and write 'contents' (a
sequence of strings without line terminators) to it.
"""
@@ -644,7 +648,7 @@ def write_file(filename, contents):
f.write(contents)
-def write_pkg_info(cmd, basename, filename):
+def write_pkg_info(cmd, basename, filename) -> None:
log.info("writing %s", filename)
if not cmd.dry_run:
metadata = cmd.distribution.metadata
@@ -663,7 +667,7 @@ def write_pkg_info(cmd, basename, filename):
bdist_egg.write_safety_flag(cmd.egg_info, safe)
-def warn_depends_obsolete(cmd, basename, filename):
+def warn_depends_obsolete(cmd, basename, filename) -> None:
"""
Unused: left to avoid errors when updating (from source) from <= 67.8.
Old installations have a .dist-info directory with the entry-point
@@ -678,18 +682,18 @@ write_requirements = _requirestxt.write_requirements
write_setup_requirements = _requirestxt.write_setup_requirements
-def write_toplevel_names(cmd, basename, filename):
+def write_toplevel_names(cmd, basename, filename) -> None:
pkgs = dict.fromkeys([
k.split('.', 1)[0] for k in cmd.distribution.iter_distribution_names()
])
cmd.write_file("top-level names", filename, '\n'.join(sorted(pkgs)) + '\n')
-def overwrite_arg(cmd, basename, filename):
+def overwrite_arg(cmd, basename, filename) -> None:
write_arg(cmd, basename, filename, True)
-def write_arg(cmd, basename, filename, force=False):
+def write_arg(cmd, basename, filename, force: bool = False) -> None:
argname = os.path.splitext(basename)[0]
value = getattr(cmd.distribution, argname, None)
if value is not None:
@@ -697,7 +701,7 @@ def write_arg(cmd, basename, filename, force=False):
cmd.write_or_delete_file(argname, filename, value, force)
-def write_entries(cmd, basename, filename):
+def write_entries(cmd, basename, filename) -> None:
eps = _entry_points.load(cmd.distribution.entry_points)
defn = _entry_points.render(eps)
cmd.write_or_delete_file('entry points', filename, defn, True)
diff --git a/contrib/python/setuptools/py3/setuptools/command/install.py b/contrib/python/setuptools/py3/setuptools/command/install.py
index e2ec1abdde..741b140c70 100644
--- a/contrib/python/setuptools/py3/setuptools/command/install.py
+++ b/contrib/python/setuptools/py3/setuptools/command/install.py
@@ -4,7 +4,7 @@ import glob
import inspect
import platform
from collections.abc import Callable
-from typing import Any, ClassVar, cast
+from typing import TYPE_CHECKING, Any, ClassVar, cast
import setuptools
@@ -15,9 +15,22 @@ from .bdist_egg import bdist_egg as bdist_egg_cls
import distutils.command.install as orig
from distutils.errors import DistutilsArgError
-# Prior to numpy 1.9, NumPy relies on the '_install' name, so provide it for
-# now. See https://github.com/pypa/setuptools/issues/199/
-_install = orig.install
+if TYPE_CHECKING:
+ # This is only used for a type-cast, don't import at runtime or it'll cause deprecation warnings
+ from .easy_install import easy_install as easy_install_cls
+else:
+ easy_install_cls = None
+
+
+def __getattr__(name: str): # pragma: no cover
+ if name == "_install":
+ SetuptoolsDeprecationWarning.emit(
+ "`setuptools.command._install` was an internal implementation detail "
+ + "that was left in for numpy<1.9 support.",
+ due_date=(2025, 5, 2), # Originally added on 2024-11-01
+ )
+ return orig.install
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
class install(orig.install):
@@ -63,15 +76,14 @@ class install(orig.install):
self.old_and_unmanageable = None
self.single_version_externally_managed = None
- def finalize_options(self):
+ def finalize_options(self) -> None:
super().finalize_options()
if self.root:
self.single_version_externally_managed = True
elif self.single_version_externally_managed:
if not self.root and not self.record:
raise DistutilsArgError(
- "You must specify --record or --root when building system"
- " packages"
+ "You must specify --record or --root when building system packages"
)
def handle_extra_path(self):
@@ -131,14 +143,20 @@ class install(orig.install):
return False
- def do_egg_install(self):
+ def do_egg_install(self) -> None:
easy_install = self.distribution.get_command_class('easy_install')
- cmd = easy_install(
- self.distribution,
- args="x",
- root=self.root,
- record=self.record,
+ cmd = cast(
+ # We'd want to cast easy_install as type[easy_install_cls] but a bug in
+ # mypy makes it think easy_install() returns a Command on Python 3.12+
+ # https://github.com/python/mypy/issues/18088
+ easy_install_cls,
+ easy_install( # type: ignore[call-arg]
+ self.distribution,
+ args="x",
+ root=self.root,
+ record=self.record,
+ ),
)
cmd.ensure_finalized() # finalize before bdist_egg munges install cmd
cmd.always_copy_from = '.' # make sure local-dir eggs get installed
diff --git a/contrib/python/setuptools/py3/setuptools/command/install_egg_info.py b/contrib/python/setuptools/py3/setuptools/command/install_egg_info.py
index be47254f00..a6e6ec6446 100644
--- a/contrib/python/setuptools/py3/setuptools/command/install_egg_info.py
+++ b/contrib/python/setuptools/py3/setuptools/command/install_egg_info.py
@@ -20,15 +20,15 @@ class install_egg_info(namespaces.Installer, Command):
def initialize_options(self):
self.install_dir = None
- def finalize_options(self):
+ def finalize_options(self) -> None:
self.set_undefined_options('install_lib', ('install_dir', 'install_dir'))
ei_cmd = self.get_finalized_command("egg_info")
basename = f"{ei_cmd._get_egg_basename()}.egg-info"
self.source = ei_cmd.egg_info
self.target = os.path.join(self.install_dir, basename)
- self.outputs = []
+ self.outputs: list[str] = []
- def run(self):
+ def run(self) -> None:
self.run_command('egg_info')
if os.path.isdir(self.target) and not os.path.islink(self.target):
dir_util.remove_tree(self.target, dry_run=self.dry_run)
@@ -42,7 +42,7 @@ class install_egg_info(namespaces.Installer, Command):
def get_outputs(self):
return self.outputs
- def copytree(self):
+ def copytree(self) -> None:
# Copy the .egg-info tree to site-packages
def skimmer(src, dst):
# filter out source-control directories; note that 'src' is always
diff --git a/contrib/python/setuptools/py3/setuptools/command/install_lib.py b/contrib/python/setuptools/py3/setuptools/command/install_lib.py
index 292f07ab6e..8e1e072710 100644
--- a/contrib/python/setuptools/py3/setuptools/command/install_lib.py
+++ b/contrib/python/setuptools/py3/setuptools/command/install_lib.py
@@ -15,7 +15,7 @@ class install_lib(orig.install_lib):
distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution
- def run(self):
+ def run(self) -> None:
self.build()
outfiles = self.install()
if outfiles is not None:
@@ -52,7 +52,7 @@ class install_lib(orig.install_lib):
"""
while pkg_name:
yield pkg_name
- pkg_name, sep, child = pkg_name.rpartition('.')
+ pkg_name, _sep, _child = pkg_name.rpartition('.')
def _get_SVEM_NSPs(self):
"""
@@ -95,12 +95,15 @@ class install_lib(orig.install_lib):
self,
infile: StrPath,
outfile: str,
- preserve_mode=True,
- preserve_times=True,
- preserve_symlinks=False,
- level=1,
+ # override: Using actual booleans
+ preserve_mode: bool = True, # type: ignore[override]
+ preserve_times: bool = True, # type: ignore[override]
+ preserve_symlinks: bool = False, # type: ignore[override]
+ level: object = 1,
) -> list[str]:
- assert preserve_mode and preserve_times and not preserve_symlinks
+ assert preserve_mode
+ assert preserve_times
+ assert not preserve_symlinks
exclude = self.get_exclusions()
if not exclude:
diff --git a/contrib/python/setuptools/py3/setuptools/command/install_scripts.py b/contrib/python/setuptools/py3/setuptools/command/install_scripts.py
index 7b90611d1c..4401cf693d 100644
--- a/contrib/python/setuptools/py3/setuptools/command/install_scripts.py
+++ b/contrib/python/setuptools/py3/setuptools/command/install_scripts.py
@@ -15,7 +15,7 @@ class install_scripts(orig.install_scripts):
distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution
- def initialize_options(self):
+ def initialize_options(self) -> None:
orig.install_scripts.initialize_options(self)
self.no_ep = False
@@ -56,7 +56,7 @@ class install_scripts(orig.install_scripts):
for args in writer.get_args(dist, cmd.as_header()):
self.write_script(*args)
- def write_script(self, script_name, contents, mode="t", *ignored):
+ def write_script(self, script_name, contents, mode: str = "t", *ignored) -> None:
"""Write an executable file to the scripts directory"""
from setuptools.command.easy_install import chmod, current_umask
diff --git a/contrib/python/setuptools/py3/setuptools/command/register.py b/contrib/python/setuptools/py3/setuptools/command/register.py
deleted file mode 100644
index 93ef04aa0e..0000000000
--- a/contrib/python/setuptools/py3/setuptools/command/register.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from setuptools.errors import RemovedCommandError
-
-from ..dist import Distribution
-
-import distutils.command.register as orig
-from distutils import log
-
-
-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 "
- "instead (https://pypi.org/p/twine)"
- )
-
- self.announce("ERROR: " + msg, log.ERROR)
-
- raise RemovedCommandError(msg)
diff --git a/contrib/python/setuptools/py3/setuptools/command/rotate.py b/contrib/python/setuptools/py3/setuptools/command/rotate.py
index dcdfafbcf7..acdce07baa 100644
--- a/contrib/python/setuptools/py3/setuptools/command/rotate.py
+++ b/contrib/python/setuptools/py3/setuptools/command/rotate.py
@@ -1,9 +1,9 @@
from __future__ import annotations
import os
-import shutil
+from typing import ClassVar
-from setuptools import Command
+from .. import Command, _shutil
from distutils import log
from distutils.errors import DistutilsOptionError
@@ -20,14 +20,14 @@ class rotate(Command):
('keep=', 'k', "number of matching distributions to keep"),
]
- boolean_options: list[str] = []
+ boolean_options: ClassVar[list[str]] = []
def initialize_options(self):
self.match = None
self.dist_dir = None
self.keep = None
- def finalize_options(self):
+ def finalize_options(self) -> None:
if self.match is None:
raise DistutilsOptionError(
"Must specify one or more (comma-separated) match patterns "
@@ -43,7 +43,7 @@ class rotate(Command):
self.match = [convert_path(p.strip()) for p in self.match.split(',')]
self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
- def run(self):
+ def run(self) -> None:
self.run_command("egg_info")
from glob import glob
@@ -60,6 +60,6 @@ class rotate(Command):
log.info("Deleting %s", f)
if not self.dry_run:
if os.path.isdir(f):
- shutil.rmtree(f)
+ _shutil.rmtree(f)
else:
os.unlink(f)
diff --git a/contrib/python/setuptools/py3/setuptools/command/saveopts.py b/contrib/python/setuptools/py3/setuptools/command/saveopts.py
index f175de1015..2a2cbce6e2 100644
--- a/contrib/python/setuptools/py3/setuptools/command/saveopts.py
+++ b/contrib/python/setuptools/py3/setuptools/command/saveopts.py
@@ -6,9 +6,9 @@ class saveopts(option_base):
description = "save supplied options to setup.cfg or other config file"
- def run(self):
+ def run(self) -> None:
dist = self.distribution
- settings = {}
+ settings: dict[str, dict[str, str]] = {}
for cmd in dist.command_options:
if cmd == 'saveopts':
diff --git a/contrib/python/setuptools/py3/setuptools/command/sdist.py b/contrib/python/setuptools/py3/setuptools/command/sdist.py
index fa9a2c4d81..64e866c96b 100644
--- a/contrib/python/setuptools/py3/setuptools/command/sdist.py
+++ b/contrib/python/setuptools/py3/setuptools/command/sdist.py
@@ -4,6 +4,7 @@ import contextlib
import os
import re
from itertools import chain
+from typing import ClassVar
from .._importlib import metadata
from ..dist import Distribution
@@ -49,12 +50,12 @@ class sdist(orig.sdist):
]
distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution
- negative_opt: dict[str, str] = {}
+ negative_opt: ClassVar[dict[str, str]] = {}
README_EXTENSIONS = ['', '.rst', '.txt', '.md']
READMES = tuple('README{0}'.format(ext) for ext in README_EXTENSIONS)
- def run(self):
+ def run(self) -> None:
self.run_command('egg_info')
ei_cmd = self.get_finalized_command('egg_info')
self.filelist = ei_cmd.filelist
@@ -73,10 +74,10 @@ class sdist(orig.sdist):
if data not in dist_files:
dist_files.append(data)
- def initialize_options(self):
+ def initialize_options(self) -> None:
orig.sdist.initialize_options(self)
- def make_distribution(self):
+ def make_distribution(self) -> None:
"""
Workaround for #516
"""
@@ -104,7 +105,7 @@ class sdist(orig.sdist):
if orig_val is not NoValue:
os.link = orig_val
- def add_defaults(self):
+ def add_defaults(self) -> None:
super().add_defaults()
self._add_defaults_build_sub_commands()
@@ -157,13 +158,13 @@ class sdist(orig.sdist):
except TypeError:
log.warn("data_files contains unexpected objects")
- def prune_file_list(self):
+ def prune_file_list(self) -> None:
super().prune_file_list()
# Prevent accidental inclusion of test-related cache dirs at the project root
sep = re.escape(os.sep)
self.filelist.exclude_pattern(r"^(\.tox|\.nox|\.venv)" + sep, is_regex=True)
- def check_readme(self):
+ def check_readme(self) -> None:
for f in self.READMES:
if os.path.exists(f):
return
@@ -172,7 +173,7 @@ class sdist(orig.sdist):
"standard file not found: should have one of " + ', '.join(self.READMES)
)
- def make_release_tree(self, base_dir, files):
+ def make_release_tree(self, base_dir, files) -> None:
orig.sdist.make_release_tree(self, base_dir, files)
# Save any egg_info command line options used to create this sdist
@@ -201,10 +202,10 @@ class sdist(orig.sdist):
"""
log.info("reading manifest file '%s'", self.manifest)
manifest = open(self.manifest, 'rb')
- for line in manifest:
+ for bytes_line in manifest:
# The manifest must contain UTF-8. See #303.
try:
- line = line.decode('UTF-8')
+ line = bytes_line.decode('UTF-8')
except UnicodeDecodeError:
log.warn("%r not UTF-8 decodable -- skipping" % line)
continue
diff --git a/contrib/python/setuptools/py3/setuptools/command/setopt.py b/contrib/python/setuptools/py3/setuptools/command/setopt.py
index e351af22f0..200cdff0f7 100644
--- a/contrib/python/setuptools/py3/setuptools/command/setopt.py
+++ b/contrib/python/setuptools/py3/setuptools/command/setopt.py
@@ -37,7 +37,7 @@ def edit_config(filename, settings, dry_run=False):
"""
log.debug("Reading configuration from %s", filename)
opts = configparser.RawConfigParser()
- opts.optionxform = lambda x: x
+ opts.optionxform = lambda optionstr: optionstr # type: ignore[method-assign] # overriding method
_cfg_read_utf8_with_fallback(opts, filename)
for section, options in settings.items():
@@ -126,14 +126,14 @@ class setopt(option_base):
self.set_value = None
self.remove = None
- def finalize_options(self):
+ def finalize_options(self) -> None:
option_base.finalize_options(self)
if self.command is None or self.option is None:
raise DistutilsOptionError("Must specify --command *and* --option")
if self.set_value is None and not self.remove:
raise DistutilsOptionError("Must specify --set-value or --remove")
- def run(self):
+ def run(self) -> None:
edit_config(
self.filename,
{self.command: {self.option.replace('-', '_'): self.set_value}},
diff --git a/contrib/python/setuptools/py3/setuptools/command/upload.py b/contrib/python/setuptools/py3/setuptools/command/upload.py
deleted file mode 100644
index 649b41fa30..0000000000
--- a/contrib/python/setuptools/py3/setuptools/command/upload.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from setuptools.dist import Distribution
-from setuptools.errors import RemovedCommandError
-
-from distutils import log
-from distutils.command import upload as orig
-
-
-class upload(orig.upload):
- """Formerly used to upload packages to PyPI."""
-
- distribution: Distribution # override distutils.dist.Distribution with setuptools.dist.Distribution
-
- def run(self):
- msg = (
- "The upload command has been removed, use twine to upload "
- "instead (https://pypi.org/p/twine)"
- )
-
- self.announce("ERROR: " + msg, log.ERROR)
- raise RemovedCommandError(msg)
diff --git a/contrib/python/setuptools/py3/setuptools/command/upload_docs.py b/contrib/python/setuptools/py3/setuptools/command/upload_docs.py
deleted file mode 100644
index 3c2946cfc8..0000000000
--- a/contrib/python/setuptools/py3/setuptools/command/upload_docs.py
+++ /dev/null
@@ -1,221 +0,0 @@
-"""upload_docs
-
-Implements a Distutils 'upload_docs' subcommand (upload documentation to
-sites other than PyPi such as devpi).
-"""
-
-import functools
-import http.client
-import itertools
-import os
-import shutil
-import tempfile
-import urllib.parse
-import zipfile
-from base64 import standard_b64encode
-
-from .._importlib import metadata
-from ..warnings import SetuptoolsDeprecationWarning
-from .upload import upload
-
-from distutils import log
-from distutils.errors import DistutilsOptionError
-
-
-def _encode(s):
- return s.encode('utf-8', 'surrogateescape')
-
-
-class upload_docs(upload):
- # override the default repository as upload_docs isn't
- # supported by Warehouse (and won't be).
- DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi/'
-
- description = 'Upload documentation to sites other than PyPi such as devpi'
-
- user_options = [
- (
- 'repository=',
- 'r',
- "url of repository [default: %s]" % upload.DEFAULT_REPOSITORY,
- ),
- ('show-response', None, 'display full response text from server'),
- ('upload-dir=', None, 'directory to upload'),
- ]
- boolean_options = upload.boolean_options
-
- def has_sphinx(self):
- return bool(
- self.upload_dir is None
- and metadata.entry_points(group='distutils.commands', name='build_sphinx')
- )
-
- sub_commands = [('build_sphinx', has_sphinx)]
-
- def initialize_options(self):
- upload.initialize_options(self)
- self.upload_dir = None
- self.target_dir = None
-
- def finalize_options(self):
- log.warn(
- "Upload_docs command is deprecated. Use Read the Docs "
- "(https://readthedocs.org) instead."
- )
- upload.finalize_options(self)
- if self.upload_dir is None:
- if self.has_sphinx():
- build_sphinx = self.get_finalized_command('build_sphinx')
- self.target_dir = dict(build_sphinx.builder_target_dirs)['html']
- else:
- build = self.get_finalized_command('build')
- self.target_dir = os.path.join(build.build_base, 'docs')
- else:
- self.ensure_dirname('upload_dir')
- self.target_dir = self.upload_dir
- self.announce('Using upload directory %s' % self.target_dir)
-
- def create_zipfile(self, filename):
- zip_file = zipfile.ZipFile(filename, "w")
- try:
- self.mkpath(self.target_dir) # just in case
- for root, dirs, files in os.walk(self.target_dir):
- if root == self.target_dir and not files:
- tmpl = "no files found in upload directory '%s'"
- raise DistutilsOptionError(tmpl % self.target_dir)
- for name in files:
- full = os.path.join(root, name)
- relative = root[len(self.target_dir) :].lstrip(os.path.sep)
- dest = os.path.join(relative, name)
- zip_file.write(full, dest)
- finally:
- zip_file.close()
-
- def run(self):
- SetuptoolsDeprecationWarning.emit(
- "Deprecated command",
- """
- upload_docs is deprecated and will be removed in a future version.
- Instead, use tools like devpi and Read the Docs; or lower level tools like
- httpie and curl to interact directly with your hosting service API.
- """,
- due_date=(2023, 9, 26), # warning introduced in 27 Jul 2022
- )
-
- # Run sub commands
- for cmd_name in self.get_sub_commands():
- self.run_command(cmd_name)
-
- tmp_dir = tempfile.mkdtemp()
- name = self.distribution.metadata.get_name()
- zip_file = os.path.join(tmp_dir, "%s.zip" % name)
- try:
- self.create_zipfile(zip_file)
- self.upload_file(zip_file)
- finally:
- shutil.rmtree(tmp_dir)
-
- @staticmethod
- def _build_part(item, sep_boundary):
- key, values = item
- title = '\nContent-Disposition: form-data; name="%s"' % key
- # handle multiple entries for the same name
- if not isinstance(values, list):
- values = [values]
- for value in values:
- if isinstance(value, tuple):
- title += '; filename="%s"' % value[0]
- value = value[1]
- else:
- value = _encode(value)
- yield sep_boundary
- yield _encode(title)
- yield b"\n\n"
- yield value
- if value and value[-1:] == b'\r':
- yield b'\n' # write an extra newline (lurve Macs)
-
- @classmethod
- def _build_multipart(cls, data):
- """
- Build up the MIME payload for the POST data
- """
- boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
- sep_boundary = b'\n--' + boundary.encode('ascii')
- end_boundary = sep_boundary + b'--'
- end_items = (
- end_boundary,
- b"\n",
- )
- builder = functools.partial(
- cls._build_part,
- sep_boundary=sep_boundary,
- )
- part_groups = map(builder, data.items())
- parts = itertools.chain.from_iterable(part_groups)
- body_items = itertools.chain(parts, end_items)
- content_type = 'multipart/form-data; boundary=%s' % boundary
- return b''.join(body_items), content_type
-
- def upload_file(self, filename):
- with open(filename, 'rb') as f:
- content = f.read()
- meta = self.distribution.metadata
- data = {
- ':action': 'doc_upload',
- 'name': meta.get_name(),
- 'content': (os.path.basename(filename), content),
- }
- # set up the authentication
- credentials = _encode(self.username + ':' + self.password)
- credentials = standard_b64encode(credentials).decode('ascii')
- auth = "Basic " + credentials
-
- body, ct = self._build_multipart(data)
-
- msg = "Submitting documentation to %s" % (self.repository)
- self.announce(msg, log.INFO)
-
- # build the Request
- # We can't use urllib2 since we need to send the Basic
- # auth right with the first request
- schema, netloc, url, params, query, fragments = urllib.parse.urlparse(
- self.repository
- )
- assert not params and not query and not fragments
- if schema == 'http':
- conn = http.client.HTTPConnection(netloc)
- elif schema == 'https':
- conn = http.client.HTTPSConnection(netloc)
- else:
- raise AssertionError("unsupported schema " + schema)
-
- data = ''
- try:
- conn.connect()
- conn.putrequest("POST", url)
- content_type = ct
- conn.putheader('Content-type', content_type)
- conn.putheader('Content-length', str(len(body)))
- conn.putheader('Authorization', auth)
- conn.endheaders()
- conn.send(body)
- except OSError as e:
- self.announce(str(e), log.ERROR)
- return
-
- r = conn.getresponse()
- if r.status == 200:
- msg = 'Server response (%s): %s' % (r.status, r.reason)
- self.announce(msg, log.INFO)
- elif r.status == 301:
- location = r.getheader('Location')
- if location is None:
- location = 'https://pythonhosted.org/%s/' % meta.get_name()
- msg = 'Upload successful. Visit %s' % location
- self.announce(msg, log.INFO)
- else:
- msg = 'Upload failed (%s): %s' % (r.status, r.reason)
- self.announce(msg, log.ERROR)
- if self.show_response:
- print('-' * 75, r.read(), '-' * 75)
diff --git a/contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py b/contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py
index 23179f3548..c4bbcff730 100644
--- a/contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py
+++ b/contrib/python/setuptools/py3/setuptools/config/_apply_pyprojecttoml.py
@@ -12,12 +12,13 @@ from __future__ import annotations
import logging
import os
+from collections.abc import Mapping
from email.headerregistry import Address
from functools import partial, reduce
from inspect import cleandoc
from itertools import chain
from types import MappingProxyType
-from typing import TYPE_CHECKING, Any, Callable, Dict, Mapping, TypeVar, Union
+from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union
from .._path import StrPath
from ..errors import RemovedConfigError
@@ -30,12 +31,12 @@ if TYPE_CHECKING:
from setuptools._importlib import metadata
from setuptools.dist import Distribution
- from distutils.dist import _OptionsList
+ from distutils.dist import _OptionsList # Comes from typeshed
+
EMPTY: Mapping = MappingProxyType({}) # Immutable dict-like
-_ProjectReadmeValue: TypeAlias = Union[str, Dict[str, str]]
-_CorrespFn: TypeAlias = Callable[["Distribution", Any, StrPath], None]
-_Correspondence: TypeAlias = Union[str, _CorrespFn]
+_ProjectReadmeValue: TypeAlias = Union[str, dict[str, str]]
+_Correspondence: TypeAlias = Callable[["Distribution", Any, Union[StrPath, None]], None]
_T = TypeVar("_T")
_logger = logging.getLogger(__name__)
@@ -149,7 +150,9 @@ def _guess_content_type(file: str) -> str | None:
raise ValueError(f"Undefined content type for {file}, {msg}")
-def _long_description(dist: Distribution, val: _ProjectReadmeValue, root_dir: StrPath):
+def _long_description(
+ dist: Distribution, val: _ProjectReadmeValue, root_dir: StrPath | None
+):
from setuptools.config import expand
file: str | tuple[()]
@@ -171,7 +174,7 @@ def _long_description(dist: Distribution, val: _ProjectReadmeValue, root_dir: St
dist._referenced_files.add(file)
-def _license(dist: Distribution, val: dict, root_dir: StrPath):
+def _license(dist: Distribution, val: dict, root_dir: StrPath | None):
from setuptools.config import expand
if "file" in val:
@@ -181,7 +184,7 @@ def _license(dist: Distribution, val: dict, root_dir: StrPath):
_set_config(dist, "license", val["text"])
-def _people(dist: Distribution, val: list[dict], _root_dir: StrPath, kind: str):
+def _people(dist: Distribution, val: list[dict], _root_dir: StrPath | None, kind: str):
field = []
email_field = []
for person in val:
@@ -199,26 +202,28 @@ def _people(dist: Distribution, val: list[dict], _root_dir: StrPath, kind: str):
_set_config(dist, f"{kind}_email", ", ".join(email_field))
-def _project_urls(dist: Distribution, val: dict, _root_dir):
+def _project_urls(dist: Distribution, val: dict, _root_dir: StrPath | None):
_set_config(dist, "project_urls", val)
-def _python_requires(dist: Distribution, val: str, _root_dir):
+def _python_requires(dist: Distribution, val: str, _root_dir: StrPath | None):
from packaging.specifiers import SpecifierSet
_set_config(dist, "python_requires", SpecifierSet(val))
-def _dependencies(dist: Distribution, val: list, _root_dir):
+def _dependencies(dist: Distribution, val: list, _root_dir: StrPath | None):
if getattr(dist, "install_requires", []):
msg = "`install_requires` overwritten in `pyproject.toml` (dependencies)"
SetuptoolsWarning.emit(msg)
dist.install_requires = val
-def _optional_dependencies(dist: Distribution, val: dict, _root_dir):
- existing = getattr(dist, "extras_require", None) or {}
- dist.extras_require = {**existing, **val}
+def _optional_dependencies(dist: Distribution, val: dict, _root_dir: StrPath | None):
+ if getattr(dist, "extras_require", None):
+ msg = "`extras_require` overwritten in `pyproject.toml` (optional-dependencies)"
+ SetuptoolsWarning.emit(msg)
+ dist.extras_require = val
def _ext_modules(dist: Distribution, val: list[dict]) -> list[Extension]:
diff --git a/contrib/python/setuptools/py3/setuptools/config/expand.py b/contrib/python/setuptools/py3/setuptools/config/expand.py
index e11bcf9b42..54c68bed4f 100644
--- a/contrib/python/setuptools/py3/setuptools/config/expand.py
+++ b/contrib/python/setuptools/py3/setuptools/config/expand.py
@@ -25,13 +25,14 @@ import importlib
import os
import pathlib
import sys
+from collections.abc import Iterable, Iterator, Mapping
from configparser import ConfigParser
from glob import iglob
from importlib.machinery import ModuleSpec, all_suffixes
from itertools import chain
from pathlib import Path
from types import ModuleType, TracebackType
-from typing import TYPE_CHECKING, Any, Callable, Iterable, Iterator, Mapping, TypeVar
+from typing import TYPE_CHECKING, Any, Callable, TypeVar
from .._path import StrPath, same_path as _same_path
from ..discovery import find_package_path
@@ -45,13 +46,13 @@ if TYPE_CHECKING:
from setuptools.dist import Distribution
_K = TypeVar("_K")
-_V = TypeVar("_V", covariant=True)
+_V_co = TypeVar("_V_co", covariant=True)
class StaticModule:
"""Proxy to a module object that avoids executing arbitrary code."""
- def __init__(self, name: str, spec: ModuleSpec):
+ def __init__(self, name: str, spec: ModuleSpec) -> None:
module = ast.parse(pathlib.Path(spec.origin).read_bytes()) # type: ignore[arg-type] # Let it raise an error on None
vars(self).update(locals())
del self.self
@@ -203,7 +204,8 @@ def _load_spec(spec: ModuleSpec, module_name: str) -> ModuleType:
return sys.modules[name]
module = importlib.util.module_from_spec(spec)
sys.modules[name] = module # cache (it also ensures `==` works on loaded items)
- spec.loader.exec_module(module) # type: ignore
+ assert spec.loader is not None
+ spec.loader.exec_module(module)
return module
@@ -285,10 +287,11 @@ def find_packages(
from setuptools.discovery import construct_package_dir
- if namespaces:
- from setuptools.discovery import PEP420PackageFinder as PackageFinder
+ # check "not namespaces" first due to python/mypy#6232
+ if not namespaces:
+ from setuptools.discovery import PackageFinder
else:
- from setuptools.discovery import PackageFinder # type: ignore
+ from setuptools.discovery import PEP420PackageFinder as PackageFinder
root_dir = root_dir or os.curdir
where = kwargs.pop('where', ['.'])
@@ -352,14 +355,17 @@ def canonic_data_files(
]
-def entry_points(text: str, text_source="entry-points") -> dict[str, dict]:
+def entry_points(
+ text: str, text_source: str = "entry-points"
+) -> dict[str, dict[str, str]]:
"""Given the contents of entry-points file,
process it into a 2-level dictionary (``dict[str, dict[str, str]]``).
The first level keys are entry-point groups, the second level keys are
entry-point names, and the second level values are references to objects
(that correspond to the entry-point value).
"""
- parser = ConfigParser(default_section=None, delimiters=("=",)) # type: ignore
+ # Using undocumented behaviour, see python/typeshed#12700
+ parser = ConfigParser(default_section=None, delimiters=("=",)) # type: ignore[call-overload]
parser.optionxform = str # case sensitive
parser.read_string(text, text_source)
groups = {k: dict(v.items()) for k, v in parser.items()}
@@ -377,7 +383,7 @@ class EnsurePackagesDiscovered:
and those might not have been processed yet.
"""
- def __init__(self, distribution: Distribution):
+ def __init__(self, distribution: Distribution) -> None:
self._dist = distribution
self._called = False
@@ -395,7 +401,7 @@ class EnsurePackagesDiscovered:
exc_type: type[BaseException] | None,
exc_value: BaseException | None,
traceback: TracebackType | None,
- ) -> None:
+ ):
if self._called:
self._dist.set_defaults.analyse_name() # Now we can set a default name
@@ -410,7 +416,7 @@ class EnsurePackagesDiscovered:
return LazyMappingProxy(self._get_package_dir)
-class LazyMappingProxy(Mapping[_K, _V]):
+class LazyMappingProxy(Mapping[_K, _V_co]):
"""Mapping proxy that delays resolving the target object, until really needed.
>>> def obtain_mapping():
@@ -424,16 +430,16 @@ class LazyMappingProxy(Mapping[_K, _V]):
'other value'
"""
- def __init__(self, obtain_mapping_value: Callable[[], Mapping[_K, _V]]):
+ def __init__(self, obtain_mapping_value: Callable[[], Mapping[_K, _V_co]]) -> None:
self._obtain = obtain_mapping_value
- self._value: Mapping[_K, _V] | None = None
+ self._value: Mapping[_K, _V_co] | None = None
- def _target(self) -> Mapping[_K, _V]:
+ def _target(self) -> Mapping[_K, _V_co]:
if self._value is None:
self._value = self._obtain()
return self._value
- def __getitem__(self, key: _K) -> _V:
+ def __getitem__(self, key: _K) -> _V_co:
return self._target()[key]
def __len__(self) -> int:
diff --git a/contrib/python/setuptools/py3/setuptools/config/pyprojecttoml.py b/contrib/python/setuptools/py3/setuptools/config/pyprojecttoml.py
index 5d95e18b83..15b0baa18e 100644
--- a/contrib/python/setuptools/py3/setuptools/config/pyprojecttoml.py
+++ b/contrib/python/setuptools/py3/setuptools/config/pyprojecttoml.py
@@ -13,10 +13,11 @@ from __future__ import annotations
import logging
import os
+from collections.abc import Mapping
from contextlib import contextmanager
from functools import partial
from types import TracebackType
-from typing import TYPE_CHECKING, Any, Callable, Mapping
+from typing import TYPE_CHECKING, Any, Callable
from .._path import StrPath
from ..errors import FileError, InvalidConfigError
@@ -44,8 +45,8 @@ def validate(config: dict, filepath: StrPath) -> bool:
trove_classifier = validator.FORMAT_FUNCTIONS.get("trove-classifier")
if hasattr(trove_classifier, "_disable_download"):
- # Improve reproducibility by default. See issue 31 for validate-pyproject.
- trove_classifier._disable_download() # type: ignore
+ # Improve reproducibility by default. See abravalheri/validate-pyproject#31
+ trove_classifier._disable_download() # type: ignore[union-attr]
try:
return validator.validate(config)
@@ -63,7 +64,7 @@ def validate(config: dict, filepath: StrPath) -> bool:
def apply_configuration(
dist: Distribution,
filepath: StrPath,
- ignore_option_errors=False,
+ ignore_option_errors: bool = False,
) -> Distribution:
"""Apply the configuration from a ``pyproject.toml`` file into an existing
distribution object.
@@ -74,8 +75,8 @@ def apply_configuration(
def read_configuration(
filepath: StrPath,
- expand=True,
- ignore_option_errors=False,
+ expand: bool = True,
+ ignore_option_errors: bool = False,
dist: Distribution | None = None,
) -> dict[str, Any]:
"""Read given configuration file and returns options from it as a dict.
@@ -175,7 +176,7 @@ class _ConfigExpander:
root_dir: StrPath | None = None,
ignore_option_errors: bool = False,
dist: Distribution | None = None,
- ):
+ ) -> None:
self.config = config
self.root_dir = root_dir or os.getcwd()
self.project_cfg = config.get("project", {})
@@ -330,7 +331,7 @@ class _ConfigExpander:
def _obtain_entry_points(
self, dist: Distribution, package_dir: Mapping[str, str]
- ) -> dict[str, dict] | None:
+ ) -> dict[str, dict[str, Any]] | None:
fields = ("entry-points", "scripts", "gui-scripts")
if not any(field in self.dynamic for field in fields):
return None
@@ -340,7 +341,8 @@ class _ConfigExpander:
return None
groups = _expand.entry_points(text)
- expanded = {"entry-points": groups}
+ # Any is str | dict[str, str], but causes variance issues
+ expanded: dict[str, dict[str, Any]] = {"entry-points": groups}
def _set_scripts(field: str, group: str):
if group in groups:
@@ -411,7 +413,7 @@ def _ignore_errors(ignore_option_errors: bool):
class _EnsurePackagesDiscovered(_expand.EnsurePackagesDiscovered):
def __init__(
self, distribution: Distribution, project_cfg: dict, setuptools_cfg: dict
- ):
+ ) -> None:
super().__init__(distribution)
self._project_cfg = project_cfg
self._setuptools_cfg = setuptools_cfg
diff --git a/contrib/python/setuptools/py3/setuptools/config/setupcfg.py b/contrib/python/setuptools/py3/setuptools/config/setupcfg.py
index 54469f74a3..b35d0b00cd 100644
--- a/contrib/python/setuptools/py3/setuptools/config/setupcfg.py
+++ b/contrib/python/setuptools/py3/setuptools/config/setupcfg.py
@@ -15,21 +15,9 @@ import contextlib
import functools
import os
from collections import defaultdict
+from collections.abc import Iterable, Iterator
from functools import partial, wraps
-from typing import (
- TYPE_CHECKING,
- Any,
- Callable,
- Dict,
- Generic,
- Iterable,
- Iterator,
- List,
- Tuple,
- TypeVar,
- Union,
- cast,
-)
+from typing import TYPE_CHECKING, Any, Callable, ClassVar, Generic, TypeVar, cast
from packaging.markers import default_environment as marker_env
from packaging.requirements import InvalidRequirement, Requirement
@@ -42,22 +30,25 @@ from ..warnings import SetuptoolsDeprecationWarning
from . import expand
if TYPE_CHECKING:
+ from typing_extensions import TypeAlias
+
from setuptools.dist import Distribution
from distutils.dist import DistributionMetadata
-SingleCommandOptions = Dict["str", Tuple["str", Any]]
+SingleCommandOptions: TypeAlias = dict[str, tuple[str, Any]]
"""Dict that associate the name of the options of a particular command to a
tuple. The first element of the tuple indicates the origin of the option value
(e.g. the name of the configuration file where it was read from),
while the second element of the tuple is the option value itself
"""
-AllCommandOptions = Dict["str", SingleCommandOptions] # cmd name => its options
-Target = TypeVar("Target", bound=Union["Distribution", "DistributionMetadata"])
+AllCommandOptions: TypeAlias = dict[str, SingleCommandOptions]
+"""cmd name => its options"""
+Target = TypeVar("Target", "Distribution", "DistributionMetadata")
def read_configuration(
- filepath: StrPath, find_others=False, ignore_option_errors=False
+ filepath: StrPath, find_others: bool = False, ignore_option_errors: bool = False
) -> dict:
"""Read given configuration file and returns options from it as a dict.
@@ -96,7 +87,7 @@ def _apply(
filepath: StrPath,
other_files: Iterable[StrPath] = (),
ignore_option_errors: bool = False,
-) -> tuple[ConfigHandler, ...]:
+) -> tuple[ConfigMetadataHandler, ConfigOptionsHandler]:
"""Read configuration from ``filepath`` and applies to the ``dist`` object."""
from setuptools.dist import _Distribution
@@ -111,7 +102,7 @@ def _apply(
try:
# TODO: Temporary cast until mypy 1.12 is released with upstream fixes from typeshed
- _Distribution.parse_config_files(dist, filenames=cast(List[str], filenames))
+ _Distribution.parse_config_files(dist, filenames=cast(list[str], filenames))
handlers = parse_configuration(
dist, dist.command_options, ignore_option_errors=ignore_option_errors
)
@@ -122,7 +113,7 @@ def _apply(
return handlers
-def _get_option(target_obj: Target, key: str):
+def _get_option(target_obj: Distribution | DistributionMetadata, key: str):
"""
Given a target object and option key, get that option from
the target object, either through a get_{key} method or
@@ -134,10 +125,14 @@ def _get_option(target_obj: Target, key: str):
return getter()
-def configuration_to_dict(handlers: tuple[ConfigHandler, ...]) -> dict:
+def configuration_to_dict(
+ handlers: Iterable[
+ ConfigHandler[Distribution] | ConfigHandler[DistributionMetadata]
+ ],
+) -> dict:
"""Returns configuration data gathered by given handlers as a dict.
- :param list[ConfigHandler] handlers: Handlers list,
+ :param Iterable[ConfigHandler] handlers: Handlers list,
usually from parse_configuration()
:rtype: dict
@@ -155,7 +150,7 @@ def configuration_to_dict(handlers: tuple[ConfigHandler, ...]) -> dict:
def parse_configuration(
distribution: Distribution,
command_options: AllCommandOptions,
- ignore_option_errors=False,
+ ignore_option_errors: bool = False,
) -> tuple[ConfigMetadataHandler, ConfigOptionsHandler]:
"""Performs additional parsing of configuration options
for a distribution.
@@ -239,7 +234,7 @@ class ConfigHandler(Generic[Target]):
"""
- aliases: dict[str, str] = {}
+ aliases: ClassVar[dict[str, str]] = {}
"""Options aliases.
For compatibility with various packages. E.g.: d2to1 and pbr.
Note: `-` in keys is replaced with `_` by config parser.
@@ -252,9 +247,9 @@ class ConfigHandler(Generic[Target]):
options: AllCommandOptions,
ignore_option_errors,
ensure_discovered: expand.EnsurePackagesDiscovered,
- ):
+ ) -> None:
self.ignore_option_errors = ignore_option_errors
- self.target_obj = target_obj
+ self.target_obj: Target = target_obj
self.sections = dict(self._section_options(options))
self.set_options: list[str] = []
self.ensure_discovered = ensure_discovered
@@ -268,7 +263,7 @@ class ConfigHandler(Generic[Target]):
cls, options: AllCommandOptions
) -> Iterator[tuple[str, SingleCommandOptions]]:
for full_name, value in options.items():
- pre, sep, name = full_name.partition(cls.section_prefix)
+ pre, _sep, name = full_name.partition(cls.section_prefix)
if pre:
continue
yield name.lstrip('.'), value
@@ -301,7 +296,7 @@ class ConfigHandler(Generic[Target]):
return
simple_setter = functools.partial(target_obj.__setattr__, option_name)
- setter = getattr(target_obj, 'set_%s' % option_name, simple_setter)
+ setter = getattr(target_obj, f"set_{option_name}", simple_setter)
setter(parsed)
self.set_options.append(option_name)
@@ -369,14 +364,14 @@ class ConfigHandler(Generic[Target]):
exclude_directive = 'file:'
if value.startswith(exclude_directive):
raise ValueError(
- 'Only strings are accepted for the {0} field, '
- 'files are not accepted'.format(key)
+ f'Only strings are accepted for the {key} field, '
+ 'files are not accepted'
)
return value
return parser
- def _parse_file(self, value, root_dir: StrPath):
+ def _parse_file(self, value, root_dir: StrPath | None):
"""Represents value as a string, allowing including text
from nearest files using `file:` directive.
@@ -470,7 +465,7 @@ class ConfigHandler(Generic[Target]):
parser = (lambda _, v: values_parser(v)) if values_parser else (lambda _, v: v)
return cls._parse_section_to_dict_with_key(section_options, parser)
- def parse_section(self, section_options):
+ def parse_section(self, section_options) -> None:
"""Parses configuration file section.
:param dict section_options:
@@ -488,12 +483,12 @@ class ConfigHandler(Generic[Target]):
for section_name, section_options in self.sections.items():
method_postfix = ''
if section_name: # [section.option] variant
- method_postfix = '_%s' % section_name
+ method_postfix = f"_{section_name}"
section_parser_method: Callable | None = getattr(
self,
# Dots in section names are translated into dunderscores.
- ('parse_section%s' % method_postfix).replace('.', '__'),
+ f'parse_section{method_postfix}'.replace('.', '__'),
None,
)
@@ -544,8 +539,8 @@ class ConfigMetadataHandler(ConfigHandler["DistributionMetadata"]):
ignore_option_errors: bool,
ensure_discovered: expand.EnsurePackagesDiscovered,
package_dir: dict | None = None,
- root_dir: StrPath = os.curdir,
- ):
+ root_dir: StrPath | None = os.curdir,
+ ) -> None:
super().__init__(target_obj, options, ignore_option_errors, ensure_discovered)
self.package_dir = package_dir
self.root_dir = root_dir
@@ -607,7 +602,7 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]):
options: AllCommandOptions,
ignore_option_errors: bool,
ensure_discovered: expand.EnsurePackagesDiscovered,
- ):
+ ) -> None:
super().__init__(target_obj, options, ignore_option_errors, ensure_discovered)
self.root_dir = target_obj.src_root
self.package_dir: dict[str, str] = {} # To be filled by `find_packages`
@@ -698,10 +693,7 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]):
section_data = self._parse_section_to_dict(section_options, self._parse_list)
valid_keys = ['where', 'include', 'exclude']
-
- find_kwargs = dict([
- (k, v) for k, v in section_data.items() if k in valid_keys and v
- ])
+ find_kwargs = {k: v for k, v in section_data.items() if k in valid_keys and v}
where = find_kwargs.get('where')
if where is not None:
@@ -709,7 +701,7 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]):
return find_kwargs
- def parse_section_entry_points(self, section_options):
+ def parse_section_entry_points(self, section_options) -> None:
"""Parses `entry_points` configuration file section.
:param dict section_options:
@@ -721,21 +713,21 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]):
package_data = self._parse_section_to_dict(section_options, self._parse_list)
return expand.canonic_package_data(package_data)
- def parse_section_package_data(self, section_options):
+ def parse_section_package_data(self, section_options) -> None:
"""Parses `package_data` configuration file section.
:param dict section_options:
"""
self['package_data'] = self._parse_package_data(section_options)
- def parse_section_exclude_package_data(self, section_options):
+ def parse_section_exclude_package_data(self, section_options) -> None:
"""Parses `exclude_package_data` configuration file section.
:param dict section_options:
"""
self['exclude_package_data'] = self._parse_package_data(section_options)
- def parse_section_extras_require(self, section_options):
+ def parse_section_extras_require(self, section_options) -> None:
"""Parses `extras_require` configuration file section.
:param dict section_options:
@@ -747,7 +739,7 @@ class ConfigOptionsHandler(ConfigHandler["Distribution"]):
self['extras_require'] = parsed
- def parse_section_data_files(self, section_options):
+ def parse_section_data_files(self, section_options) -> None:
"""Parses `data_files` configuration file section.
:param dict section_options:
diff --git a/contrib/python/setuptools/py3/setuptools/depends.py b/contrib/python/setuptools/py3/setuptools/depends.py
index 9398b95331..1be71857a5 100644
--- a/contrib/python/setuptools/py3/setuptools/depends.py
+++ b/contrib/python/setuptools/py3/setuptools/depends.py
@@ -1,13 +1,19 @@
+from __future__ import annotations
+
import contextlib
import dis
import marshal
import sys
+from types import CodeType
+from typing import Any, Literal, TypeVar
from packaging.version import Version
from . import _imp
from ._imp import PY_COMPILED, PY_FROZEN, PY_SOURCE, find_module
+_T = TypeVar("_T")
+
__all__ = ['Require', 'find_module']
@@ -15,8 +21,14 @@ class Require:
"""A prerequisite to building or installing a distribution"""
def __init__(
- self, name, requested_version, module, homepage='', attribute=None, format=None
- ):
+ self,
+ name,
+ requested_version,
+ module,
+ homepage: str = '',
+ attribute=None,
+ format=None,
+ ) -> None:
if format is None and requested_version is not None:
format = Version
@@ -43,7 +55,9 @@ class Require:
and self.format(version) >= self.requested_version
)
- def get_version(self, paths=None, default="unknown"):
+ def get_version(
+ self, paths=None, default: _T | Literal["unknown"] = "unknown"
+ ) -> _T | Literal["unknown"] | None | Any:
"""Get version number of installed module, 'None', or 'default'
Search 'paths' for module. If not found, return 'None'. If found,
@@ -56,7 +70,7 @@ class Require:
if self.attribute is None:
try:
- f, p, i = find_module(self.module, paths)
+ f, _p, _i = find_module(self.module, paths)
except ImportError:
return None
if f:
@@ -98,7 +112,9 @@ def maybe_close(f):
# XXX it'd be better to test assertions about bytecode instead.
if not sys.platform.startswith('java') and sys.platform != 'cli':
- def get_module_constant(module, symbol, default=-1, paths=None):
+ def get_module_constant(
+ module, symbol, default: _T | int = -1, paths=None
+ ) -> _T | int | None | Any:
"""Find 'module' by searching 'paths', and extract 'symbol'
Return 'None' if 'module' does not exist on 'paths', or it does not define
@@ -106,7 +122,7 @@ if not sys.platform.startswith('java') and sys.platform != 'cli':
constant. Otherwise, return 'default'."""
try:
- f, path, (suffix, mode, kind) = info = find_module(module, paths)
+ f, path, (_suffix, _mode, kind) = info = find_module(module, paths)
except ImportError:
# Module doesn't exist
return None
@@ -126,7 +142,9 @@ if not sys.platform.startswith('java') and sys.platform != 'cli':
return extract_constant(code, symbol, default)
- def extract_constant(code, symbol, default=-1):
+ def extract_constant(
+ code: CodeType, symbol: str, default: _T | int = -1
+ ) -> _T | int | None | Any:
"""Extract the constant value of 'symbol' from 'code'
If the name 'symbol' is bound to a constant value by the Python code
@@ -155,6 +173,7 @@ if not sys.platform.startswith('java') and sys.platform != 'cli':
arg = byte_code.arg
if op == LOAD_CONST:
+ assert arg is not None
const = code.co_consts[arg]
elif arg == name_idx and (op == STORE_NAME or op == STORE_GLOBAL):
return const
diff --git a/contrib/python/setuptools/py3/setuptools/discovery.py b/contrib/python/setuptools/py3/setuptools/discovery.py
index 577be2f16b..c888399185 100644
--- a/contrib/python/setuptools/py3/setuptools/discovery.py
+++ b/contrib/python/setuptools/py3/setuptools/discovery.py
@@ -41,10 +41,11 @@ from __future__ import annotations
import itertools
import os
+from collections.abc import Iterable, Iterator, Mapping
from fnmatch import fnmatchcase
from glob import glob
from pathlib import Path
-from typing import TYPE_CHECKING, Iterable, Iterator, Mapping
+from typing import TYPE_CHECKING, ClassVar
import _distutils_hack.override # noqa: F401
@@ -53,13 +54,11 @@ from ._path import StrPath
from distutils import log
from distutils.util import convert_path
-StrIter = Iterator[str]
-
-chain_iter = itertools.chain.from_iterable
-
if TYPE_CHECKING:
from setuptools import Distribution
+chain_iter = itertools.chain.from_iterable
+
def _valid_name(path: StrPath) -> bool:
# Ignore invalid names that cannot be imported directly
@@ -72,7 +71,7 @@ class _Filter:
the input matches at least one of the patterns.
"""
- def __init__(self, *patterns: str):
+ def __init__(self, *patterns: str) -> None:
self._patterns = dict.fromkeys(patterns)
def __call__(self, item: str) -> bool:
@@ -85,8 +84,8 @@ class _Filter:
class _Finder:
"""Base class that exposes functionality for module/package finders"""
- ALWAYS_EXCLUDE: tuple[str, ...] = ()
- DEFAULT_EXCLUDE: tuple[str, ...] = ()
+ ALWAYS_EXCLUDE: ClassVar[tuple[str, ...]] = ()
+ DEFAULT_EXCLUDE: ClassVar[tuple[str, ...]] = ()
@classmethod
def find(
@@ -124,7 +123,9 @@ class _Finder:
)
@classmethod
- def _find_iter(cls, where: StrPath, exclude: _Filter, include: _Filter) -> StrIter:
+ def _find_iter(
+ cls, where: StrPath, exclude: _Filter, include: _Filter
+ ) -> Iterator[str]:
raise NotImplementedError
@@ -136,7 +137,9 @@ class PackageFinder(_Finder):
ALWAYS_EXCLUDE = ("ez_setup", "*__pycache__")
@classmethod
- def _find_iter(cls, where: StrPath, exclude: _Filter, include: _Filter) -> StrIter:
+ def _find_iter(
+ cls, where: StrPath, exclude: _Filter, include: _Filter
+ ) -> Iterator[str]:
"""
All the packages found in 'where' that pass the 'include' filter, but
not the 'exclude' filter.
@@ -185,7 +188,9 @@ class ModuleFinder(_Finder):
"""
@classmethod
- def _find_iter(cls, where: StrPath, exclude: _Filter, include: _Filter) -> StrIter:
+ def _find_iter(
+ cls, where: StrPath, exclude: _Filter, include: _Filter
+ ) -> Iterator[str]:
for file in glob(os.path.join(where, "*.py")):
module, _ext = os.path.splitext(os.path.basename(file))
@@ -295,7 +300,7 @@ class ConfigDiscovery:
(from other metadata/options, the file system or conventions)
"""
- def __init__(self, distribution: Distribution):
+ def __init__(self, distribution: Distribution) -> None:
self.dist = distribution
self._called = False
self._disabled = False
@@ -328,7 +333,9 @@ class ConfigDiscovery:
return {}
return self.dist.package_dir
- def __call__(self, force=False, name=True, ignore_ext_modules=False):
+ def __call__(
+ self, force: bool = False, name: bool = True, ignore_ext_modules: bool = False
+ ):
"""Automatically discover missing configuration fields
and modifies the given ``distribution`` object in-place.
@@ -472,7 +479,7 @@ class ConfigDiscovery:
"""
raise PackageDiscoveryError(cleandoc(msg))
- def analyse_name(self):
+ def analyse_name(self) -> None:
"""The packages/modules are the essential contribution of the author.
Therefore the name of the distribution can be derived from them.
"""
diff --git a/contrib/python/setuptools/py3/setuptools/dist.py b/contrib/python/setuptools/py3/setuptools/dist.py
index 68f877decd..5b3175fb5b 100644
--- a/contrib/python/setuptools/py3/setuptools/dist.py
+++ b/contrib/python/setuptools/py3/setuptools/dist.py
@@ -6,9 +6,10 @@ import numbers
import os
import re
import sys
+from collections.abc import Iterable, MutableMapping, Sequence
from glob import iglob
from pathlib import Path
-from typing import TYPE_CHECKING, MutableMapping
+from typing import TYPE_CHECKING, Any, Union
from more_itertools import partition, unique_everseen
from packaging.markers import InvalidMarker, Marker
@@ -21,6 +22,8 @@ from . import (
command as _, # noqa: F401 # imported for side-effects
)
from ._importlib import metadata
+from ._path import StrPath
+from ._reqs import _StrOrIter
from .config import pyprojecttoml, setupcfg
from .discovery import ConfigDiscovery
from .monkey import get_unpatched
@@ -36,9 +39,44 @@ from distutils.errors import DistutilsOptionError, DistutilsSetupError
from distutils.fancy_getopt import translate_longopt
from distutils.util import strtobool
+if TYPE_CHECKING:
+ from typing_extensions import TypeAlias
+
+ from pkg_resources import Distribution as _pkg_resources_Distribution
+
+
__all__ = ['Distribution']
-sequence = tuple, list
+_sequence = tuple, list
+"""
+:meta private:
+
+Supported iterable types that are known to be:
+- ordered (which `set` isn't)
+- not match a str (which `Sequence[str]` does)
+- not imply a nested type (like `dict`)
+for use with `isinstance`.
+"""
+_Sequence: TypeAlias = Union[tuple[str, ...], list[str]]
+# This is how stringifying _Sequence would look in Python 3.10
+_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
+ if name == "sequence":
+ SetuptoolsDeprecationWarning.emit(
+ "`setuptools.dist.sequence` is an internal implementation detail.",
+ "Please define your own `sequence = tuple, list` instead.",
+ due_date=(2025, 8, 28), # Originally added on 2024-08-27
+ )
+ return _sequence
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
def check_importable(dist, attr, value):
@@ -51,17 +89,17 @@ def check_importable(dist, attr, value):
) from e
-def assert_string_list(dist, attr, value):
+def assert_string_list(dist, attr: str, value: _Sequence) -> None:
"""Verify that value is a string list"""
try:
# verify that value is a list or tuple to exclude unordered
# or single-use iterables
- assert isinstance(value, sequence)
+ assert isinstance(value, _sequence)
# verify that elements of value are strings
assert ''.join(value) != value
except (TypeError, ValueError, AttributeError, AssertionError) as e:
raise DistutilsSetupError(
- "%r must be a list of strings (got %r)" % (attr, value)
+ f"{attr!r} must be of type <{_sequence_type_repr}> (got {value!r})"
) from e
@@ -75,7 +113,7 @@ def check_nsp(dist, attr, value):
"Distribution contains no modules or packages for "
+ "namespace package %r" % nsp
)
- parent, sep, child = nsp.rpartition('.')
+ parent, _sep, _child = nsp.rpartition('.')
if parent and parent not in ns_packages:
distutils.log.warn(
"WARNING: %r is declared as a package namespace, but %r"
@@ -107,7 +145,7 @@ def check_extras(dist, attr, value):
def _check_extra(extra, reqs):
- name, sep, marker = extra.partition(':')
+ _name, _sep, marker = extra.partition(':')
try:
_check_marker(marker)
except InvalidMarker:
@@ -126,8 +164,7 @@ def _check_marker(marker):
def assert_bool(dist, attr, value):
"""Verify that value is True, False, 0, or 1"""
if bool(value) != value:
- tmpl = "{attr!r} must be a boolean value (got {value!r})"
- raise DistutilsSetupError(tmpl.format(attr=attr, value=value))
+ raise DistutilsSetupError(f"{attr!r} must be a boolean value (got {value!r})")
def invalid_unless_false(dist, attr, value):
@@ -138,18 +175,18 @@ def invalid_unless_false(dist, attr, value):
raise DistutilsSetupError(f"{attr} is invalid.")
-def check_requirements(dist, attr, value):
+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:
- tmpl = (
- "{attr!r} must be a string or list of strings "
- "containing valid project/version requirement specifiers; {error}"
+ msg = (
+ f"{attr!r} must be a string or iterable of strings "
+ f"containing valid project/version requirement specifiers; {error}"
)
- raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) from error
+ raise DistutilsSetupError(msg) from error
def check_specifier(dist, attr, value):
@@ -157,8 +194,8 @@ def check_specifier(dist, attr, value):
try:
SpecifierSet(value)
except (InvalidSpecifier, AttributeError) as error:
- tmpl = "{attr!r} must be a string containing valid version specifiers; {error}"
- raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) from error
+ msg = f"{attr!r} must be a string containing valid version specifiers; {error}"
+ raise DistutilsSetupError(msg) from error
def check_entry_points(dist, attr, value):
@@ -195,10 +232,8 @@ def check_packages(dist, attr, value):
if TYPE_CHECKING:
- from typing_extensions import TypeAlias
-
# Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962
- _Distribution: TypeAlias = distutils.core.Distribution
+ from distutils.core import Distribution as _Distribution
else:
_Distribution = get_unpatched(distutils.core.Distribution)
@@ -262,7 +297,8 @@ class Distribution(_Distribution):
# Used by build_py, editable_wheel and install_lib commands for legacy namespaces
namespace_packages: list[str] #: :meta private: DEPRECATED
- def __init__(self, attrs: MutableMapping | None = None) -> None:
+ # Any: Dynamic assignment results in Incompatible types in assignment
+ def __init__(self, attrs: MutableMapping[str, Any] | None = None) -> None:
have_package_data = hasattr(self, "package_data")
if not have_package_data:
self.package_data: dict[str, list[str]] = {}
@@ -271,9 +307,9 @@ class Distribution(_Distribution):
self.include_package_data: bool | None = None
self.exclude_package_data: dict[str, list[str]] | None = None
# Filter-out setuptools' specific options.
- self.src_root = attrs.pop("src_root", None)
- self.dependency_links = attrs.pop('dependency_links', [])
- self.setup_requires = attrs.pop('setup_requires', [])
+ self.src_root: str | None = attrs.pop("src_root", None)
+ self.dependency_links: list[str] = attrs.pop('dependency_links', [])
+ self.setup_requires: list[str] = attrs.pop('setup_requires', [])
for ep in metadata.entry_points(group='distutils.setup_keywords'):
vars(self).setdefault(ep.name, None)
@@ -475,7 +511,7 @@ class Distribution(_Distribution):
except ValueError as e:
raise DistutilsOptionError(e) from e
- def warn_dash_deprecation(self, opt, section):
+ def warn_dash_deprecation(self, opt: str, section: str) -> str:
if section in (
'options.extras_require',
'options.data_files',
@@ -504,7 +540,7 @@ class Distribution(_Distribution):
versions. Please use the underscore name {underscore_opt!r} instead.
""",
see_docs="userguide/declarative_config.html",
- due_date=(2024, 9, 26),
+ due_date=(2025, 3, 3),
# Warning initially introduced in 3 Mar 2021
)
return underscore_opt
@@ -517,7 +553,7 @@ class Distribution(_Distribution):
# during bootstrapping, distribution doesn't exist
return []
- def make_option_lowercase(self, opt, section):
+ def make_option_lowercase(self, opt: str, section: str) -> str:
if section != 'metadata' or opt.islower():
return opt
@@ -529,7 +565,7 @@ class Distribution(_Distribution):
future versions. Please use lowercase {lowercase_opt!r} instead.
""",
see_docs="userguide/declarative_config.html",
- due_date=(2024, 9, 26),
+ due_date=(2025, 3, 3),
# Warning initially introduced in 6 Mar 2021
)
return lowercase_opt
@@ -581,7 +617,7 @@ class Distribution(_Distribution):
except ValueError as e:
raise DistutilsOptionError(e) from e
- def _get_project_config_files(self, filenames):
+ def _get_project_config_files(self, filenames: Iterable[StrPath] | None):
"""Add default file and split between INI and TOML"""
tomlfiles = []
standard_project_metadata = Path(self.src_root or os.curdir, "pyproject.toml")
@@ -593,7 +629,11 @@ class Distribution(_Distribution):
tomlfiles = [standard_project_metadata]
return filenames, tomlfiles
- def parse_config_files(self, filenames=None, ignore_option_errors=False):
+ def parse_config_files(
+ self,
+ filenames: Iterable[StrPath] | None = None,
+ ignore_option_errors: bool = False,
+ ) -> None:
"""Parses configuration files from various levels
and loads configuration.
"""
@@ -610,13 +650,15 @@ class Distribution(_Distribution):
self._finalize_requires()
self._finalize_license_files()
- def fetch_build_eggs(self, requires):
+ def fetch_build_eggs(
+ self, requires: _StrOrIter
+ ) -> list[_pkg_resources_Distribution]:
"""Resolve pre-setup requirements"""
from .installer import _fetch_build_eggs
return _fetch_build_eggs(self, requires)
- def finalize_options(self):
+ def finalize_options(self) -> None:
"""
Allow plugins to apply arbitrary operations to the
distribution. Each hook may optionally define a 'order'
@@ -681,7 +723,7 @@ class Distribution(_Distribution):
return fetch_build_egg(self, req)
- def get_command_class(self, command):
+ def get_command_class(self, command: str) -> type[distutils.cmd.Command]: # type: ignore[override] # Not doing complex overrides yet
"""Pluggable version of get_command_class()"""
if command in self.cmdclass:
return self.cmdclass[command]
@@ -713,7 +755,7 @@ class Distribution(_Distribution):
self.cmdclass[ep.name] = cmdclass
return _Distribution.get_command_list(self)
- def include(self, **attrs):
+ def include(self, **attrs) -> None:
"""Add items to distribution that are named in keyword arguments
For example, 'dist.include(py_modules=["x"])' would add 'x' to
@@ -735,7 +777,7 @@ class Distribution(_Distribution):
else:
self._include_misc(k, v)
- def exclude_package(self, package):
+ def exclude_package(self, package: str) -> None:
"""Remove packages, modules, and extensions in named package"""
pfx = package + '.'
@@ -756,7 +798,7 @@ class Distribution(_Distribution):
if p.name != package and not p.name.startswith(pfx)
]
- def has_contents_for(self, package):
+ def has_contents_for(self, package: str) -> bool:
"""Return true if 'exclude_package(package)' would do something"""
pfx = package + '.'
@@ -767,43 +809,45 @@ class Distribution(_Distribution):
return False
- def _exclude_misc(self, name, value):
+ def _exclude_misc(self, name: str, value: _Sequence) -> None:
"""Handle 'exclude()' for list/tuple attrs without a special handler"""
- if not isinstance(value, sequence):
+ if not isinstance(value, _sequence):
raise DistutilsSetupError(
- "%s: setting must be a list or tuple (%r)" % (name, value)
+ f"{name}: setting must be of type <{_sequence_type_repr}> (got {value!r})"
)
try:
old = getattr(self, name)
except AttributeError as e:
raise DistutilsSetupError("%s: No such distribution setting" % name) from e
- if old is not None and not isinstance(old, sequence):
+ if old is not None and not isinstance(old, _sequence):
raise DistutilsSetupError(
name + ": this setting cannot be changed via include/exclude"
)
elif old:
setattr(self, name, [item for item in old if item not in value])
- def _include_misc(self, name, value):
+ def _include_misc(self, name: str, value: _Sequence) -> None:
"""Handle 'include()' for list/tuple attrs without a special handler"""
- if not isinstance(value, sequence):
- raise DistutilsSetupError("%s: setting must be a list (%r)" % (name, value))
+ if not isinstance(value, _sequence):
+ raise DistutilsSetupError(
+ f"{name}: setting must be of type <{_sequence_type_repr}> (got {value!r})"
+ )
try:
old = getattr(self, name)
except AttributeError as e:
raise DistutilsSetupError("%s: No such distribution setting" % name) from e
if old is None:
setattr(self, name, value)
- elif not isinstance(old, sequence):
+ elif not isinstance(old, _sequence):
raise DistutilsSetupError(
name + ": this setting cannot be changed via include/exclude"
)
else:
new = [item for item in value if item not in old]
- setattr(self, name, old + new)
+ setattr(self, name, list(old) + new)
- def exclude(self, **attrs):
+ def exclude(self, **attrs) -> None:
"""Remove items from distribution that are named in keyword arguments
For example, 'dist.exclude(py_modules=["x"])' would remove 'x' from
@@ -826,10 +870,10 @@ class Distribution(_Distribution):
else:
self._exclude_misc(k, v)
- def _exclude_packages(self, packages):
- if not isinstance(packages, sequence):
+ def _exclude_packages(self, packages: _Sequence) -> None:
+ if not isinstance(packages, _sequence):
raise DistutilsSetupError(
- "packages: setting must be a list or tuple (%r)" % (packages,)
+ f"packages: setting must be of type <{_sequence_type_repr}> (got {packages!r})"
)
list(map(self.exclude_package, packages))
@@ -842,7 +886,7 @@ class Distribution(_Distribution):
command = args[0]
aliases = self.get_option_dict('aliases')
while command in aliases:
- src, alias = aliases[command]
+ _src, alias = aliases[command]
del aliases[command] # ensure each alias can expand only once!
import shlex
@@ -860,7 +904,7 @@ class Distribution(_Distribution):
return nargs
- def get_cmdline_options(self):
+ def get_cmdline_options(self) -> dict[str, dict[str, str | None]]:
"""Return a '{cmd: {opt:val}}' map of all command-line options
Option names are all long, but do not include the leading '--', and
@@ -870,9 +914,10 @@ class Distribution(_Distribution):
Note that options provided by config files are intentionally excluded.
"""
- d = {}
+ d: dict[str, dict[str, str | None]] = {}
for cmd, opts in self.command_options.items():
+ val: str | None
for opt, (src, val) in opts.items():
if src != "command line":
continue
@@ -907,7 +952,7 @@ class Distribution(_Distribution):
for ext in self.ext_modules or ():
if isinstance(ext, tuple):
- name, buildinfo = ext
+ name, _buildinfo = ext
else:
name = ext.name
if name.endswith('module'):
@@ -942,7 +987,7 @@ class Distribution(_Distribution):
finally:
sys.stdout.reconfigure(encoding=encoding)
- def run_command(self, command):
+ def run_command(self, command) -> None:
self.set_defaults()
# Postpone defaults until all explicit configuration is considered
# (setup() args, config files, command line and plugins)
diff --git a/contrib/python/setuptools/py3/setuptools/errors.py b/contrib/python/setuptools/py3/setuptools/errors.py
index 90fcf7170e..990ecbf4e2 100644
--- a/contrib/python/setuptools/py3/setuptools/errors.py
+++ b/contrib/python/setuptools/py3/setuptools/errors.py
@@ -5,45 +5,40 @@ Provides exceptions used by setuptools modules.
from __future__ import annotations
-from typing import TYPE_CHECKING
-
from distutils import errors as _distutils_errors
-if TYPE_CHECKING:
- from typing_extensions import TypeAlias
-
# Re-export errors from distutils to facilitate the migration to PEP632
-ByteCompileError: TypeAlias = _distutils_errors.DistutilsByteCompileError
-CCompilerError: TypeAlias = _distutils_errors.CCompilerError
-ClassError: TypeAlias = _distutils_errors.DistutilsClassError
-CompileError: TypeAlias = _distutils_errors.CompileError
-ExecError: TypeAlias = _distutils_errors.DistutilsExecError
-FileError: TypeAlias = _distutils_errors.DistutilsFileError
-InternalError: TypeAlias = _distutils_errors.DistutilsInternalError
-LibError: TypeAlias = _distutils_errors.LibError
-LinkError: TypeAlias = _distutils_errors.LinkError
-ModuleError: TypeAlias = _distutils_errors.DistutilsModuleError
-OptionError: TypeAlias = _distutils_errors.DistutilsOptionError
-PlatformError: TypeAlias = _distutils_errors.DistutilsPlatformError
-PreprocessError: TypeAlias = _distutils_errors.PreprocessError
-SetupError: TypeAlias = _distutils_errors.DistutilsSetupError
-TemplateError: TypeAlias = _distutils_errors.DistutilsTemplateError
-UnknownFileError: TypeAlias = _distutils_errors.UnknownFileError
+ByteCompileError = _distutils_errors.DistutilsByteCompileError
+CCompilerError = _distutils_errors.CCompilerError
+ClassError = _distutils_errors.DistutilsClassError
+CompileError = _distutils_errors.CompileError
+ExecError = _distutils_errors.DistutilsExecError
+FileError = _distutils_errors.DistutilsFileError
+InternalError = _distutils_errors.DistutilsInternalError
+LibError = _distutils_errors.LibError
+LinkError = _distutils_errors.LinkError
+ModuleError = _distutils_errors.DistutilsModuleError
+OptionError = _distutils_errors.DistutilsOptionError
+PlatformError = _distutils_errors.DistutilsPlatformError
+PreprocessError = _distutils_errors.PreprocessError
+SetupError = _distutils_errors.DistutilsSetupError
+TemplateError = _distutils_errors.DistutilsTemplateError
+UnknownFileError = _distutils_errors.UnknownFileError
# The root error class in the hierarchy
-BaseError: TypeAlias = _distutils_errors.DistutilsError
+BaseError = _distutils_errors.DistutilsError
-class InvalidConfigError(OptionError):
+class InvalidConfigError(OptionError): # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+
"""Error used for invalid configurations."""
-class RemovedConfigError(OptionError):
+class RemovedConfigError(OptionError): # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+
"""Error used for configurations that were deprecated and removed."""
-class RemovedCommandError(BaseError, RuntimeError):
+class RemovedCommandError(BaseError, RuntimeError): # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+
"""Error used for commands that have been removed in setuptools.
Since ``setuptools`` is built on ``distutils``, simply removing a command
@@ -53,7 +48,7 @@ class RemovedCommandError(BaseError, RuntimeError):
"""
-class PackageDiscoveryError(BaseError, RuntimeError):
+class PackageDiscoveryError(BaseError, RuntimeError): # type: ignore[valid-type, misc] # distutils imports are `Any` on python 3.12+
"""Impossible to perform automatic discovery of packages and/or modules.
The current project layout or given discovery options can lead to problems when
diff --git a/contrib/python/setuptools/py3/setuptools/extension.py b/contrib/python/setuptools/py3/setuptools/extension.py
index cbefc72508..76e03d9d6b 100644
--- a/contrib/python/setuptools/py3/setuptools/extension.py
+++ b/contrib/python/setuptools/py3/setuptools/extension.py
@@ -4,6 +4,8 @@ import functools
import re
from typing import TYPE_CHECKING
+from setuptools._path import StrPath
+
from .monkey import get_unpatched
import distutils.core
@@ -27,10 +29,8 @@ def _have_cython():
# for compatibility
have_pyrex = _have_cython
if TYPE_CHECKING:
- from typing_extensions import TypeAlias
-
# Work around a mypy issue where type[T] can't be used as a base: https://github.com/python/mypy/issues/10962
- _Extension: TypeAlias = distutils.core.Extension
+ from distutils.core import Extension as _Extension
else:
_Extension = get_unpatched(distutils.core.Extension)
@@ -52,7 +52,7 @@ class Extension(_Extension):
the full name of the extension, including any packages -- ie.
*not* a filename or pathname, but Python dotted name
- :arg list[str] sources:
+ :arg list[str|os.PathLike[str]] sources:
list of source filenames, relative to the distribution root
(where the setup script lives), in Unix form (slash-separated)
for portability. Source files may be C, C++, SWIG (.i),
@@ -140,11 +140,23 @@ class Extension(_Extension):
_needs_stub: bool #: Private API, internal use only.
_file_name: str #: Private API, internal use only.
- def __init__(self, name: str, sources, *args, py_limited_api: bool = False, **kw):
+ def __init__(
+ self,
+ name: str,
+ sources: list[StrPath],
+ *args,
+ py_limited_api: bool = False,
+ **kw,
+ ) -> None:
# The *args is needed for compatibility as calls may use positional
# arguments. py_limited_api may be set only via keyword.
self.py_limited_api = py_limited_api
- super().__init__(name, sources, *args, **kw)
+ super().__init__(
+ name,
+ sources, # type: ignore[arg-type] # Vendored version of setuptools supports PathLike
+ *args,
+ **kw,
+ )
def _convert_pyx_sources_to_lang(self):
"""
diff --git a/contrib/python/setuptools/py3/setuptools/glob.py b/contrib/python/setuptools/py3/setuptools/glob.py
index ffe0ae92cb..1dfff2cd50 100644
--- a/contrib/python/setuptools/py3/setuptools/glob.py
+++ b/contrib/python/setuptools/py3/setuptools/glob.py
@@ -6,14 +6,21 @@ Changes include:
* Hidden files are not ignored.
"""
+from __future__ import annotations
+
import fnmatch
import os
import re
+from collections.abc import Iterable, Iterator
+from typing import TYPE_CHECKING, AnyStr, overload
+
+if TYPE_CHECKING:
+ from _typeshed import BytesPath, StrOrBytesPath, StrPath
__all__ = ["glob", "iglob", "escape"]
-def glob(pathname, recursive=False):
+def glob(pathname: AnyStr, recursive: bool = False) -> list[AnyStr]:
"""Return a list of paths matching a pathname pattern.
The pattern may contain simple shell-style wildcards a la
@@ -27,7 +34,7 @@ def glob(pathname, recursive=False):
return list(iglob(pathname, recursive=recursive))
-def iglob(pathname, recursive=False):
+def iglob(pathname: AnyStr, recursive: bool = False) -> Iterator[AnyStr]:
"""Return an iterator which yields the paths matching a pathname pattern.
The pattern may contain simple shell-style wildcards a la
@@ -45,7 +52,7 @@ def iglob(pathname, recursive=False):
return it
-def _iglob(pathname, recursive):
+def _iglob(pathname: AnyStr, recursive: bool) -> Iterator[AnyStr]:
dirname, basename = os.path.split(pathname)
glob_in_dir = glob2 if recursive and _isrecursive(basename) else glob1
@@ -66,7 +73,7 @@ def _iglob(pathname, recursive):
# drive or UNC path. Prevent an infinite recursion if a drive or UNC path
# contains magic characters (i.e. r'\\?\C:').
if dirname != pathname and has_magic(dirname):
- dirs = _iglob(dirname, recursive)
+ dirs: Iterable[AnyStr] = _iglob(dirname, recursive)
else:
dirs = [dirname]
if not has_magic(basename):
@@ -81,7 +88,11 @@ def _iglob(pathname, recursive):
# takes a literal basename (so it only has to check for its existence).
-def glob1(dirname, pattern):
+@overload
+def glob1(dirname: StrPath, pattern: str) -> list[str]: ...
+@overload
+def glob1(dirname: BytesPath, pattern: bytes) -> list[bytes]: ...
+def glob1(dirname: StrOrBytesPath, pattern: str | bytes) -> list[str] | list[bytes]:
if not dirname:
if isinstance(pattern, bytes):
dirname = os.curdir.encode('ASCII')
@@ -91,7 +102,8 @@ def glob1(dirname, pattern):
names = os.listdir(dirname)
except OSError:
return []
- return fnmatch.filter(names, pattern)
+ # mypy false-positives: str or bytes type possibility is always kept in sync
+ return fnmatch.filter(names, pattern) # type: ignore[type-var, return-value]
def glob0(dirname, basename):
@@ -110,14 +122,22 @@ def glob0(dirname, basename):
# directory.
-def glob2(dirname, pattern):
+@overload
+def glob2(dirname: StrPath, pattern: str) -> Iterator[str]: ...
+@overload
+def glob2(dirname: BytesPath, pattern: bytes) -> Iterator[bytes]: ...
+def glob2(dirname: StrOrBytesPath, pattern: str | bytes) -> Iterator[str | bytes]:
assert _isrecursive(pattern)
yield pattern[:0]
yield from _rlistdir(dirname)
# Recursively yields relative pathnames inside a literal directory.
-def _rlistdir(dirname):
+@overload
+def _rlistdir(dirname: StrPath) -> Iterator[str]: ...
+@overload
+def _rlistdir(dirname: BytesPath) -> Iterator[bytes]: ...
+def _rlistdir(dirname: StrOrBytesPath) -> Iterator[str | bytes]:
if not dirname:
if isinstance(dirname, bytes):
dirname = os.curdir.encode('ASCII')
@@ -129,24 +149,24 @@ def _rlistdir(dirname):
return
for x in names:
yield x
- path = os.path.join(dirname, x) if dirname else x
+ # mypy false-positives: str or bytes type possibility is always kept in sync
+ path = os.path.join(dirname, x) if dirname else x # type: ignore[arg-type]
for y in _rlistdir(path):
- yield os.path.join(x, y)
+ yield os.path.join(x, y) # type: ignore[arg-type]
magic_check = re.compile('([*?[])')
magic_check_bytes = re.compile(b'([*?[])')
-def has_magic(s):
+def has_magic(s: str | bytes) -> bool:
if isinstance(s, bytes):
- match = magic_check_bytes.search(s)
+ return magic_check_bytes.search(s) is not None
else:
- match = magic_check.search(s)
- return match is not None
+ return magic_check.search(s) is not None
-def _isrecursive(pattern):
+def _isrecursive(pattern: str | bytes) -> bool:
if isinstance(pattern, bytes):
return pattern == b'**'
else:
diff --git a/contrib/python/setuptools/py3/setuptools/installer.py b/contrib/python/setuptools/py3/setuptools/installer.py
index ce3559cd93..64bc2def07 100644
--- a/contrib/python/setuptools/py3/setuptools/installer.py
+++ b/contrib/python/setuptools/py3/setuptools/installer.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
import glob
import os
import subprocess
@@ -5,7 +7,10 @@ import sys
import tempfile
from functools import partial
+from pkg_resources import Distribution
+
from . import _reqs
+from ._reqs import _StrOrIter
from .warnings import SetuptoolsDeprecationWarning
from .wheel import Wheel
@@ -30,7 +35,7 @@ def fetch_build_egg(dist, req):
return _fetch_build_egg_no_warn(dist, req)
-def _fetch_build_eggs(dist, requires):
+def _fetch_build_eggs(dist, requires: _StrOrIter) -> list[Distribution]:
import pkg_resources # Delay import to avoid unnecessary side-effects
_DeprecatedInstaller.emit(stacklevel=3)
diff --git a/contrib/python/setuptools/py3/setuptools/launch.py b/contrib/python/setuptools/py3/setuptools/launch.py
index 56c7d035f1..0d162647d5 100644
--- a/contrib/python/setuptools/py3/setuptools/launch.py
+++ b/contrib/python/setuptools/py3/setuptools/launch.py
@@ -10,7 +10,7 @@ import sys
import tokenize
-def run():
+def run() -> None:
"""
Run the script in sys.argv[1] as if it had
been invoked naturally.
diff --git a/contrib/python/setuptools/py3/setuptools/logging.py b/contrib/python/setuptools/py3/setuptools/logging.py
index e9674c5a81..532da899f7 100644
--- a/contrib/python/setuptools/py3/setuptools/logging.py
+++ b/contrib/python/setuptools/py3/setuptools/logging.py
@@ -11,7 +11,7 @@ def _not_warning(record):
return record.levelno < logging.WARNING
-def configure():
+def configure() -> None:
"""
Configure logging to emit warning and above to stderr
and everything else to stdout. This behavior is provided
@@ -35,6 +35,6 @@ def configure():
distutils.dist.log = distutils.log
-def set_threshold(level):
+def set_threshold(level: int) -> int:
logging.root.setLevel(level * 10)
return set_threshold.unpatched(level)
diff --git a/contrib/python/setuptools/py3/setuptools/modified.py b/contrib/python/setuptools/py3/setuptools/modified.py
index 245a61580b..6ba02fab68 100644
--- a/contrib/python/setuptools/py3/setuptools/modified.py
+++ b/contrib/python/setuptools/py3/setuptools/modified.py
@@ -1,8 +1,18 @@
-from ._distutils._modified import (
- newer,
- newer_group,
- newer_pairwise,
- newer_pairwise_group,
-)
+try:
+ # Ensure a DistutilsError raised by these methods is the same as distutils.errors.DistutilsError
+ from distutils._modified import (
+ newer,
+ newer_group,
+ newer_pairwise,
+ newer_pairwise_group,
+ )
+except ImportError:
+ # fallback for SETUPTOOLS_USE_DISTUTILS=stdlib, because _modified never existed in stdlib
+ from ._distutils._modified import (
+ newer,
+ newer_group,
+ newer_pairwise,
+ newer_pairwise_group,
+ )
__all__ = ['newer', 'newer_pairwise', 'newer_group', 'newer_pairwise_group']
diff --git a/contrib/python/setuptools/py3/setuptools/monkey.py b/contrib/python/setuptools/py3/setuptools/monkey.py
index 3b6eefb4dd..d8e30dbb80 100644
--- a/contrib/python/setuptools/py3/setuptools/monkey.py
+++ b/contrib/python/setuptools/py3/setuptools/monkey.py
@@ -8,7 +8,7 @@ import inspect
import platform
import sys
import types
-from typing import Type, TypeVar, cast, overload
+from typing import TypeVar, cast, overload
import distutils.filelist
@@ -58,7 +58,7 @@ def get_unpatched_class(cls: type[_T]) -> type[_T]:
first.
"""
external_bases = (
- cast(Type[_T], cls)
+ cast(type[_T], cls)
for cls in _get_mro(cls)
if not cls.__module__.startswith('setuptools')
)
@@ -73,7 +73,7 @@ def patch_all():
import setuptools
# we can't patch distutils.cmd, alas
- distutils.core.Command = setuptools.Command
+ distutils.core.Command = setuptools.Command # type: ignore[misc,assignment] # monkeypatching
_patch_distribution_metadata()
@@ -82,8 +82,8 @@ def patch_all():
module.Distribution = setuptools.dist.Distribution
# Install the patched Extension
- distutils.core.Extension = setuptools.extension.Extension
- distutils.extension.Extension = setuptools.extension.Extension
+ distutils.core.Extension = setuptools.extension.Extension # type: ignore[misc,assignment] # monkeypatching
+ distutils.extension.Extension = setuptools.extension.Extension # type: ignore[misc,assignment] # monkeypatching
if 'distutils.command.build_ext' in sys.modules:
sys.modules[
'distutils.command.build_ext'
diff --git a/contrib/python/setuptools/py3/setuptools/msvc.py b/contrib/python/setuptools/py3/setuptools/msvc.py
index de4b05f928..8d6d2cf084 100644
--- a/contrib/python/setuptools/py3/setuptools/msvc.py
+++ b/contrib/python/setuptools/py3/setuptools/msvc.py
@@ -13,12 +13,15 @@ import json
import os
import os.path
import platform
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, TypedDict
from more_itertools import unique_everseen
import distutils.errors
+if TYPE_CHECKING:
+ from typing_extensions import LiteralString, NotRequired
+
# https://github.com/python/mypy/issues/8166
if not TYPE_CHECKING and platform.system() == 'Windows':
import winreg
@@ -47,7 +50,7 @@ class PlatformInfo:
current_cpu = environ.get('processor_architecture', '').lower()
- def __init__(self, arch):
+ def __init__(self, arch) -> None:
self.arch = arch.lower().replace('x64', 'amd64')
@property
@@ -84,7 +87,7 @@ class PlatformInfo:
"""
return self.current_cpu == 'x86'
- def current_dir(self, hidex86=False, x64=False):
+ def current_dir(self, hidex86=False, x64=False) -> str:
"""
Current platform specific subfolder.
@@ -108,7 +111,7 @@ class PlatformInfo:
else r'\%s' % self.current_cpu
)
- def target_dir(self, hidex86=False, x64=False):
+ def target_dir(self, hidex86=False, x64=False) -> str:
r"""
Target platform specific subfolder.
@@ -173,11 +176,11 @@ class RegistryInfo:
winreg.HKEY_CLASSES_ROOT,
)
- def __init__(self, platform_info):
+ def __init__(self, platform_info) -> None:
self.pi = platform_info
@property
- def visualstudio(self):
+ def visualstudio(self) -> str:
"""
Microsoft Visual Studio root registry key.
@@ -225,7 +228,7 @@ class RegistryInfo:
return os.path.join(self.sxs, 'VS7')
@property
- def vc_for_python(self):
+ def vc_for_python(self) -> str:
"""
Microsoft Visual C++ for Python registry key.
@@ -237,7 +240,7 @@ class RegistryInfo:
return r'DevDiv\VCForPython'
@property
- def microsoft_sdk(self):
+ def microsoft_sdk(self) -> str:
"""
Microsoft SDK registry key.
@@ -273,7 +276,7 @@ class RegistryInfo:
return os.path.join(self.microsoft_sdk, 'NETFXSDK')
@property
- def windows_kits_roots(self):
+ def windows_kits_roots(self) -> str:
"""
Microsoft Windows Kits Roots registry key.
@@ -363,7 +366,7 @@ class SystemInfo:
ProgramFiles = environ.get('ProgramFiles', '')
ProgramFilesx86 = environ.get('ProgramFiles(x86)', ProgramFiles)
- def __init__(self, registry_info, vc_ver=None):
+ def __init__(self, registry_info, vc_ver=None) -> None:
self.ri = registry_info
self.pi = self.ri.pi
@@ -423,7 +426,7 @@ class SystemInfo:
vs_vers.append(ver)
return sorted(vs_vers)
- def find_programdata_vs_vers(self):
+ def find_programdata_vs_vers(self) -> dict[float, str]:
r"""
Find Visual studio 2017+ versions from information in
"C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances".
@@ -433,7 +436,7 @@ class SystemInfo:
dict
float version as key, path as value.
"""
- vs_versions = {}
+ vs_versions: dict[float, str] = {}
instances_dir = r'C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances'
try:
@@ -570,7 +573,7 @@ class SystemInfo:
return self.ri.lookup(self.ri.vc, '%0.1f' % self.vs_ver) or default_vc
@property
- def WindowsSdkVersion(self):
+ def WindowsSdkVersion(self) -> tuple[LiteralString, ...]:
"""
Microsoft Windows SDK versions for specified MSVC++ version.
@@ -589,7 +592,7 @@ class SystemInfo:
return '8.1', '8.1a'
elif self.vs_ver >= 14.0:
return '10.0', '8.1'
- return None
+ return ()
@property
def WindowsSdkLastVersion(self):
@@ -604,7 +607,7 @@ class SystemInfo:
return self._use_last_dir_name(os.path.join(self.WindowsSdkDir, 'lib'))
@property
- def WindowsSdkDir(self): # noqa: C901 # is too complex (12) # FIXME
+ def WindowsSdkDir(self) -> str | None: # noqa: C901 # is too complex (12) # FIXME
"""
Microsoft Windows SDK directory.
@@ -613,7 +616,7 @@ class SystemInfo:
str
path
"""
- sdkdir = ''
+ sdkdir: str | None = ''
for ver in self.WindowsSdkVersion:
# Try to get it from registry
loc = os.path.join(self.ri.windows_sdk, 'v%s' % ver)
@@ -797,7 +800,7 @@ class SystemInfo:
return self.ri.lookup(self.ri.vc, 'frameworkdir64') or guess_fw
@property
- def FrameworkVersion32(self):
+ def FrameworkVersion32(self) -> tuple[str, ...]:
"""
Microsoft .NET Framework 32bit versions.
@@ -809,7 +812,7 @@ class SystemInfo:
return self._find_dot_net_versions(32)
@property
- def FrameworkVersion64(self):
+ def FrameworkVersion64(self) -> tuple[str, ...]:
"""
Microsoft .NET Framework 64bit versions.
@@ -820,7 +823,7 @@ class SystemInfo:
"""
return self._find_dot_net_versions(64)
- def _find_dot_net_versions(self, bits):
+ def _find_dot_net_versions(self, bits) -> tuple[str, ...]:
"""
Find Microsoft .NET Framework versions.
@@ -848,7 +851,7 @@ class SystemInfo:
return 'v3.5', 'v2.0.50727'
elif self.vs_ver == 8.0:
return 'v3.0', 'v2.0.50727'
- return None
+ return ()
@staticmethod
def _use_last_dir_name(path, prefix=''):
@@ -876,6 +879,14 @@ class SystemInfo:
return next(matching_dirs, None) or ''
+class _EnvironmentDict(TypedDict):
+ include: str
+ lib: str
+ libpath: str
+ path: str
+ py_vcruntime_redist: NotRequired[str | None]
+
+
class EnvironmentInfo:
"""
Return environment variables for specified Microsoft Visual C++ version
@@ -900,7 +911,7 @@ class EnvironmentInfo:
# Variables and properties in this class use originals CamelCase variables
# names from Microsoft source files for more easy comparison.
- def __init__(self, arch, vc_ver=None, vc_min_ver=0):
+ def __init__(self, arch, vc_ver=None, vc_min_ver=0) -> None:
self.pi = PlatformInfo(arch)
self.ri = RegistryInfo(self.pi)
self.si = SystemInfo(self.ri, vc_ver)
@@ -1418,9 +1429,9 @@ class EnvironmentInfo:
os.path.join(prefix, arch_subdir, crt_dir, vcruntime)
for (prefix, crt_dir) in itertools.product(prefixes, crt_dirs)
)
- return next(filter(os.path.isfile, candidate_paths), None)
+ return next(filter(os.path.isfile, candidate_paths), None) # type: ignore[arg-type] #python/mypy#12682
- def return_env(self, exists=True):
+ def return_env(self, exists: bool = True) -> _EnvironmentDict:
"""
Return environment dict.
@@ -1434,7 +1445,7 @@ class EnvironmentInfo:
dict
environment
"""
- env = dict(
+ env = _EnvironmentDict(
include=self._build_paths(
'include',
[
diff --git a/contrib/python/setuptools/py3/setuptools/namespaces.py b/contrib/python/setuptools/py3/setuptools/namespaces.py
index 299fdd9479..85ea2ebd65 100644
--- a/contrib/python/setuptools/py3/setuptools/namespaces.py
+++ b/contrib/python/setuptools/py3/setuptools/namespaces.py
@@ -11,7 +11,7 @@ flatten = itertools.chain.from_iterable
class Installer:
nspkg_ext = '-nspkg.pth'
- def install_namespaces(self):
+ def install_namespaces(self) -> None:
nsp = self._get_all_ns_packages()
if not nsp:
return
@@ -30,7 +30,7 @@ class Installer:
# See: python/cpython#77102
f.writelines(lines)
- def uninstall_namespaces(self):
+ def uninstall_namespaces(self) -> None:
filename = self._get_nspkg_file()
if not os.path.exists(filename):
return
diff --git a/contrib/python/setuptools/py3/setuptools/package_index.py b/contrib/python/setuptools/py3/setuptools/package_index.py
index 9e01d5e082..97806e8ff8 100644
--- a/contrib/python/setuptools/py3/setuptools/package_index.py
+++ b/contrib/python/setuptools/py3/setuptools/package_index.py
@@ -1,5 +1,7 @@
"""PyPI and direct package downloading."""
+from __future__ import annotations
+
import base64
import configparser
import hashlib
@@ -64,10 +66,7 @@ __all__ = [
_SOCKET_TIMEOUT = 15
-_tmpl = "setuptools/{setuptools.__version__} Python-urllib/{py_major}"
-user_agent = _tmpl.format(
- py_major='{}.{}'.format(*sys.version_info), setuptools=setuptools
-)
+user_agent = f"setuptools/{setuptools.__version__} Python-urllib/{sys.version_info.major}.{sys.version_info.minor}"
def parse_requirement_arg(spec):
@@ -105,7 +104,7 @@ def parse_bdist_wininst(name):
def egg_info_for_url(url):
parts = urllib.parse.urlparse(url)
- scheme, server, path, parameters, query, fragment = parts
+ _scheme, server, path, _parameters, _query, fragment = parts
base = urllib.parse.unquote(path.split('/')[-1])
if server == 'sourceforge.net' and base == 'download': # XXX Yuck
base = urllib.parse.unquote(path.split('/')[-2])
@@ -271,7 +270,7 @@ class HashChecker(ContentChecker):
r'(?P<expected>[a-f0-9]+)'
)
- def __init__(self, hash_name, expected):
+ def __init__(self, hash_name, expected) -> None:
self.hash_name = hash_name
self.hash = hashlib.new(hash_name)
self.expected = expected
@@ -303,20 +302,20 @@ class PackageIndex(Environment):
def __init__(
self,
- index_url="https://pypi.org/simple/",
+ index_url: str = "https://pypi.org/simple/",
hosts=('*',),
ca_bundle=None,
- verify_ssl=True,
+ verify_ssl: bool = True,
*args,
**kw,
- ):
+ ) -> None:
super().__init__(*args, **kw)
self.index_url = index_url + "/"[: not index_url.endswith('/')]
- self.scanned_urls = {}
- self.fetched_urls = {}
- self.package_pages = {}
+ self.scanned_urls: dict = {}
+ self.fetched_urls: dict = {}
+ self.package_pages: dict = {}
self.allows = re.compile('|'.join(map(translate, hosts))).match
- self.to_scan = []
+ self.to_scan: list = []
self.opener = urllib.request.urlopen
def add(self, dist):
@@ -328,7 +327,7 @@ class PackageIndex(Environment):
return super().add(dist)
# FIXME: 'PackageIndex.process_url' is too complex (14)
- def process_url(self, url, retrieve=False): # noqa: C901
+ def process_url(self, url, retrieve: bool = False) -> None: # noqa: C901
"""Evaluate a URL as a possible download, and maybe retrieve it"""
if url in self.scanned_urls and not retrieve:
return
@@ -381,7 +380,7 @@ class PackageIndex(Environment):
if url.startswith(self.index_url) and getattr(f, 'code', None) != 404:
page = self.process_index(url, page)
- def process_filename(self, fn, nested=False):
+ def process_filename(self, fn, nested: bool = False) -> None:
# process filenames or directories
if not os.path.exists(fn):
self.warn("Not found: %s", fn)
@@ -397,7 +396,7 @@ class PackageIndex(Environment):
self.debug("Found: %s", fn)
list(map(self.add, dists))
- def url_ok(self, url, fatal=False):
+ def url_ok(self, url, fatal: bool = False) -> bool:
s = URL_SCHEME(url)
is_file = s and s.group(1).lower() == 'file'
if is_file or self.allows(urllib.parse.urlparse(url)[1]):
@@ -413,7 +412,7 @@ class PackageIndex(Environment):
self.warn(msg, url)
return False
- def scan_egg_links(self, search_path):
+ def scan_egg_links(self, search_path) -> None:
dirs = filter(os.path.isdir, search_path)
egg_links = (
(path, entry)
@@ -423,7 +422,7 @@ class PackageIndex(Environment):
)
list(itertools.starmap(self.scan_egg_link, egg_links))
- def scan_egg_link(self, path, entry):
+ def scan_egg_link(self, path, entry) -> None:
content = _read_utf8_with_fallback(os.path.join(path, entry))
# filter non-empty lines
lines = list(filter(None, map(str.strip, content.splitlines())))
@@ -432,7 +431,7 @@ class PackageIndex(Environment):
# format is not recognized; punt
return
- egg_path, setup_path = lines
+ egg_path, _setup_path = lines
for dist in find_distributions(os.path.join(path, egg_path)):
dist.location = os.path.join(path, *lines)
@@ -484,21 +483,21 @@ class PackageIndex(Environment):
lambda m: '<a href="%s#md5=%s">%s</a>' % m.group(1, 3, 2), page
)
- def need_version_info(self, url):
+ def need_version_info(self, url) -> None:
self.scan_all(
"Page at %s links to .py file(s) without version info; an index "
"scan is required.",
url,
)
- def scan_all(self, msg=None, *args):
+ def scan_all(self, msg=None, *args) -> None:
if self.index_url not in self.fetched_urls:
if msg:
self.warn(msg, *args)
self.info("Scanning index of all packages (this may take a while)")
self.scan_url(self.index_url)
- def find_packages(self, requirement):
+ def find_packages(self, requirement) -> None:
self.scan_url(self.index_url + requirement.unsafe_name + '/')
if not self.package_pages.get(requirement.key):
@@ -522,7 +521,7 @@ class PackageIndex(Environment):
self.debug("%s does not match %s", requirement, dist)
return super().obtain(requirement, installer)
- def check_hash(self, checker, filename, tfp):
+ def check_hash(self, checker, filename, tfp) -> None:
"""
checker is a ContentChecker
"""
@@ -536,7 +535,7 @@ class PackageIndex(Environment):
% (checker.hash.name, os.path.basename(filename))
)
- def add_find_links(self, urls):
+ def add_find_links(self, urls) -> None:
"""Add `urls` to the list that will be prescanned for searches"""
for url in urls:
if (
@@ -557,14 +556,11 @@ class PackageIndex(Environment):
list(map(self.scan_url, self.to_scan))
self.to_scan = None # from now on, go ahead and process immediately
- def not_found_in_index(self, requirement):
+ def not_found_in_index(self, requirement) -> None:
if self[requirement.key]: # we've seen at least one distro
meth, msg = self.info, "Couldn't retrieve index page for %r"
else: # no distros seen for this name, might be misspelled
- meth, msg = (
- self.warn,
- "Couldn't find index page for %r (maybe misspelled?)",
- )
+ meth, msg = self.warn, "Couldn't find index page for %r (maybe misspelled?)"
meth(msg, requirement.unsafe_name)
self.scan_all()
@@ -606,11 +602,11 @@ class PackageIndex(Environment):
self,
requirement,
tmpdir,
- force_scan=False,
- source=False,
- develop_ok=False,
+ force_scan: bool = False,
+ source: bool = False,
+ develop_ok: bool = False,
local_index=None,
- ):
+ ) -> Distribution | None:
"""Obtain a distribution suitable for fulfilling `requirement`
`requirement` must be a ``pkg_resources.Requirement`` instance.
@@ -632,7 +628,7 @@ class PackageIndex(Environment):
skipped = set()
dist = None
- def find(req, env=None):
+ def find(req, env: Environment | None = None):
if env is None:
env = self
# Find a matching distribution; may be called more than once
@@ -684,7 +680,9 @@ class PackageIndex(Environment):
self.info("Best match: %s", dist)
return dist.clone(location=dist.download_location)
- def fetch(self, requirement, tmpdir, force_scan=False, source=False):
+ def fetch(
+ self, requirement, tmpdir, force_scan: bool = False, source: bool = False
+ ) -> str | None:
"""Obtain a file suitable for fulfilling `requirement`
DEPRECATED; use the ``fetch_distribution()`` method now instead. For
@@ -781,7 +779,7 @@ class PackageIndex(Environment):
if fp:
fp.close()
- def reporthook(self, url, filename, blocknum, blksize, size):
+ def reporthook(self, url, filename, blocknum, blksize, size) -> None:
pass # no-op
# FIXME:
@@ -822,7 +820,7 @@ class PackageIndex(Environment):
def _download_url(self, url, tmpdir):
# Determine download filename
#
- name, fragment = egg_info_for_url(url)
+ name, _fragment = egg_info_for_url(url)
if name:
while '..' in name:
name = name.replace('..', '.').replace('\\', '_')
@@ -850,7 +848,7 @@ class PackageIndex(Environment):
>>> rvcs('http://foo/bar')
"""
scheme = urllib.parse.urlsplit(url).scheme
- pre, sep, post = scheme.partition('+')
+ pre, sep, _post = scheme.partition('+')
# svn and git have their own protocol; hg does not
allowed = set(['svn', 'git'] + ['hg'] * bool(sep))
return next(iter({pre} & allowed), None)
@@ -888,7 +886,7 @@ class PackageIndex(Environment):
self.url_ok(url, True)
return self._attempt_download(url, filename)
- def scan_url(self, url):
+ def scan_url(self, url) -> None:
self.process_url(url, True)
def _attempt_download(self, url, filename):
@@ -934,13 +932,13 @@ class PackageIndex(Environment):
return resolved, rev
- def debug(self, msg, *args):
+ def debug(self, msg, *args) -> None:
log.debug(msg, *args)
- def info(self, msg, *args):
+ def info(self, msg, *args) -> None:
log.info(msg, *args)
- def warn(self, msg, *args):
+ def warn(self, msg, *args) -> None:
log.warn(msg, *args)
@@ -1105,6 +1103,7 @@ def open_with_auth(url, opener=urllib.request.urlopen):
# copy of urllib.parse._splituser from Python 3.8
+# See https://github.com/python/cpython/issues/80072.
def _splituser(host):
"""splituser('user[:passwd]@host[:port]')
--> 'user[:passwd]', 'host[:port]'."""
@@ -1122,7 +1121,7 @@ def fix_sf_url(url):
def local_open(url):
"""Read a local path, with special support for directories"""
- scheme, server, path, param, query, frag = urllib.parse.urlparse(url)
+ _scheme, _server, path, _param, _query, _frag = urllib.parse.urlparse(url)
filename = urllib.request.url2pathname(path)
if os.path.isfile(filename):
return urllib.request.urlopen(url)
diff --git a/contrib/python/setuptools/py3/setuptools/sandbox.py b/contrib/python/setuptools/py3/setuptools/sandbox.py
index 9c2c78a32c..2d84242d66 100644
--- a/contrib/python/setuptools/py3/setuptools/sandbox.py
+++ b/contrib/python/setuptools/py3/setuptools/sandbox.py
@@ -12,14 +12,16 @@ import sys
import tempfile
import textwrap
from types import TracebackType
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Any, ClassVar
import pkg_resources
from pkg_resources import working_set
from distutils.errors import DistutilsError
-if sys.platform.startswith('java'):
+if TYPE_CHECKING:
+ import os as _os
+elif sys.platform.startswith('java'):
import org.python.modules.posix.PosixModule as _os # pyright: ignore[reportMissingImports]
else:
_os = sys.modules[os.name]
@@ -148,7 +150,7 @@ class ExceptionSaver:
if '_saved' not in vars(self):
return
- type, exc = map(pickle.loads, self._saved)
+ _type, exc = map(pickle.loads, self._saved)
raise exc.with_traceback(self._tb)
@@ -277,7 +279,7 @@ class AbstractSandbox:
_active = False
- def __init__(self):
+ def __init__(self) -> None:
self._attrs = [
name
for name in dir(_os)
@@ -295,10 +297,10 @@ class AbstractSandbox:
def __exit__(
self,
- exc_type: object,
- exc_value: object,
- traceback: object,
- ) -> None:
+ exc_type: type[BaseException] | None,
+ exc_value: BaseException | None,
+ traceback: TracebackType | None,
+ ):
self._active = False
builtins.open = _open
self._copy(_os)
@@ -406,6 +408,11 @@ class AbstractSandbox:
self._remap_input(operation + '-to', dst, *args, **kw),
)
+ if TYPE_CHECKING:
+ # This is a catch-all for all the dynamically created attributes.
+ # This isn't public API anyway
+ def __getattribute__(self, name: str) -> Any: ...
+
if hasattr(os, 'devnull'):
_EXCEPTIONS = [os.devnull]
@@ -416,7 +423,7 @@ else:
class DirectorySandbox(AbstractSandbox):
"""Restrict operations to a single subdirectory - pseudo-chroot"""
- write_ops = dict.fromkeys([
+ write_ops: ClassVar[dict[str, None]] = dict.fromkeys([
"open",
"chmod",
"chown",
@@ -435,7 +442,7 @@ class DirectorySandbox(AbstractSandbox):
_exception_patterns: list[str | re.Pattern] = []
"exempt writing to paths that match the pattern"
- def __init__(self, sandbox, exceptions=_EXCEPTIONS):
+ def __init__(self, sandbox, exceptions=_EXCEPTIONS) -> None:
self._sandbox = os.path.normcase(os.path.realpath(sandbox))
self._prefix = os.path.join(self._sandbox, '')
self._exceptions = [
@@ -453,7 +460,7 @@ class DirectorySandbox(AbstractSandbox):
self._violation("open", path, mode, *args, **kw)
return _open(path, mode, *args, **kw)
- def tmpnam(self):
+ def tmpnam(self) -> None:
self._violation("tmpnam")
def _ok(self, path):
@@ -491,7 +498,7 @@ class DirectorySandbox(AbstractSandbox):
self._violation(operation, src, dst, *args, **kw)
return (src, dst)
- def open(self, file, flags, mode=0o777, *args, **kw):
+ def open(self, file, flags, mode: int = 0o777, *args, **kw) -> int:
"""Called for low-level os.open()"""
if flags & WRITE_FLAGS and not self._ok(file):
self._violation("os.open", file, flags, mode, *args, **kw)
diff --git a/contrib/python/setuptools/py3/setuptools/unicode_utils.py b/contrib/python/setuptools/py3/setuptools/unicode_utils.py
index 862d79e898..a6e33f2e0d 100644
--- a/contrib/python/setuptools/py3/setuptools/unicode_utils.py
+++ b/contrib/python/setuptools/py3/setuptools/unicode_utils.py
@@ -1,6 +1,6 @@
import sys
import unicodedata
-from configparser import ConfigParser
+from configparser import RawConfigParser
from .compat import py39
from .warnings import SetuptoolsDeprecationWarning
@@ -65,10 +65,10 @@ def _read_utf8_with_fallback(file: str, fallback_encoding=py39.LOCALE_ENCODING)
def _cfg_read_utf8_with_fallback(
- cfg: ConfigParser, file: str, fallback_encoding=py39.LOCALE_ENCODING
+ cfg: RawConfigParser, file: str, fallback_encoding=py39.LOCALE_ENCODING
) -> None:
"""Same idea as :func:`_read_utf8_with_fallback`, but for the
- :meth:`ConfigParser.read` method.
+ :meth:`RawConfigParser.read` method.
This method may call ``cfg.clear()``.
"""
diff --git a/contrib/python/setuptools/py3/setuptools/warnings.py b/contrib/python/setuptools/py3/setuptools/warnings.py
index 8c94bc96e6..96467787c2 100644
--- a/contrib/python/setuptools/py3/setuptools/warnings.py
+++ b/contrib/python/setuptools/py3/setuptools/warnings.py
@@ -12,9 +12,12 @@ import warnings
from datetime import date
from inspect import cleandoc
from textwrap import indent
-from typing import Tuple
+from typing import TYPE_CHECKING
-_DueDate = Tuple[int, int, int] # time tuple
+if TYPE_CHECKING:
+ from typing_extensions import TypeAlias
+
+_DueDate: TypeAlias = tuple[int, int, int] # time tuple
_INDENT = 8 * " "
_TEMPLATE = f"""{80 * '*'}\n{{details}}\n{80 * '*'}"""
diff --git a/contrib/python/setuptools/py3/setuptools/wheel.py b/contrib/python/setuptools/py3/setuptools/wheel.py
index 69a73df244..fb19f1a65a 100644
--- a/contrib/python/setuptools/py3/setuptools/wheel.py
+++ b/contrib/python/setuptools/py3/setuptools/wheel.py
@@ -31,7 +31,7 @@ WHEEL_NAME = re.compile(
NAMESPACE_PACKAGE_INIT = "__import__('pkg_resources').declare_namespace(__name__)\n"
-@functools.lru_cache(maxsize=None)
+@functools.cache
def _get_supported_tags():
# We calculate the supported tags only once, otherwise calling
# this method on thousands of wheels takes seconds instead of
@@ -39,7 +39,7 @@ def _get_supported_tags():
return {(t.interpreter, t.abi, t.platform) for t in sys_tags()}
-def unpack(src_dir, dst_dir):
+def unpack(src_dir, dst_dir) -> None:
"""Move everything under `src_dir` to `dst_dir`, and delete the former."""
for dirpath, dirnames, filenames in os.walk(src_dir):
subdir = os.path.relpath(dirpath, src_dir)
@@ -76,7 +76,7 @@ def disable_info_traces():
class Wheel:
- def __init__(self, filename):
+ def __init__(self, filename) -> None:
match = WHEEL_NAME(os.path.basename(filename))
if match is None:
raise ValueError('invalid wheel name: %r' % filename)
@@ -116,7 +116,7 @@ class Wheel:
return dirname
raise ValueError("unsupported wheel format. .dist-info not found")
- def install_as_egg(self, destination_eggdir):
+ def install_as_egg(self, destination_eggdir) -> None:
"""Install wheel as an egg directory."""
with zipfile.ZipFile(self.filename) as zf:
self._install_as_egg(destination_eggdir, zf)
diff --git a/contrib/python/setuptools/py3/ya.make b/contrib/python/setuptools/py3/ya.make
index 2f3f3937c2..a6375ff2b8 100644
--- a/contrib/python/setuptools/py3/ya.make
+++ b/contrib/python/setuptools/py3/ya.make
@@ -2,11 +2,12 @@
PY3_LIBRARY()
-VERSION(74.1.3)
+VERSION(75.6.0)
LICENSE(MIT)
PEERDIR(
+ contrib/python/jaraco.collections
contrib/python/jaraco.context
contrib/python/jaraco.functools
contrib/python/jaraco.text
@@ -35,15 +36,11 @@ PY_SRCS(
setuptools/__init__.py
setuptools/_core_metadata.py
setuptools/_distutils/__init__.py
- setuptools/_distutils/_collections.py
- setuptools/_distutils/_functools.py
- setuptools/_distutils/_itertools.py
setuptools/_distutils/_log.py
setuptools/_distutils/_macos_compat.py
setuptools/_distutils/_modified.py
setuptools/_distutils/_msvccompiler.py
setuptools/_distutils/archive_util.py
- setuptools/_distutils/bcppcompiler.py
setuptools/_distutils/ccompiler.py
setuptools/_distutils/cmd.py
setuptools/_distutils/command/__init__.py
@@ -65,13 +62,10 @@ PY_SRCS(
setuptools/_distutils/command/install_headers.py
setuptools/_distutils/command/install_lib.py
setuptools/_distutils/command/install_scripts.py
- setuptools/_distutils/command/register.py
setuptools/_distutils/command/sdist.py
- setuptools/_distutils/command/upload.py
setuptools/_distutils/compat/__init__.py
setuptools/_distutils/compat/py38.py
setuptools/_distutils/compat/py39.py
- setuptools/_distutils/config.py
setuptools/_distutils/core.py
setuptools/_distutils/cygwinccompiler.py
setuptools/_distutils/debug.py
@@ -99,6 +93,7 @@ PY_SRCS(
setuptools/_normalization.py
setuptools/_path.py
setuptools/_reqs.py
+ setuptools/_shutil.py
setuptools/archive_util.py
setuptools/build_meta.py
setuptools/command/__init__.py
@@ -120,14 +115,11 @@ PY_SRCS(
setuptools/command/install_egg_info.py
setuptools/command/install_lib.py
setuptools/command/install_scripts.py
- setuptools/command/register.py
setuptools/command/rotate.py
setuptools/command/saveopts.py
setuptools/command/sdist.py
setuptools/command/setopt.py
setuptools/command/test.py
- setuptools/command/upload.py
- setuptools/command/upload_docs.py
setuptools/compat/__init__.py
setuptools/compat/py310.py
setuptools/compat/py311.py
diff --git a/yt/yt/library/program/program-inl.h b/yt/yt/library/program/program-inl.h
index bff3bc2b1f..baf71791d0 100644
--- a/yt/yt/library/program/program-inl.h
+++ b/yt/yt/library/program/program-inl.h
@@ -8,6 +8,24 @@ namespace NYT {
////////////////////////////////////////////////////////////////////////////////
+template <class T>
+T FromStringArgMapper(TStringBuf arg)
+{
+ T result;
+ if (!T::FromString(arg, &result)) {
+ throw TProgramException(Format("Error parsing %Qv", arg));
+ }
+ return result;
+}
+
+template <class T>
+T ParseEnumArgMapper(TStringBuf arg)
+{
+ return ParseEnum<T>(arg);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
template <class E>
requires std::is_enum_v<E>
void TProgram::Abort(E exitCode) noexcept
diff --git a/yt/yt/library/program/program.cpp b/yt/yt/library/program/program.cpp
index 5c13a6d09c..5d0c9795ef 100644
--- a/yt/yt/library/program/program.cpp
+++ b/yt/yt/library/program/program.cpp
@@ -239,15 +239,6 @@ TString CheckPathExistsArgMapper(const TString& arg)
return arg;
}
-TGuid CheckGuidArgMapper(const TString& arg)
-{
- TGuid result;
- if (!TGuid::FromString(arg, &result)) {
- throw TProgramException(Format("Error parsing guid %Qv", arg));
- }
- return result;
-}
-
NYson::TYsonString CheckYsonArgMapper(const TString& arg)
{
ParseYsonStringBuffer(arg, EYsonType::Node, GetNullYsonConsumer());
diff --git a/yt/yt/library/program/program.h b/yt/yt/library/program/program.h
index 1f47ce93b7..7104326bda 100644
--- a/yt/yt/library/program/program.h
+++ b/yt/yt/library/program/program.h
@@ -112,8 +112,13 @@ private:
//! Helper for TOpt::StoreMappedResult to validate file paths for existence.
TString CheckPathExistsArgMapper(const TString& arg);
-//! Helper for TOpt::StoreMappedResult to parse GUIDs.
-TGuid CheckGuidArgMapper(const TString& arg);
+//! Helper for TOpt::StoreMappedResult to parse types with #FromString.
+template <class T>
+T FromStringArgMapper(TStringBuf arg);
+
+//! Helper for TOpt::StoreMappedResult to parse enums.
+template <class T>
+T ParseEnumArgMapper(TStringBuf arg);
//! Helper for TOpt::StoreMappedResult to parse YSON strings.
NYson::TYsonString CheckYsonArgMapper(const TString& arg);