summaryrefslogtreecommitdiff
path: root/setuptools
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2022-01-29 17:19:40 -0500
committerJason R. Coombs <jaraco@jaraco.com>2022-01-29 17:19:40 -0500
commit9ce6e03a9489cf79a57ee464db6670d9f28af085 (patch)
tree988cc8d136bfb8f7f7ec93a9f958887f184e6f8e /setuptools
parent4d660833d040b20d2923fb1077c9cc5525943568 (diff)
parent4156c0d108f5efd43da6ca851a2d801e9943394a (diff)
downloadpython-setuptools-git-9ce6e03a9489cf79a57ee464db6670d9f28af085.tar.gz
Merge branch 'main' into missing_metadata
Diffstat (limited to 'setuptools')
-rw-r--r--setuptools/__init__.py6
-rw-r--r--setuptools/_distutils/__init__.py11
-rw-r--r--setuptools/_distutils/_collections.py56
-rw-r--r--setuptools/_distutils/_msvccompiler.py2
-rw-r--r--setuptools/_distutils/ccompiler.py2
-rw-r--r--setuptools/_distutils/command/build_ext.py4
-rw-r--r--setuptools/_distutils/command/install.py261
-rw-r--r--setuptools/_distutils/command/install_egg_info.py15
-rw-r--r--setuptools/_distutils/core.py55
-rw-r--r--setuptools/_distutils/cygwinccompiler.py152
-rw-r--r--setuptools/_distutils/log.py6
-rw-r--r--setuptools/_distutils/msvc9compiler.py4
-rw-r--r--setuptools/_distutils/spawn.py2
-rw-r--r--setuptools/_distutils/sysconfig.py67
-rw-r--r--setuptools/_distutils/tests/py38compat.py9
-rw-r--r--setuptools/_distutils/tests/test_archive_util.py14
-rw-r--r--setuptools/_distutils/tests/test_bdist.py2
-rw-r--r--setuptools/_distutils/tests/test_bdist_dumb.py2
-rw-r--r--setuptools/_distutils/tests/test_bdist_msi.py2
-rw-r--r--setuptools/_distutils/tests/test_bdist_rpm.py11
-rw-r--r--setuptools/_distutils/tests/test_bdist_wininst.py2
-rw-r--r--setuptools/_distutils/tests/test_build.py2
-rw-r--r--setuptools/_distutils/tests/test_build_clib.py2
-rw-r--r--setuptools/_distutils/tests/test_build_ext.py12
-rw-r--r--setuptools/_distutils/tests/test_build_py.py2
-rw-r--r--setuptools/_distutils/tests/test_build_scripts.py2
-rw-r--r--setuptools/_distutils/tests/test_check.py2
-rw-r--r--setuptools/_distutils/tests/test_clean.py2
-rw-r--r--setuptools/_distutils/tests/test_cmd.py2
-rw-r--r--setuptools/_distutils/tests/test_config.py2
-rw-r--r--setuptools/_distutils/tests/test_config_cmd.py2
-rw-r--r--setuptools/_distutils/tests/test_core.py27
-rw-r--r--setuptools/_distutils/tests/test_cygwinccompiler.py70
-rw-r--r--setuptools/_distutils/tests/test_dep_util.py2
-rw-r--r--setuptools/_distutils/tests/test_dir_util.py2
-rw-r--r--setuptools/_distutils/tests/test_dist.py4
-rw-r--r--setuptools/_distutils/tests/test_extension.py2
-rw-r--r--setuptools/_distutils/tests/test_file_util.py2
-rw-r--r--setuptools/_distutils/tests/test_filelist.py4
-rw-r--r--setuptools/_distutils/tests/test_install.py4
-rw-r--r--setuptools/_distutils/tests/test_install_data.py2
-rw-r--r--setuptools/_distutils/tests/test_install_headers.py2
-rw-r--r--setuptools/_distutils/tests/test_install_lib.py2
-rw-r--r--setuptools/_distutils/tests/test_install_scripts.py2
-rw-r--r--setuptools/_distutils/tests/test_log.py2
-rw-r--r--setuptools/_distutils/tests/test_msvc9compiler.py2
-rw-r--r--setuptools/_distutils/tests/test_msvccompiler.py4
-rw-r--r--setuptools/_distutils/tests/test_register.py2
-rw-r--r--setuptools/_distutils/tests/test_sdist.py13
-rw-r--r--setuptools/_distutils/tests/test_spawn.py2
-rw-r--r--setuptools/_distutils/tests/test_sysconfig.py17
-rw-r--r--setuptools/_distutils/tests/test_text_file.py2
-rw-r--r--setuptools/_distutils/tests/test_unixccompiler.py20
-rw-r--r--setuptools/_distutils/tests/test_upload.py2
-rw-r--r--setuptools/_distutils/tests/test_util.py127
-rw-r--r--setuptools/_distutils/tests/test_version.py10
-rw-r--r--setuptools/_distutils/tests/unix_compat.py16
-rw-r--r--setuptools/_distutils/unixccompiler.py25
-rw-r--r--setuptools/_distutils/util.py135
-rw-r--r--setuptools/_distutils/version.py28
-rw-r--r--setuptools/_distutils/versionpredicate.py7
-rw-r--r--setuptools/_vendor/more_itertools-8.8.0.dist-info/INSTALLER1
-rw-r--r--setuptools/_vendor/more_itertools-8.8.0.dist-info/LICENSE19
-rw-r--r--setuptools/_vendor/more_itertools-8.8.0.dist-info/METADATA462
-rw-r--r--setuptools/_vendor/more_itertools-8.8.0.dist-info/RECORD17
-rw-r--r--setuptools/_vendor/more_itertools-8.8.0.dist-info/REQUESTED0
-rw-r--r--setuptools/_vendor/more_itertools-8.8.0.dist-info/WHEEL5
-rw-r--r--setuptools/_vendor/more_itertools-8.8.0.dist-info/top_level.txt1
-rw-r--r--setuptools/_vendor/ordered_set-3.1.1.dist-info/INSTALLER1
-rw-r--r--setuptools/_vendor/ordered_set-3.1.1.dist-info/METADATA157
-rw-r--r--setuptools/_vendor/ordered_set-3.1.1.dist-info/MIT-LICENSE19
-rw-r--r--setuptools/_vendor/ordered_set-3.1.1.dist-info/RECORD9
-rw-r--r--setuptools/_vendor/ordered_set-3.1.1.dist-info/REQUESTED0
-rw-r--r--setuptools/_vendor/ordered_set-3.1.1.dist-info/WHEEL6
-rw-r--r--setuptools/_vendor/ordered_set-3.1.1.dist-info/top_level.txt1
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/INSTALLER1
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/LICENSE3
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/LICENSE.APACHE177
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/LICENSE.BSD23
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/METADATA446
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/RECORD32
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/REQUESTED0
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/WHEEL5
-rw-r--r--setuptools/_vendor/packaging-21.2.dist-info/top_level.txt1
-rw-r--r--setuptools/_vendor/packaging/__about__.py5
-rw-r--r--setuptools/_vendor/packaging/__init__.py1
-rw-r--r--setuptools/_vendor/packaging/_compat.py38
-rw-r--r--setuptools/_vendor/packaging/_manylinux.py301
-rw-r--r--setuptools/_vendor/packaging/_musllinux.py136
-rw-r--r--setuptools/_vendor/packaging/_structures.py59
-rw-r--r--setuptools/_vendor/packaging/_typing.py48
-rw-r--r--setuptools/_vendor/packaging/markers.py136
-rw-r--r--setuptools/_vendor/packaging/requirements.py71
-rw-r--r--setuptools/_vendor/packaging/specifiers.py313
-rw-r--r--setuptools/_vendor/packaging/tags.py559
-rw-r--r--setuptools/_vendor/packaging/utils.py125
-rw-r--r--setuptools/_vendor/packaging/version.py275
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/DESCRIPTION.rst3
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/INSTALLER1
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/LICENSE.txt18
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/METADATA30
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/RECORD11
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/REQUESTED0
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/WHEEL6
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/metadata.json1
-rw-r--r--setuptools/_vendor/pyparsing-2.2.1.dist-info/top_level.txt1
-rw-r--r--setuptools/_vendor/pyparsing.py2
-rw-r--r--setuptools/_vendor/vendored.txt2
-rw-r--r--setuptools/build_meta.py13
-rw-r--r--setuptools/command/build_py.py10
-rw-r--r--setuptools/command/easy_install.py59
-rw-r--r--setuptools/command/egg_info.py21
-rw-r--r--setuptools/command/install.py7
-rw-r--r--setuptools/command/sdist.py13
-rw-r--r--setuptools/config.py6
-rw-r--r--setuptools/depends.py9
-rw-r--r--setuptools/dist.py22
-rw-r--r--setuptools/errors.py24
-rw-r--r--setuptools/extension.py2
-rw-r--r--setuptools/installer.py7
-rw-r--r--setuptools/logging.py36
-rw-r--r--setuptools/package_index.py14
-rw-r--r--setuptools/tests/contexts.py13
-rw-r--r--setuptools/tests/environment.py18
-rw-r--r--setuptools/tests/fixtures.py81
-rw-r--r--setuptools/tests/integration/__init__.py0
-rw-r--r--setuptools/tests/integration/helpers.py61
-rw-r--r--setuptools/tests/integration/test_pip_install_sdist.py218
-rw-r--r--setuptools/tests/requirements.txt14
-rw-r--r--setuptools/tests/test_bdist_deprecations.py2
-rw-r--r--setuptools/tests/test_develop.py25
-rw-r--r--setuptools/tests/test_distutils_adoption.py68
-rw-r--r--setuptools/tests/test_easy_install.py51
-rw-r--r--setuptools/tests/test_logging.py36
-rw-r--r--setuptools/tests/test_sdist.py71
-rw-r--r--setuptools/tests/test_setuptools.py26
-rw-r--r--setuptools/tests/test_test.py109
-rw-r--r--setuptools/tests/test_virtualenv.py118
-rw-r--r--setuptools/tests/test_wheel.py33
-rw-r--r--setuptools/wheel.py6
140 files changed, 4051 insertions, 1924 deletions
diff --git a/setuptools/__init__.py b/setuptools/__init__.py
index 9d6f0bc0..06991b65 100644
--- a/setuptools/__init__.py
+++ b/setuptools/__init__.py
@@ -18,6 +18,7 @@ from setuptools.extension import Extension
from setuptools.dist import Distribution
from setuptools.depends import Require
from . import monkey
+from . import logging
__all__ = [
@@ -131,7 +132,7 @@ def _install_setup_requires(attrs):
def __init__(self, attrs):
_incl = 'dependency_links', 'setup_requires'
filtered = {k: attrs[k] for k in set(_incl) & set(attrs)}
- distutils.core.Distribution.__init__(self, filtered)
+ super().__init__(filtered)
def finalize_options(self):
"""
@@ -149,6 +150,7 @@ def _install_setup_requires(attrs):
def setup(**attrs):
# Make sure we have any requirements needed to interpret 'attrs'.
+ logging.configure()
_install_setup_requires(attrs)
return distutils.core.setup(**attrs)
@@ -169,7 +171,7 @@ class Command(_Command):
Construct the command for dist, updating
vars(self) with any keyword parameters.
"""
- _Command.__init__(self, dist)
+ super().__init__(dist)
vars(self).update(kw)
def _ensure_stringlike(self, option, what, default=None):
diff --git a/setuptools/_distutils/__init__.py b/setuptools/_distutils/__init__.py
index 7dac55b6..8fd493b4 100644
--- a/setuptools/_distutils/__init__.py
+++ b/setuptools/_distutils/__init__.py
@@ -9,7 +9,16 @@ used from a setup script as
"""
import sys
+import importlib
__version__ = sys.version[:sys.version.index(' ')]
-local = True
+
+try:
+ # Allow Debian and pkgsrc (only) to customize system
+ # behavior. Ref pypa/distutils#2 and pypa/distutils#16.
+ # This hook is deprecated and no other environments
+ # should use it.
+ importlib.import_module('_distutils_system_mod')
+except ImportError:
+ pass
diff --git a/setuptools/_distutils/_collections.py b/setuptools/_distutils/_collections.py
new file mode 100644
index 00000000..98fce800
--- /dev/null
+++ b/setuptools/_distutils/_collections.py
@@ -0,0 +1,56 @@
+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/setuptools/_distutils/_msvccompiler.py b/setuptools/_distutils/_msvccompiler.py
index b7a06082..c41ea9ae 100644
--- a/setuptools/_distutils/_msvccompiler.py
+++ b/setuptools/_distutils/_msvccompiler.py
@@ -527,7 +527,7 @@ class MSVCCompiler(CCompiler) :
return
warnings.warn(
"Fallback spawn triggered. Please update distutils monkeypatch.")
- with unittest.mock.patch('os.environ', env):
+ with unittest.mock.patch.dict('os.environ', env):
bag.value = super().spawn(cmd)
# -- Miscellaneous methods -----------------------------------------
diff --git a/setuptools/_distutils/ccompiler.py b/setuptools/_distutils/ccompiler.py
index 48d160d2..777fc661 100644
--- a/setuptools/_distutils/ccompiler.py
+++ b/setuptools/_distutils/ccompiler.py
@@ -802,7 +802,7 @@ int main (int argc, char **argv) {
except (LinkError, TypeError):
return False
else:
- os.remove("a.out")
+ os.remove(os.path.join(self.output_dir or '', "a.out"))
finally:
for fn in objects:
os.remove(fn)
diff --git a/setuptools/_distutils/command/build_ext.py b/setuptools/_distutils/command/build_ext.py
index 22628baf..181671bf 100644
--- a/setuptools/_distutils/command/build_ext.py
+++ b/setuptools/_distutils/command/build_ext.py
@@ -202,9 +202,7 @@ class build_ext(Command):
# Append the source distribution include and library directories,
# this allows distutils on windows to work in the source tree
self.include_dirs.append(os.path.dirname(get_config_h_filename()))
- _sys_home = getattr(sys, '_home', None)
- if _sys_home:
- self.library_dirs.append(_sys_home)
+ self.library_dirs.append(sys.base_exec_prefix)
# Use the .lib files for the correct architecture
if self.plat_name == 'win32':
diff --git a/setuptools/_distutils/command/install.py b/setuptools/_distutils/command/install.py
index 866e2d59..0587ccd0 100644
--- a/setuptools/_distutils/command/install.py
+++ b/setuptools/_distutils/command/install.py
@@ -4,6 +4,9 @@ Implements the Distutils 'install' command."""
import sys
import os
+import contextlib
+import sysconfig
+import itertools
from distutils import log
from distutils.core import Command
@@ -14,68 +17,69 @@ from distutils.file_util import write_file
from distutils.util import convert_path, subst_vars, change_root
from distutils.util import get_platform
from distutils.errors import DistutilsOptionError
+from .. import _collections
from site import USER_BASE
from site import USER_SITE
HAS_USER_SITE = True
WINDOWS_SCHEME = {
- 'purelib': '$base/Lib/site-packages',
- 'platlib': '$base/Lib/site-packages',
- 'headers': '$base/Include/$dist_name',
- 'scripts': '$base/Scripts',
- 'data' : '$base',
+ 'purelib': '{base}/Lib/site-packages',
+ 'platlib': '{base}/Lib/site-packages',
+ 'headers': '{base}/Include/{dist_name}',
+ 'scripts': '{base}/Scripts',
+ 'data' : '{base}',
}
INSTALL_SCHEMES = {
- 'unix_prefix': {
- 'purelib': '$base/lib/python$py_version_short/site-packages',
- 'platlib': '$platbase/$platlibdir/python$py_version_short/site-packages',
- 'headers': '$base/include/python$py_version_short$abiflags/$dist_name',
- 'scripts': '$base/bin',
- 'data' : '$base',
+ 'posix_prefix': {
+ 'purelib': '{base}/lib/{implementation_lower}{py_version_short}/site-packages',
+ 'platlib': '{platbase}/{platlibdir}/{implementation_lower}{py_version_short}/site-packages',
+ 'headers': '{base}/include/{implementation_lower}{py_version_short}{abiflags}/{dist_name}',
+ 'scripts': '{base}/bin',
+ 'data' : '{base}',
},
- 'unix_home': {
- 'purelib': '$base/lib/python',
- 'platlib': '$base/$platlibdir/python',
- 'headers': '$base/include/python/$dist_name',
- 'scripts': '$base/bin',
- 'data' : '$base',
+ 'posix_home': {
+ 'purelib': '{base}/lib/{implementation_lower}',
+ 'platlib': '{base}/{platlibdir}/{implementation_lower}',
+ 'headers': '{base}/include/{implementation_lower}/{dist_name}',
+ 'scripts': '{base}/bin',
+ 'data' : '{base}',
},
'nt': WINDOWS_SCHEME,
'pypy': {
- 'purelib': '$base/site-packages',
- 'platlib': '$base/site-packages',
- 'headers': '$base/include/$dist_name',
- 'scripts': '$base/bin',
- 'data' : '$base',
+ 'purelib': '{base}/site-packages',
+ 'platlib': '{base}/site-packages',
+ 'headers': '{base}/include/{dist_name}',
+ 'scripts': '{base}/bin',
+ 'data' : '{base}',
},
'pypy_nt': {
- 'purelib': '$base/site-packages',
- 'platlib': '$base/site-packages',
- 'headers': '$base/include/$dist_name',
- 'scripts': '$base/Scripts',
- 'data' : '$base',
+ 'purelib': '{base}/site-packages',
+ 'platlib': '{base}/site-packages',
+ 'headers': '{base}/include/{dist_name}',
+ 'scripts': '{base}/Scripts',
+ 'data' : '{base}',
},
}
# user site schemes
if HAS_USER_SITE:
INSTALL_SCHEMES['nt_user'] = {
- 'purelib': '$usersite',
- 'platlib': '$usersite',
- 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name',
- 'scripts': '$userbase/Python$py_version_nodot/Scripts',
- 'data' : '$userbase',
+ 'purelib': '{usersite}',
+ 'platlib': '{usersite}',
+ 'headers': '{userbase}/{implementation}{py_version_nodot}/Include/{dist_name}',
+ 'scripts': '{userbase}/{implementation}{py_version_nodot}/Scripts',
+ 'data' : '{userbase}',
}
- INSTALL_SCHEMES['unix_user'] = {
- 'purelib': '$usersite',
- 'platlib': '$usersite',
+ INSTALL_SCHEMES['posix_user'] = {
+ 'purelib': '{usersite}',
+ 'platlib': '{usersite}',
'headers':
- '$userbase/include/python$py_version_short$abiflags/$dist_name',
- 'scripts': '$userbase/bin',
- 'data' : '$userbase',
+ '{userbase}/include/{implementation_lower}{py_version_short}{abiflags}/{dist_name}',
+ 'scripts': '{userbase}/bin',
+ 'data' : '{userbase}',
}
# The keys to an installation scheme; if any new types of files are to be
@@ -84,6 +88,96 @@ if HAS_USER_SITE:
SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data')
+def _load_sysconfig_schemes():
+ with contextlib.suppress(AttributeError):
+ return {
+ scheme: sysconfig.get_paths(scheme, expand=False)
+ for scheme in sysconfig.get_scheme_names()
+ }
+
+
+def _load_schemes():
+ """
+ Extend default schemes with schemes from sysconfig.
+ """
+
+ sysconfig_schemes = _load_sysconfig_schemes() or {}
+
+ return {
+ scheme: {
+ **INSTALL_SCHEMES.get(scheme, {}),
+ **sysconfig_schemes.get(scheme, {}),
+ }
+ for scheme in set(itertools.chain(INSTALL_SCHEMES, sysconfig_schemes))
+ }
+
+
+def _get_implementation():
+ if hasattr(sys, 'pypy_version_info'):
+ return 'PyPy'
+ else:
+ return 'Python'
+
+
+def _select_scheme(ob, name):
+ scheme = _inject_headers(name, _load_scheme(_resolve_scheme(name)))
+ vars(ob).update(_remove_set(ob, _scheme_attrs(scheme)))
+
+
+def _remove_set(ob, attrs):
+ """
+ Include only attrs that are None in ob.
+ """
+ return {
+ key: value
+ for key, value in attrs.items()
+ if getattr(ob, key) is None
+ }
+
+
+def _resolve_scheme(name):
+ os_name, sep, key = name.partition('_')
+ try:
+ resolved = sysconfig.get_preferred_scheme(key)
+ except Exception:
+ resolved = _pypy_hack(name)
+ return resolved
+
+
+def _load_scheme(name):
+ return _load_schemes()[name]
+
+
+def _inject_headers(name, scheme):
+ """
+ Given a scheme name and the resolved scheme,
+ if the scheme does not include headers, resolve
+ the fallback scheme for the name and use headers
+ from it. pypa/distutils#88
+ """
+ # Bypass the preferred scheme, which may not
+ # have defined headers.
+ fallback = _load_scheme(_pypy_hack(name))
+ scheme.setdefault('headers', fallback['headers'])
+ return scheme
+
+
+def _scheme_attrs(scheme):
+ """Resolve install directories by applying the install schemes."""
+ return {
+ f'install_{key}': scheme[key]
+ for key in SCHEME_KEYS
+ }
+
+
+def _pypy_hack(name):
+ PY37 = sys.version_info < (3, 8)
+ old_pypy = hasattr(sys, 'pypy_version_info') and PY37
+ prefix = not name.endswith(('_user', '_home'))
+ pypy_name = 'pypy' + '_nt' * (os.name == 'nt')
+ return pypy_name if old_pypy and prefix else name
+
+
class install(Command):
description = "install everything from build directory"
@@ -278,7 +372,7 @@ class install(Command):
# input a heady brew of prefix, exec_prefix, home, install_base,
# install_platbase, user-supplied versions of
# install_{purelib,platlib,lib,scripts,data,...}, and the
- # INSTALL_SCHEME dictionary above. Phew!
+ # install schemes. Phew!
self.dump_dirs("pre-finalize_{unix,other}")
@@ -301,23 +395,29 @@ class install(Command):
except AttributeError:
# sys.abiflags may not be defined on all platforms.
abiflags = ''
- self.config_vars = {'dist_name': self.distribution.get_name(),
- 'dist_version': self.distribution.get_version(),
- 'dist_fullname': self.distribution.get_fullname(),
- 'py_version': py_version,
- 'py_version_short': '%d.%d' % sys.version_info[:2],
- 'py_version_nodot': '%d%d' % sys.version_info[:2],
- 'sys_prefix': prefix,
- 'prefix': prefix,
- 'sys_exec_prefix': exec_prefix,
- 'exec_prefix': exec_prefix,
- 'abiflags': abiflags,
- 'platlibdir': getattr(sys, 'platlibdir', 'lib'),
- }
+ local_vars = {
+ 'dist_name': self.distribution.get_name(),
+ 'dist_version': self.distribution.get_version(),
+ 'dist_fullname': self.distribution.get_fullname(),
+ 'py_version': py_version,
+ 'py_version_short': '%d.%d' % sys.version_info[:2],
+ 'py_version_nodot': '%d%d' % sys.version_info[:2],
+ 'sys_prefix': prefix,
+ 'prefix': prefix,
+ 'sys_exec_prefix': exec_prefix,
+ 'exec_prefix': exec_prefix,
+ 'abiflags': abiflags,
+ 'platlibdir': getattr(sys, 'platlibdir', 'lib'),
+ 'implementation_lower': _get_implementation().lower(),
+ 'implementation': _get_implementation(),
+ }
if HAS_USER_SITE:
- self.config_vars['userbase'] = self.install_userbase
- self.config_vars['usersite'] = self.install_usersite
+ local_vars['userbase'] = self.install_userbase
+ local_vars['usersite'] = self.install_usersite
+
+ self.config_vars = _collections.DictStack(
+ [sysconfig.get_config_vars(), local_vars])
self.expand_basedirs()
@@ -325,13 +425,13 @@ class install(Command):
# Now define config vars for the base directories so we can expand
# everything else.
- self.config_vars['base'] = self.install_base
- self.config_vars['platbase'] = self.install_platbase
+ local_vars['base'] = self.install_base
+ local_vars['platbase'] = self.install_platbase
if DEBUG:
from pprint import pprint
print("config vars:")
- pprint(self.config_vars)
+ pprint(dict(self.config_vars))
# Expand "~" and configuration variables in the installation
# directories.
@@ -407,12 +507,17 @@ class install(Command):
def finalize_unix(self):
"""Finalizes options for posix platforms."""
if self.install_base is not None or self.install_platbase is not None:
- if ((self.install_lib is None and
- self.install_purelib is None and
- self.install_platlib is None) or
+ incomplete_scheme = (
+ (
+ self.install_lib is None and
+ self.install_purelib is None and
+ self.install_platlib is None
+ ) or
self.install_headers is None or
self.install_scripts is None or
- self.install_data is None):
+ self.install_data is None
+ )
+ if incomplete_scheme:
raise DistutilsOptionError(
"install-base or install-platbase supplied, but "
"installation scheme is incomplete")
@@ -423,18 +528,23 @@ class install(Command):
raise DistutilsPlatformError(
"User base directory is not specified")
self.install_base = self.install_platbase = self.install_userbase
- self.select_scheme("unix_user")
+ self.select_scheme("posix_user")
elif self.home is not None:
self.install_base = self.install_platbase = self.home
- self.select_scheme("unix_home")
+ self.select_scheme("posix_home")
else:
if self.prefix is None:
if self.exec_prefix is not None:
raise DistutilsOptionError(
"must not supply exec-prefix without prefix")
- self.prefix = os.path.normpath(sys.prefix)
- self.exec_prefix = os.path.normpath(sys.exec_prefix)
+ # Allow Fedora to add components to the prefix
+ _prefix_addition = getattr(sysconfig, '_prefix_addition', "")
+
+ self.prefix = (
+ os.path.normpath(sys.prefix) + _prefix_addition)
+ self.exec_prefix = (
+ os.path.normpath(sys.exec_prefix) + _prefix_addition)
else:
if self.exec_prefix is None:
@@ -442,7 +552,7 @@ class install(Command):
self.install_base = self.prefix
self.install_platbase = self.exec_prefix
- self.select_scheme("unix_prefix")
+ self.select_scheme("posix_prefix")
def finalize_other(self):
"""Finalizes options for non-posix platforms"""
@@ -454,7 +564,7 @@ class install(Command):
self.select_scheme(os.name + "_user")
elif self.home is not None:
self.install_base = self.install_platbase = self.home
- self.select_scheme("unix_home")
+ self.select_scheme("posix_home")
else:
if self.prefix is None:
self.prefix = os.path.normpath(sys.prefix)
@@ -467,20 +577,7 @@ class install(Command):
"I don't know how to install stuff on '%s'" % os.name)
def select_scheme(self, name):
- """Sets the install directories by applying the install schemes."""
- # it's the caller's problem if they supply a bad name!
- if (hasattr(sys, 'pypy_version_info') and
- sys.version_info < (3, 8) and
- not name.endswith(('_user', '_home'))):
- if os.name == 'nt':
- name = 'pypy_nt'
- else:
- name = 'pypy'
- scheme = INSTALL_SCHEMES[name]
- for key in SCHEME_KEYS:
- attrname = 'install_' + key
- if getattr(self, attrname) is None:
- setattr(self, attrname, scheme[key])
+ _select_scheme(self, name)
def _expand_attrs(self, attrs):
for attr in attrs:
@@ -554,7 +651,7 @@ class install(Command):
return
home = convert_path(os.path.expanduser("~"))
for name, path in self.config_vars.items():
- if path.startswith(home) and not os.path.isdir(path):
+ if str(path).startswith(home) and not os.path.isdir(path):
self.debug_print("os.makedirs('%s', 0o700)" % path)
os.makedirs(path, 0o700)
diff --git a/setuptools/_distutils/command/install_egg_info.py b/setuptools/_distutils/command/install_egg_info.py
index 0ddc7367..adc0323f 100644
--- a/setuptools/_distutils/command/install_egg_info.py
+++ b/setuptools/_distutils/command/install_egg_info.py
@@ -19,14 +19,21 @@ class install_egg_info(Command):
def initialize_options(self):
self.install_dir = None
- def finalize_options(self):
- self.set_undefined_options('install_lib',('install_dir','install_dir'))
- basename = "%s-%s-py%d.%d.egg-info" % (
+ @property
+ def basename(self):
+ """
+ Allow basename to be overridden by child class.
+ Ref pypa/distutils#2.
+ """
+ return "%s-%s-py%d.%d.egg-info" % (
to_filename(safe_name(self.distribution.get_name())),
to_filename(safe_version(self.distribution.get_version())),
*sys.version_info[:2]
)
- self.target = os.path.join(self.install_dir, basename)
+
+ def finalize_options(self):
+ self.set_undefined_options('install_lib',('install_dir','install_dir'))
+ self.target = os.path.join(self.install_dir, self.basename)
self.outputs = [self.target]
def run(self):
diff --git a/setuptools/_distutils/core.py b/setuptools/_distutils/core.py
index d603d4a4..f43888ea 100644
--- a/setuptools/_distutils/core.py
+++ b/setuptools/_distutils/core.py
@@ -8,6 +8,7 @@ really defined in distutils.dist and distutils.cmd.
import os
import sys
+import tokenize
from distutils.debug import DEBUG
from distutils.errors import *
@@ -144,29 +145,41 @@ def setup (**attrs):
# And finally, run all the commands found on the command line.
if ok:
- try:
- dist.run_commands()
- except KeyboardInterrupt:
- raise SystemExit("interrupted")
- except OSError as exc:
- if DEBUG:
- sys.stderr.write("error: %s\n" % (exc,))
- raise
- else:
- raise SystemExit("error: %s" % (exc,))
-
- except (DistutilsError,
- CCompilerError) as msg:
- if DEBUG:
- raise
- else:
- raise SystemExit("error: " + str(msg))
+ return run_commands(dist)
return dist
# setup ()
+def run_commands (dist):
+ """Given a Distribution object run all the commands,
+ raising ``SystemExit`` errors in the case of failure.
+
+ This function assumes that either ``sys.argv`` or ``dist.script_args``
+ is already set accordingly.
+ """
+ try:
+ dist.run_commands()
+ except KeyboardInterrupt:
+ raise SystemExit("interrupted")
+ except OSError as exc:
+ if DEBUG:
+ sys.stderr.write("error: %s\n" % (exc,))
+ raise
+ else:
+ raise SystemExit("error: %s" % (exc,))
+
+ except (DistutilsError,
+ CCompilerError) as msg:
+ if DEBUG:
+ raise
+ else:
+ raise SystemExit("error: " + str(msg))
+
+ return dist
+
+
def run_setup (script_name, script_args=None, stop_after="run"):
"""Run a setup script in a somewhat controlled environment, and
return the Distribution instance that drives things. This is useful
@@ -205,14 +218,16 @@ def run_setup (script_name, script_args=None, stop_after="run"):
_setup_stop_after = stop_after
save_argv = sys.argv.copy()
- g = {'__file__': script_name}
+ g = {'__file__': script_name, '__name__': '__main__'}
try:
try:
sys.argv[0] = script_name
if script_args is not None:
sys.argv[1:] = script_args
- with open(script_name, 'rb') as f:
- exec(f.read(), g)
+ # tokenize.open supports automatic encoding detection
+ with tokenize.open(script_name) as f:
+ code = f.read().replace(r'\r\n', r'\n')
+ exec(code, g)
finally:
sys.argv = save_argv
_setup_stop_after = None
diff --git a/setuptools/_distutils/cygwinccompiler.py b/setuptools/_distutils/cygwinccompiler.py
index f1c38e39..fd082f6d 100644
--- a/setuptools/_distutils/cygwinccompiler.py
+++ b/setuptools/_distutils/cygwinccompiler.py
@@ -50,15 +50,15 @@ cygwin in no-cygwin mode).
import os
import sys
import copy
-from subprocess import Popen, PIPE, check_output
-import re
+import shlex
+import warnings
+from subprocess import check_output
from distutils.unixccompiler import UnixCCompiler
from distutils.file_util import write_file
from distutils.errors import (DistutilsExecError, CCompilerError,
CompileError, UnknownFileError)
-from distutils.version import LooseVersion
-from distutils.spawn import find_executable
+from distutils.version import LooseVersion, suppress_known_deprecation
def get_msvcr():
"""Include the appropriate MSVC runtime library if Python was built
@@ -82,6 +82,15 @@ def get_msvcr():
elif msc_ver == '1600':
# VS2010 / MSVC 10.0
return ['msvcr100']
+ elif msc_ver == '1700':
+ # VS2012 / MSVC 11.0
+ return ['msvcr110']
+ elif msc_ver == '1800':
+ # VS2013 / MSVC 12.0
+ return ['msvcr120']
+ elif 1900 <= int(msc_ver) < 2000:
+ # VS2015 / MSVC 14.0
+ return ['ucrt', 'vcruntime140']
else:
raise ValueError("Unknown MS Compiler version %s " % msc_ver)
@@ -114,33 +123,8 @@ class CygwinCCompiler(UnixCCompiler):
self.cc = os.environ.get('CC', 'gcc')
self.cxx = os.environ.get('CXX', 'g++')
- if ('gcc' in self.cc): # Start gcc workaround
- self.gcc_version, self.ld_version, self.dllwrap_version = \
- get_versions()
- self.debug_print(self.compiler_type + ": gcc %s, ld %s, dllwrap %s\n" %
- (self.gcc_version,
- self.ld_version,
- self.dllwrap_version) )
-
- # ld_version >= "2.10.90" and < "2.13" should also be able to use
- # gcc -mdll instead of dllwrap
- # Older dllwraps had own version numbers, newer ones use the
- # same as the rest of binutils ( also ld )
- # dllwrap 2.10.90 is buggy
- if self.ld_version >= "2.10.90":
- self.linker_dll = self.cc
- else:
- self.linker_dll = "dllwrap"
-
- # ld_version >= "2.13" support -shared so use it instead of
- # -mdll -static
- if self.ld_version >= "2.13":
- shared_option = "-shared"
- else:
- shared_option = "-mdll -static"
- else: # Assume linker is up to date
- self.linker_dll = self.cc
- shared_option = "-shared"
+ self.linker_dll = self.cc
+ shared_option = "-shared"
self.set_executables(compiler='%s -mcygwin -O -Wall' % self.cc,
compiler_so='%s -mcygwin -mdll -O -Wall' % self.cc,
@@ -149,17 +133,24 @@ class CygwinCCompiler(UnixCCompiler):
linker_so=('%s -mcygwin %s' %
(self.linker_dll, shared_option)))
- # cygwin and mingw32 need different sets of libraries
- if ('gcc' in self.cc and self.gcc_version == "2.91.57"):
- # cygwin shouldn't need msvcrt, but without the dlls will crash
- # (gcc version 2.91.57) -- perhaps something about initialization
- self.dll_libraries=["msvcrt"]
- self.warn(
- "Consider upgrading to a newer version of gcc")
- else:
- # Include the appropriate MSVC runtime library if Python was built
- # with MSVC 7.0 or later.
- self.dll_libraries = get_msvcr()
+ # Include the appropriate MSVC runtime library if Python was built
+ # with MSVC 7.0 or later.
+ self.dll_libraries = get_msvcr()
+
+ @property
+ def gcc_version(self):
+ # Older numpy dependend on this existing to check for ancient
+ # gcc versions. This doesn't make much sense with clang etc so
+ # just hardcode to something recent.
+ # https://github.com/numpy/numpy/pull/20333
+ warnings.warn(
+ "gcc_version attribute of CygwinCCompiler is deprecated. "
+ "Instead of returning actual gcc version a fixed value 11.2.0 is returned.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ with suppress_known_deprecation():
+ return LooseVersion("11.2.0")
def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
"""Compiles the source by spawning GCC and windres if needed."""
@@ -221,24 +212,17 @@ class CygwinCCompiler(UnixCCompiler):
# next add options for def-file and to creating import libraries
- # dllwrap uses different options than gcc/ld
- if self.linker_dll == "dllwrap":
- extra_preargs.extend(["--output-lib", lib_file])
- # for dllwrap we have to use a special option
- extra_preargs.extend(["--def", def_file])
- # we use gcc/ld here and can be sure ld is >= 2.9.10
- else:
- # doesn't work: bfd_close build\...\libfoo.a: Invalid operation
- #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file])
- # for gcc/ld the def-file is specified as any object files
- objects.append(def_file)
+ # doesn't work: bfd_close build\...\libfoo.a: Invalid operation
+ #extra_preargs.extend(["-Wl,--out-implib,%s" % lib_file])
+ # for gcc/ld the def-file is specified as any object files
+ objects.append(def_file)
#end: if ((export_symbols is not None) and
# (target_desc != self.EXECUTABLE or self.linker_dll == "gcc")):
# who wants symbols and a many times larger output file
# should explicitly switch the debug mode on
- # otherwise we let dllwrap/ld strip the output file
+ # otherwise we let ld strip the output file
# (On my machine: 10KiB < stripped_file < ??100KiB
# unstripped_file = stripped_file + XXX KiB
# ( XXX=254 for a typical python extension))
@@ -286,19 +270,7 @@ class Mingw32CCompiler(CygwinCCompiler):
CygwinCCompiler.__init__ (self, verbose, dry_run, force)
- # ld_version >= "2.13" support -shared so use it instead of
- # -mdll -static
- if ('gcc' in self.cc and self.ld_version < "2.13"):
- shared_option = "-mdll -static"
- else:
- shared_option = "-shared"
-
- # A real mingw32 doesn't need to specify a different entry point,
- # but cygwin 2.91.57 in no-cygwin-mode needs it.
- if ('gcc' in self.cc and self.gcc_version <= "2.91.57"):
- entry_point = '--entry _DllMain@12'
- else:
- entry_point = ''
+ shared_option = "-shared"
if is_cygwincc(self.cc):
raise CCompilerError(
@@ -308,9 +280,9 @@ class Mingw32CCompiler(CygwinCCompiler):
compiler_so='%s -mdll -O -Wall' % self.cc,
compiler_cxx='%s -O -Wall' % self.cxx,
linker_exe='%s' % self.cc,
- linker_so='%s %s %s'
- % (self.linker_dll, shared_option,
- entry_point))
+ linker_so='%s %s'
+ % (self.linker_dll, shared_option))
+
# Maybe we should also append -mthreads, but then the finished
# dlls need another dll (mingwm10.dll see Mingw32 docs)
# (-mthreads: Support thread-safe exception handling on `Mingw32')
@@ -377,38 +349,14 @@ def check_config_h():
return (CONFIG_H_UNCERTAIN,
"couldn't read '%s': %s" % (fn, exc.strerror))
-RE_VERSION = re.compile(br'(\d+\.\d+(\.\d+)*)')
-
-def _find_exe_version(cmd):
- """Find the version of an executable by running `cmd` in the shell.
-
- If the command is not found, or the output does not match
- `RE_VERSION`, returns None.
- """
- executable = cmd.split()[0]
- if find_executable(executable) is None:
- return None
- out = Popen(cmd, shell=True, stdout=PIPE).stdout
- try:
- out_string = out.read()
- finally:
- out.close()
- result = RE_VERSION.search(out_string)
- if result is None:
- return None
- # LooseVersion works with strings
- # so we need to decode our bytes
- return LooseVersion(result.group(1).decode())
-
-def get_versions():
- """ Try to find out the versions of gcc, ld and dllwrap.
-
- If not possible it returns None for it.
- """
- commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version']
- return tuple([_find_exe_version(cmd) for cmd in commands])
-
def is_cygwincc(cc):
'''Try to determine if the compiler that would be used is from cygwin.'''
- out_string = check_output([cc, '-dumpmachine'])
+ out_string = check_output(shlex.split(cc) + ['-dumpmachine'])
return out_string.strip().endswith(b'cygwin')
+
+
+get_versions = None
+"""
+A stand-in for the previous get_versions() function to prevent failures
+when monkeypatched. See pypa/setuptools#2969.
+"""
diff --git a/setuptools/_distutils/log.py b/setuptools/_distutils/log.py
index 8ef6b28e..a68b156b 100644
--- a/setuptools/_distutils/log.py
+++ b/setuptools/_distutils/log.py
@@ -3,13 +3,14 @@
# The class here is styled after PEP 282 so that it could later be
# replaced with a standard Python logging implementation.
+import sys
+
DEBUG = 1
INFO = 2
WARN = 3
ERROR = 4
FATAL = 5
-import sys
class Log:
@@ -54,6 +55,7 @@ class Log:
def fatal(self, msg, *args):
self._log(FATAL, msg, args)
+
_global_log = Log()
log = _global_log.log
debug = _global_log.debug
@@ -62,12 +64,14 @@ warn = _global_log.warn
error = _global_log.error
fatal = _global_log.fatal
+
def set_threshold(level):
# return the old threshold for use from tests
old = _global_log.threshold
_global_log.threshold = level
return old
+
def set_verbosity(v):
if v <= 0:
set_threshold(WARN)
diff --git a/setuptools/_distutils/msvc9compiler.py b/setuptools/_distutils/msvc9compiler.py
index a1b3b02f..14d13775 100644
--- a/setuptools/_distutils/msvc9compiler.py
+++ b/setuptools/_distutils/msvc9compiler.py
@@ -291,8 +291,6 @@ def query_vcvarsall(version, arch="x86"):
# More globals
VERSION = get_build_version()
-if VERSION < 8.0:
- raise DistutilsPlatformError("VC %0.1f is not supported by this module" % VERSION)
# MACROS = MacroExpander(VERSION)
class MSVCCompiler(CCompiler) :
@@ -339,6 +337,8 @@ class MSVCCompiler(CCompiler) :
def initialize(self, plat_name=None):
# multi-init means we would need to check platform same each time...
assert not self.initialized, "don't init multiple times"
+ if self.__version < 8.0:
+ raise DistutilsPlatformError("VC %0.1f is not supported by this module" % self.__version)
if plat_name is None:
plat_name = get_platform()
# sanity check for platforms to prevent obscure errors later.
diff --git a/setuptools/_distutils/spawn.py b/setuptools/_distutils/spawn.py
index 6e1c89f1..b2d10e39 100644
--- a/setuptools/_distutils/spawn.py
+++ b/setuptools/_distutils/spawn.py
@@ -10,7 +10,7 @@ import sys
import os
import subprocess
-from distutils.errors import DistutilsPlatformError, DistutilsExecError
+from distutils.errors import DistutilsExecError
from distutils.debug import DEBUG
from distutils import log
diff --git a/setuptools/_distutils/sysconfig.py b/setuptools/_distutils/sysconfig.py
index 8832b3ec..4a77a431 100644
--- a/setuptools/_distutils/sysconfig.py
+++ b/setuptools/_distutils/sysconfig.py
@@ -13,6 +13,7 @@ import _imp
import os
import re
import sys
+import sysconfig
from .errors import DistutilsPlatformError
@@ -129,6 +130,14 @@ def get_python_inc(plat_specific=0, prefix=None):
"on platform '%s'" % os.name)
+# allow this behavior to be monkey-patched. Ref pypa/distutils#2.
+def _posix_lib(standard_lib, libpython, early_prefix, prefix):
+ if standard_lib:
+ return libpython
+ else:
+ return os.path.join(libpython, "site-packages")
+
+
def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
"""Return the directory containing the Python library (standard or
site additions).
@@ -152,6 +161,8 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
return os.path.join(prefix, "lib-python", sys.version[0])
return os.path.join(prefix, 'site-packages')
+ early_prefix = prefix
+
if prefix is None:
if standard_lib:
prefix = plat_specific and BASE_EXEC_PREFIX or BASE_PREFIX
@@ -169,10 +180,7 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
implementation = 'pypy' if IS_PYPY else 'python'
libpython = os.path.join(prefix, libdir,
implementation + get_python_version())
- if standard_lib:
- return libpython
- else:
- return os.path.join(libpython, "site-packages")
+ return _posix_lib(standard_lib, libpython, early_prefix, prefix)
elif os.name == "nt":
if standard_lib:
return os.path.join(prefix, "Lib")
@@ -267,21 +275,15 @@ def get_config_h_filename():
inc_dir = os.path.join(_sys_home or project_base, "PC")
else:
inc_dir = _sys_home or project_base
+ return os.path.join(inc_dir, 'pyconfig.h')
else:
- inc_dir = get_python_inc(plat_specific=1)
+ return sysconfig.get_config_h_filename()
- return os.path.join(inc_dir, 'pyconfig.h')
def get_makefile_filename():
"""Return full pathname of installed Makefile from the Python build."""
- if python_build:
- return os.path.join(_sys_home or project_base, "Makefile")
- lib_dir = get_python_lib(plat_specific=0, standard_lib=1)
- config_file = 'config-{}{}'.format(get_python_version(), build_flags)
- if hasattr(sys.implementation, '_multiarch'):
- config_file += '-%s' % sys.implementation._multiarch
- return os.path.join(lib_dir, config_file, 'Makefile')
+ return sysconfig.get_makefile_filename()
def parse_config_h(fp, g=None):
@@ -291,26 +293,7 @@ def parse_config_h(fp, g=None):
optional dictionary is passed in as the second argument, it is
used instead of a new dictionary.
"""
- if g is None:
- g = {}
- define_rx = re.compile("#define ([A-Z][A-Za-z0-9_]+) (.*)\n")
- undef_rx = re.compile("/[*] #undef ([A-Z][A-Za-z0-9_]+) [*]/\n")
- #
- while True:
- line = fp.readline()
- if not line:
- break
- m = define_rx.match(line)
- if m:
- n, v = m.group(1, 2)
- try: v = int(v)
- except ValueError: pass
- g[n] = v
- else:
- m = undef_rx.match(line)
- if m:
- g[m.group(1)] = 0
- return g
+ return sysconfig.parse_config_h(fp, vars=g)
# Regexes needed for parsing Makefile (and similar syntaxes,
@@ -452,15 +435,21 @@ def expand_makefile_vars(s, vars):
_config_vars = None
+
+_sysconfig_name_tmpl = '_sysconfigdata_{abi}_{platform}_{multiarch}'
+
+
def _init_posix():
"""Initialize the module as appropriate for POSIX systems."""
# _sysconfigdata is generated at build time, see the sysconfig module
- name = os.environ.get('_PYTHON_SYSCONFIGDATA_NAME',
- '_sysconfigdata_{abi}_{platform}_{multiarch}'.format(
- abi=sys.abiflags,
- platform=sys.platform,
- multiarch=getattr(sys.implementation, '_multiarch', ''),
- ))
+ name = os.environ.get(
+ '_PYTHON_SYSCONFIGDATA_NAME',
+ _sysconfig_name_tmpl.format(
+ abi=sys.abiflags,
+ platform=sys.platform,
+ multiarch=getattr(sys.implementation, '_multiarch', ''),
+ ),
+ )
try:
_temp = __import__(name, globals(), locals(), ['build_time_vars'], 0)
except ImportError:
diff --git a/setuptools/_distutils/tests/py38compat.py b/setuptools/_distutils/tests/py38compat.py
index 32269c7b..c949f58e 100644
--- a/setuptools/_distutils/tests/py38compat.py
+++ b/setuptools/_distutils/tests/py38compat.py
@@ -2,6 +2,11 @@
import contextlib
import builtins
+import sys
+
+from test.support import requires_zlib
+import test.support
+
ModuleNotFoundError = getattr(builtins, 'ModuleNotFoundError', ImportError)
@@ -51,3 +56,7 @@ try:
from test.support.warnings_helper import save_restore_warnings_filters
except (ModuleNotFoundError, ImportError):
save_restore_warnings_filters = _save_restore_warnings_filters
+
+
+if sys.version_info < (3, 9):
+ requires_zlib = lambda: test.support.requires_zlib
diff --git a/setuptools/_distutils/tests/test_archive_util.py b/setuptools/_distutils/tests/test_archive_util.py
index ce6456dc..800b9018 100644
--- a/setuptools/_distutils/tests/test_archive_util.py
+++ b/setuptools/_distutils/tests/test_archive_util.py
@@ -14,16 +14,11 @@ from distutils.archive_util import (check_archive_formats, make_tarball,
from distutils.spawn import find_executable, spawn
from distutils.tests import support
from test.support import run_unittest, patch
+from .unix_compat import require_unix_id, require_uid_0, grp, pwd, UID_0_SUPPORT
from .py38compat import change_cwd
from .py38compat import check_warnings
-try:
- import grp
- import pwd
- UID_GID_SUPPORT = True
-except ImportError:
- UID_GID_SUPPORT = False
try:
import zipfile
@@ -339,7 +334,7 @@ class ArchiveUtilTestCase(support.TempdirManager,
def test_make_archive_owner_group(self):
# testing make_archive with owner and group, with various combinations
# this works even if there's not gid/uid support
- if UID_GID_SUPPORT:
+ if UID_0_SUPPORT:
group = grp.getgrgid(0)[0]
owner = pwd.getpwuid(0)[0]
else:
@@ -364,7 +359,8 @@ class ArchiveUtilTestCase(support.TempdirManager,
self.assertTrue(os.path.exists(res))
@unittest.skipUnless(ZLIB_SUPPORT, "Requires zlib")
- @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
+ @require_unix_id
+ @require_uid_0
def test_tarfile_root_owner(self):
tmpdir = self._create_files()
base_name = os.path.join(self.mkdtemp(), 'archive')
@@ -391,7 +387,7 @@ class ArchiveUtilTestCase(support.TempdirManager,
archive.close()
def test_suite():
- return unittest.makeSuite(ArchiveUtilTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(ArchiveUtilTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_bdist.py b/setuptools/_distutils/tests/test_bdist.py
index 130d8bf1..8b7498e3 100644
--- a/setuptools/_distutils/tests/test_bdist.py
+++ b/setuptools/_distutils/tests/test_bdist.py
@@ -51,7 +51,7 @@ class BuildTestCase(support.TempdirManager,
def test_suite():
- return unittest.makeSuite(BuildTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BuildTestCase)
if __name__ == '__main__':
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_bdist_dumb.py b/setuptools/_distutils/tests/test_bdist_dumb.py
index 01a233bc..bb860c8a 100644
--- a/setuptools/_distutils/tests/test_bdist_dumb.py
+++ b/setuptools/_distutils/tests/test_bdist_dumb.py
@@ -91,7 +91,7 @@ class BuildDumbTestCase(support.TempdirManager,
self.assertEqual(contents, sorted(wanted))
def test_suite():
- return unittest.makeSuite(BuildDumbTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BuildDumbTestCase)
if __name__ == '__main__':
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_bdist_msi.py b/setuptools/_distutils/tests/test_bdist_msi.py
index 937266f8..b1831ef2 100644
--- a/setuptools/_distutils/tests/test_bdist_msi.py
+++ b/setuptools/_distutils/tests/test_bdist_msi.py
@@ -22,7 +22,7 @@ class BDistMSITestCase(support.TempdirManager,
def test_suite():
- return unittest.makeSuite(BDistMSITestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BDistMSITestCase)
if __name__ == '__main__':
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_bdist_rpm.py b/setuptools/_distutils/tests/test_bdist_rpm.py
index 6453a02b..08a7cb46 100644
--- a/setuptools/_distutils/tests/test_bdist_rpm.py
+++ b/setuptools/_distutils/tests/test_bdist_rpm.py
@@ -3,13 +3,16 @@
import unittest
import sys
import os
-from test.support import run_unittest, requires_zlib
+from test.support import run_unittest
from distutils.core import Distribution
from distutils.command.bdist_rpm import bdist_rpm
from distutils.tests import support
from distutils.spawn import find_executable
+from .py38compat import requires_zlib
+
+
SETUP_PY = """\
from distutils.core import setup
import foo
@@ -44,7 +47,7 @@ class BuildRpmTestCase(support.TempdirManager,
# spurious sdtout/stderr output under Mac OS X
@unittest.skipUnless(sys.platform.startswith('linux'),
'spurious sdtout/stderr output under Mac OS X')
- @requires_zlib
+ @requires_zlib()
@unittest.skipIf(find_executable('rpm') is None,
'the rpm command is not found')
@unittest.skipIf(find_executable('rpmbuild') is None,
@@ -87,7 +90,7 @@ class BuildRpmTestCase(support.TempdirManager,
# spurious sdtout/stderr output under Mac OS X
@unittest.skipUnless(sys.platform.startswith('linux'),
'spurious sdtout/stderr output under Mac OS X')
- @requires_zlib
+ @requires_zlib()
# http://bugs.python.org/issue1533164
@unittest.skipIf(find_executable('rpm') is None,
'the rpm command is not found')
@@ -129,7 +132,7 @@ class BuildRpmTestCase(support.TempdirManager,
os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm'))
def test_suite():
- return unittest.makeSuite(BuildRpmTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BuildRpmTestCase)
if __name__ == '__main__':
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_bdist_wininst.py b/setuptools/_distutils/tests/test_bdist_wininst.py
index 31cf2628..59f25167 100644
--- a/setuptools/_distutils/tests/test_bdist_wininst.py
+++ b/setuptools/_distutils/tests/test_bdist_wininst.py
@@ -34,7 +34,7 @@ class BuildWinInstTestCase(support.TempdirManager,
self.assertGreater(len(exe_file), 10)
def test_suite():
- return unittest.makeSuite(BuildWinInstTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BuildWinInstTestCase)
if __name__ == '__main__':
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_build.py b/setuptools/_distutils/tests/test_build.py
index b020a5ba..83a9e4f4 100644
--- a/setuptools/_distutils/tests/test_build.py
+++ b/setuptools/_distutils/tests/test_build.py
@@ -50,7 +50,7 @@ class BuildTestCase(support.TempdirManager,
self.assertEqual(cmd.executable, os.path.normpath(sys.executable))
def test_suite():
- return unittest.makeSuite(BuildTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BuildTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_build_clib.py b/setuptools/_distutils/tests/test_build_clib.py
index 259c4352..d50ead7c 100644
--- a/setuptools/_distutils/tests/test_build_clib.py
+++ b/setuptools/_distutils/tests/test_build_clib.py
@@ -130,7 +130,7 @@ class BuildCLibTestCase(support.TempdirManager,
self.assertIn('libfoo.a', os.listdir(build_temp))
def test_suite():
- return unittest.makeSuite(BuildCLibTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BuildCLibTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_build_ext.py b/setuptools/_distutils/tests/test_build_ext.py
index 85ecf4b7..920e4dc8 100644
--- a/setuptools/_distutils/tests/test_build_ext.py
+++ b/setuptools/_distutils/tests/test_build_ext.py
@@ -493,12 +493,16 @@ class BuildExtTestCase(TempdirManager,
# format the target value as defined in the Apple
# Availability Macros. We can't use the macro names since
# at least one value we test with will not exist yet.
- if target[1] < 10:
+ if target[:2] < (10, 10):
# for 10.1 through 10.9.x -> "10n0"
target = '%02d%01d0' % target
else:
# for 10.10 and beyond -> "10nn00"
- target = '%02d%02d00' % target
+ if len(target) >= 2:
+ target = '%02d%02d00' % target
+ else:
+ # 11 and later can have no minor version (11 instead of 11.0)
+ target = '%02d0000' % target
deptarget_ext = Extension(
'deptarget',
[deptarget_c],
@@ -538,8 +542,8 @@ class ParallelBuildExtTestCase(BuildExtTestCase):
def test_suite():
suite = unittest.TestSuite()
- suite.addTest(unittest.makeSuite(BuildExtTestCase))
- suite.addTest(unittest.makeSuite(ParallelBuildExtTestCase))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(BuildExtTestCase))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(ParallelBuildExtTestCase))
return suite
if __name__ == '__main__':
diff --git a/setuptools/_distutils/tests/test_build_py.py b/setuptools/_distutils/tests/test_build_py.py
index 0712e92c..a590a485 100644
--- a/setuptools/_distutils/tests/test_build_py.py
+++ b/setuptools/_distutils/tests/test_build_py.py
@@ -173,7 +173,7 @@ class BuildPyTestCase(support.TempdirManager,
def test_suite():
- return unittest.makeSuite(BuildPyTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BuildPyTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_build_scripts.py b/setuptools/_distutils/tests/test_build_scripts.py
index 954fc763..f299e51e 100644
--- a/setuptools/_distutils/tests/test_build_scripts.py
+++ b/setuptools/_distutils/tests/test_build_scripts.py
@@ -106,7 +106,7 @@ class BuildScriptsTestCase(support.TempdirManager,
self.assertIn(name, built)
def test_suite():
- return unittest.makeSuite(BuildScriptsTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(BuildScriptsTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_check.py b/setuptools/_distutils/tests/test_check.py
index e534aca1..91bcdceb 100644
--- a/setuptools/_distutils/tests/test_check.py
+++ b/setuptools/_distutils/tests/test_check.py
@@ -157,7 +157,7 @@ class CheckTestCase(support.LoggingSilencer,
'restructuredtext': 1})
def test_suite():
- return unittest.makeSuite(CheckTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(CheckTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_clean.py b/setuptools/_distutils/tests/test_clean.py
index c605afd8..92367499 100644
--- a/setuptools/_distutils/tests/test_clean.py
+++ b/setuptools/_distutils/tests/test_clean.py
@@ -43,7 +43,7 @@ class cleanTestCase(support.TempdirManager,
cmd.run()
def test_suite():
- return unittest.makeSuite(cleanTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(cleanTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_cmd.py b/setuptools/_distutils/tests/test_cmd.py
index cf5197c3..2319214a 100644
--- a/setuptools/_distutils/tests/test_cmd.py
+++ b/setuptools/_distutils/tests/test_cmd.py
@@ -120,7 +120,7 @@ class CommandTestCase(unittest.TestCase):
debug.DEBUG = False
def test_suite():
- return unittest.makeSuite(CommandTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(CommandTestCase)
if __name__ == '__main__':
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_config.py b/setuptools/_distutils/tests/test_config.py
index 344084af..8ab70efb 100644
--- a/setuptools/_distutils/tests/test_config.py
+++ b/setuptools/_distutils/tests/test_config.py
@@ -135,7 +135,7 @@ class PyPIRCCommandTestCase(BasePyPIRCCommandTestCase):
def test_suite():
- return unittest.makeSuite(PyPIRCCommandTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(PyPIRCCommandTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_config_cmd.py b/setuptools/_distutils/tests/test_config_cmd.py
index 4cd9a6b9..2c84719a 100644
--- a/setuptools/_distutils/tests/test_config_cmd.py
+++ b/setuptools/_distutils/tests/test_config_cmd.py
@@ -92,7 +92,7 @@ class ConfigTestCase(support.LoggingSilencer,
self.assertFalse(os.path.exists(f))
def test_suite():
- return unittest.makeSuite(ConfigTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(ConfigTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_core.py b/setuptools/_distutils/tests/test_core.py
index 666ff4a3..7270d699 100644
--- a/setuptools/_distutils/tests/test_core.py
+++ b/setuptools/_distutils/tests/test_core.py
@@ -10,6 +10,7 @@ from . import py38compat as os_helper
import unittest
from distutils.tests import support
from distutils import log
+from distutils.dist import Distribution
# setup script that uses __file__
setup_using___file__ = """\
@@ -45,6 +46,16 @@ class install(_install):
setup(cmdclass={'install': install})
"""
+setup_within_if_main = """\
+from distutils.core import setup
+
+def main():
+ return setup(name="setup_within_if_main")
+
+if __name__ == "__main__":
+ main()
+"""
+
class CoreTestCase(support.EnvironGuard, unittest.TestCase):
def setUp(self):
@@ -115,6 +126,20 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase):
output = output[:-1]
self.assertEqual(cwd, output)
+ def test_run_setup_within_if_main(self):
+ dist = distutils.core.run_setup(
+ self.write_setup(setup_within_if_main), stop_after="config")
+ self.assertIsInstance(dist, Distribution)
+ self.assertEqual(dist.get_name(), "setup_within_if_main")
+
+ def test_run_commands(self):
+ sys.argv = ['setup.py', 'build']
+ dist = distutils.core.run_setup(
+ self.write_setup(setup_within_if_main), stop_after="commandline")
+ self.assertNotIn('build', dist.have_run)
+ distutils.core.run_commands(dist)
+ self.assertIn('build', dist.have_run)
+
def test_debug_mode(self):
# this covers the code called when DEBUG is set
sys.argv = ['setup.py', '--name']
@@ -134,7 +159,7 @@ class CoreTestCase(support.EnvironGuard, unittest.TestCase):
self.assertEqual(stdout.readlines()[0], wanted)
def test_suite():
- return unittest.makeSuite(CoreTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(CoreTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_cygwinccompiler.py b/setuptools/_distutils/tests/test_cygwinccompiler.py
index 9dc869de..8715a535 100644
--- a/setuptools/_distutils/tests/test_cygwinccompiler.py
+++ b/setuptools/_distutils/tests/test_cygwinccompiler.py
@@ -2,28 +2,14 @@
import unittest
import sys
import os
-from io import BytesIO
from test.support import run_unittest
-from distutils import cygwinccompiler
from distutils.cygwinccompiler import (check_config_h,
CONFIG_H_OK, CONFIG_H_NOTOK,
- CONFIG_H_UNCERTAIN, get_versions,
+ CONFIG_H_UNCERTAIN,
get_msvcr)
from distutils.tests import support
-class FakePopen(object):
- test_class = None
-
- def __init__(self, cmd, shell, stdout):
- self.cmd = cmd.split()[0]
- exes = self.test_class._exes
- if self.cmd in exes:
- # issue #6438 in Python 3.x, Popen returns bytes
- self.stdout = BytesIO(exes[self.cmd])
- else:
- self.stdout = os.popen(cmd, 'r')
-
class CygwinCCompilerTestCase(support.TempdirManager,
unittest.TestCase):
@@ -35,29 +21,16 @@ class CygwinCCompilerTestCase(support.TempdirManager,
from distutils import sysconfig
self.old_get_config_h_filename = sysconfig.get_config_h_filename
sysconfig.get_config_h_filename = self._get_config_h_filename
- self.old_find_executable = cygwinccompiler.find_executable
- cygwinccompiler.find_executable = self._find_executable
- self._exes = {}
- self.old_popen = cygwinccompiler.Popen
- FakePopen.test_class = self
- cygwinccompiler.Popen = FakePopen
def tearDown(self):
sys.version = self.version
from distutils import sysconfig
sysconfig.get_config_h_filename = self.old_get_config_h_filename
- cygwinccompiler.find_executable = self.old_find_executable
- cygwinccompiler.Popen = self.old_popen
super(CygwinCCompilerTestCase, self).tearDown()
def _get_config_h_filename(self):
return self.python_h
- def _find_executable(self, name):
- if name in self._exes:
- return name
- return None
-
def test_check_config_h(self):
# check_config_h looks for "GCC" in sys.version first
@@ -81,40 +54,6 @@ class CygwinCCompilerTestCase(support.TempdirManager,
self.write_file(self.python_h, 'xxx __GNUC__ xxx')
self.assertEqual(check_config_h()[0], CONFIG_H_OK)
- def test_get_versions(self):
-
- # get_versions calls distutils.spawn.find_executable on
- # 'gcc', 'ld' and 'dllwrap'
- self.assertEqual(get_versions(), (None, None, None))
-
- # Let's fake we have 'gcc' and it returns '3.4.5'
- self._exes['gcc'] = b'gcc (GCC) 3.4.5 (mingw special)\nFSF'
- res = get_versions()
- self.assertEqual(str(res[0]), '3.4.5')
-
- # and let's see what happens when the version
- # doesn't match the regular expression
- # (\d+\.\d+(\.\d+)*)
- self._exes['gcc'] = b'very strange output'
- res = get_versions()
- self.assertEqual(res[0], None)
-
- # same thing for ld
- self._exes['ld'] = b'GNU ld version 2.17.50 20060824'
- res = get_versions()
- self.assertEqual(str(res[1]), '2.17.50')
- self._exes['ld'] = b'@(#)PROGRAM:ld PROJECT:ld64-77'
- res = get_versions()
- self.assertEqual(res[1], None)
-
- # and dllwrap
- self._exes['dllwrap'] = b'GNU dllwrap 2.17.50 20060824\nFSF'
- res = get_versions()
- self.assertEqual(str(res[2]), '2.17.50')
- self._exes['dllwrap'] = b'Cheese Wrap'
- res = get_versions()
- self.assertEqual(res[2], None)
-
def test_get_msvcr(self):
# none
@@ -141,14 +80,17 @@ class CygwinCCompilerTestCase(support.TempdirManager,
sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
'[MSC v.1500 32 bits (Intel)]')
self.assertEqual(get_msvcr(), ['msvcr90'])
+
+ sys.version = '3.10.0 (tags/v3.10.0:b494f59, Oct 4 2021, 18:46:30) [MSC v.1929 32 bit (Intel)]'
+ self.assertEqual(get_msvcr(), ['ucrt', 'vcruntime140'])
# unknown
sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) '
- '[MSC v.1999 32 bits (Intel)]')
+ '[MSC v.2000 32 bits (Intel)]')
self.assertRaises(ValueError, get_msvcr)
def test_suite():
- return unittest.makeSuite(CygwinCCompilerTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(CygwinCCompilerTestCase)
if __name__ == '__main__':
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_dep_util.py b/setuptools/_distutils/tests/test_dep_util.py
index c6fae39c..0d52740a 100644
--- a/setuptools/_distutils/tests/test_dep_util.py
+++ b/setuptools/_distutils/tests/test_dep_util.py
@@ -74,7 +74,7 @@ class DepUtilTestCase(support.TempdirManager, unittest.TestCase):
def test_suite():
- return unittest.makeSuite(DepUtilTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(DepUtilTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_dir_util.py b/setuptools/_distutils/tests/test_dir_util.py
index d436cf83..1b1f3bbb 100644
--- a/setuptools/_distutils/tests/test_dir_util.py
+++ b/setuptools/_distutils/tests/test_dir_util.py
@@ -133,7 +133,7 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase):
def test_suite():
- return unittest.makeSuite(DirUtilTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(DirUtilTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_dist.py b/setuptools/_distutils/tests/test_dist.py
index 45eadee8..36155be1 100644
--- a/setuptools/_distutils/tests/test_dist.py
+++ b/setuptools/_distutils/tests/test_dist.py
@@ -525,8 +525,8 @@ class MetadataTestCase(support.TempdirManager, support.EnvironGuard,
def test_suite():
suite = unittest.TestSuite()
- suite.addTest(unittest.makeSuite(DistributionTestCase))
- suite.addTest(unittest.makeSuite(MetadataTestCase))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(DistributionTestCase))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(MetadataTestCase))
return suite
if __name__ == "__main__":
diff --git a/setuptools/_distutils/tests/test_extension.py b/setuptools/_distutils/tests/test_extension.py
index 2eb5b422..78a55daa 100644
--- a/setuptools/_distutils/tests/test_extension.py
+++ b/setuptools/_distutils/tests/test_extension.py
@@ -65,7 +65,7 @@ class ExtensionTestCase(unittest.TestCase):
"Unknown Extension options: 'chic'")
def test_suite():
- return unittest.makeSuite(ExtensionTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(ExtensionTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_file_util.py b/setuptools/_distutils/tests/test_file_util.py
index d2536075..81b90d6c 100644
--- a/setuptools/_distutils/tests/test_file_util.py
+++ b/setuptools/_distutils/tests/test_file_util.py
@@ -118,7 +118,7 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase):
def test_suite():
- return unittest.makeSuite(FileUtilTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(FileUtilTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_filelist.py b/setuptools/_distutils/tests/test_filelist.py
index 9ec507b5..a90edcf1 100644
--- a/setuptools/_distutils/tests/test_filelist.py
+++ b/setuptools/_distutils/tests/test_filelist.py
@@ -344,8 +344,8 @@ class FindAllTestCase(unittest.TestCase):
def test_suite():
return unittest.TestSuite([
- unittest.makeSuite(FileListTestCase),
- unittest.makeSuite(FindAllTestCase),
+ unittest.TestLoader().loadTestsFromTestCase(FileListTestCase),
+ unittest.TestLoader().loadTestsFromTestCase(FindAllTestCase),
])
diff --git a/setuptools/_distutils/tests/test_install.py b/setuptools/_distutils/tests/test_install.py
index eb684a09..75770b05 100644
--- a/setuptools/_distutils/tests/test_install.py
+++ b/setuptools/_distutils/tests/test_install.py
@@ -94,7 +94,7 @@ class InstallTestCase(support.TempdirManager,
self.addCleanup(cleanup)
- for key in ('nt_user', 'unix_user'):
+ for key in ('nt_user', 'posix_user'):
self.assertIn(key, INSTALL_SCHEMES)
dist = Distribution({'name': 'xx'})
@@ -244,7 +244,7 @@ class InstallTestCase(support.TempdirManager,
def test_suite():
- return unittest.makeSuite(InstallTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(InstallTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_install_data.py b/setuptools/_distutils/tests/test_install_data.py
index 32ab296a..6191d2fa 100644
--- a/setuptools/_distutils/tests/test_install_data.py
+++ b/setuptools/_distutils/tests/test_install_data.py
@@ -69,7 +69,7 @@ class InstallDataTestCase(support.TempdirManager,
self.assertTrue(os.path.exists(os.path.join(inst, rone)))
def test_suite():
- return unittest.makeSuite(InstallDataTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(InstallDataTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_install_headers.py b/setuptools/_distutils/tests/test_install_headers.py
index 2217b321..1aa4d09c 100644
--- a/setuptools/_distutils/tests/test_install_headers.py
+++ b/setuptools/_distutils/tests/test_install_headers.py
@@ -33,7 +33,7 @@ class InstallHeadersTestCase(support.TempdirManager,
self.assertEqual(len(cmd.get_outputs()), 2)
def test_suite():
- return unittest.makeSuite(InstallHeadersTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(InstallHeadersTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_install_lib.py b/setuptools/_distutils/tests/test_install_lib.py
index fda6315b..652653f2 100644
--- a/setuptools/_distutils/tests/test_install_lib.py
+++ b/setuptools/_distutils/tests/test_install_lib.py
@@ -109,7 +109,7 @@ class InstallLibTestCase(support.TempdirManager,
def test_suite():
- return unittest.makeSuite(InstallLibTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(InstallLibTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_install_scripts.py b/setuptools/_distutils/tests/test_install_scripts.py
index 1f7b1038..648db3b1 100644
--- a/setuptools/_distutils/tests/test_install_scripts.py
+++ b/setuptools/_distutils/tests/test_install_scripts.py
@@ -76,7 +76,7 @@ class InstallScriptsTestCase(support.TempdirManager,
def test_suite():
- return unittest.makeSuite(InstallScriptsTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(InstallScriptsTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_log.py b/setuptools/_distutils/tests/test_log.py
index 75cf9006..ec2ae028 100644
--- a/setuptools/_distutils/tests/test_log.py
+++ b/setuptools/_distutils/tests/test_log.py
@@ -40,7 +40,7 @@ class TestLog(unittest.TestCase):
'Fαtal\t\\xc8rr\\u014dr')
def test_suite():
- return unittest.makeSuite(TestLog)
+ return unittest.TestLoader().loadTestsFromTestCase(TestLog)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_msvc9compiler.py b/setuptools/_distutils/tests/test_msvc9compiler.py
index 77a07ef3..6235405e 100644
--- a/setuptools/_distutils/tests/test_msvc9compiler.py
+++ b/setuptools/_distutils/tests/test_msvc9compiler.py
@@ -178,7 +178,7 @@ class msvc9compilerTestCase(support.TempdirManager,
def test_suite():
- return unittest.makeSuite(msvc9compilerTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(msvc9compilerTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_msvccompiler.py b/setuptools/_distutils/tests/test_msvccompiler.py
index 46a51cd0..846e5bb8 100644
--- a/setuptools/_distutils/tests/test_msvccompiler.py
+++ b/setuptools/_distutils/tests/test_msvccompiler.py
@@ -98,7 +98,7 @@ class TestSpawn(unittest.TestCase):
compiler = _msvccompiler.MSVCCompiler()
compiler._paths = "expected"
inner_cmd = 'import os; assert os.environ["PATH"] == "expected"'
- command = ['python', '-c', inner_cmd]
+ command = [sys.executable, '-c', inner_cmd]
threads = [
CheckThread(target=compiler.spawn, args=[command])
@@ -132,7 +132,7 @@ class TestSpawn(unittest.TestCase):
def test_suite():
- return unittest.makeSuite(msvccompilerTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(msvccompilerTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_register.py b/setuptools/_distutils/tests/test_register.py
index 84607f99..5770ed58 100644
--- a/setuptools/_distutils/tests/test_register.py
+++ b/setuptools/_distutils/tests/test_register.py
@@ -319,7 +319,7 @@ class RegisterTestCase(BasePyPIRCCommandTestCase):
def test_suite():
- return unittest.makeSuite(RegisterTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(RegisterTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_sdist.py b/setuptools/_distutils/tests/test_sdist.py
index b087a817..4c51717c 100644
--- a/setuptools/_distutils/tests/test_sdist.py
+++ b/setuptools/_distutils/tests/test_sdist.py
@@ -7,6 +7,7 @@ import zipfile
from os.path import join
from textwrap import dedent
from test.support import captured_stdout, run_unittest
+from .unix_compat import require_unix_id, require_uid_0, pwd, grp
from .py38compat import check_warnings
@@ -16,13 +17,6 @@ try:
except ImportError:
ZLIB_SUPPORT = False
-try:
- import grp
- import pwd
- UID_GID_SUPPORT = True
-except ImportError:
- UID_GID_SUPPORT = False
-
from distutils.command.sdist import sdist, show_formats
from distutils.core import Distribution
from distutils.tests.test_config import BasePyPIRCCommandTestCase
@@ -440,7 +434,8 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
'fake-1.0/README.manual'])
@unittest.skipUnless(ZLIB_SUPPORT, "requires zlib")
- @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support")
+ @require_unix_id
+ @require_uid_0
@unittest.skipIf(find_executable('tar') is None,
"The tar command is not found")
@unittest.skipIf(find_executable('gzip') is None,
@@ -488,7 +483,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
archive.close()
def test_suite():
- return unittest.makeSuite(SDistTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(SDistTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_spawn.py b/setuptools/_distutils/tests/test_spawn.py
index f620da78..c5ed8e2b 100644
--- a/setuptools/_distutils/tests/test_spawn.py
+++ b/setuptools/_distutils/tests/test_spawn.py
@@ -133,7 +133,7 @@ class SpawnTestCase(support.TempdirManager,
def test_suite():
- return unittest.makeSuite(SpawnTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(SpawnTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_sysconfig.py b/setuptools/_distutils/tests/test_sysconfig.py
index 80cd1599..9de3cb70 100644
--- a/setuptools/_distutils/tests/test_sysconfig.py
+++ b/setuptools/_distutils/tests/test_sysconfig.py
@@ -38,6 +38,12 @@ class SysconfigTestCase(support.EnvironGuard, unittest.TestCase):
config_h = sysconfig.get_config_h_filename()
self.assertTrue(os.path.isfile(config_h), config_h)
+ @unittest.skipIf(sys.platform == 'win32',
+ 'Makefile only exists on Unix like systems')
+ def test_get_makefile_filename(self):
+ makefile = sysconfig.get_makefile_filename()
+ self.assertTrue(os.path.isfile(makefile), makefile)
+
def test_get_python_lib(self):
# XXX doesn't work on Linux when Python was never installed before
#self.assertTrue(os.path.isdir(lib_dir), lib_dir)
@@ -283,10 +289,19 @@ class SysconfigTestCase(support.EnvironGuard, unittest.TestCase):
outs, errs = p.communicate()
self.assertEqual(0, p.returncode, "Subprocess failed: " + outs)
+ def test_parse_config_h(self):
+ config_h = sysconfig.get_config_h_filename()
+ input = {}
+ with open(config_h, encoding="utf-8") as f:
+ result = sysconfig.parse_config_h(f, g=input)
+ self.assertTrue(input is result)
+ with open(config_h, encoding="utf-8") as f:
+ result = sysconfig.parse_config_h(f)
+ self.assertTrue(isinstance(result, dict))
def test_suite():
suite = unittest.TestSuite()
- suite.addTest(unittest.makeSuite(SysconfigTestCase))
+ suite.addTest(unittest.TestLoader().loadTestsFromTestCase(SysconfigTestCase))
return suite
diff --git a/setuptools/_distutils/tests/test_text_file.py b/setuptools/_distutils/tests/test_text_file.py
index 7e76240a..ebac3d52 100644
--- a/setuptools/_distutils/tests/test_text_file.py
+++ b/setuptools/_distutils/tests/test_text_file.py
@@ -101,7 +101,7 @@ class TextFileTestCase(support.TempdirManager, unittest.TestCase):
in_file.close()
def test_suite():
- return unittest.makeSuite(TextFileTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(TextFileTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_unixccompiler.py b/setuptools/_distutils/tests/test_unixccompiler.py
index 1008f58a..4574f77f 100644
--- a/setuptools/_distutils/tests/test_unixccompiler.py
+++ b/setuptools/_distutils/tests/test_unixccompiler.py
@@ -11,9 +11,12 @@ from distutils.errors import DistutilsPlatformError
from distutils.unixccompiler import UnixCCompiler
from distutils.util import _clear_cached_macosx_ver
-class UnixCCompilerTestCase(unittest.TestCase):
+from . import support
+
+class UnixCCompilerTestCase(support.TempdirManager, unittest.TestCase):
def setUp(self):
+ super().setUp()
self._backup_platform = sys.platform
self._backup_get_config_var = sysconfig.get_config_var
self._backup_get_config_vars = sysconfig.get_config_vars
@@ -23,6 +26,7 @@ class UnixCCompilerTestCase(unittest.TestCase):
self.cc = CompilerWrapper()
def tearDown(self):
+ super().tearDown()
sys.platform = self._backup_platform
sysconfig.get_config_var = self._backup_get_config_var
sysconfig.get_config_vars = self._backup_get_config_vars
@@ -177,7 +181,7 @@ class UnixCCompilerTestCase(unittest.TestCase):
elif v == 'GNULD':
return 'yes'
sysconfig.get_config_var = gcv
- self.assertEqual(self.cc.rpath_foo(), '-R/foo')
+ self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo')
# non-GCC non-GNULD
sys.platform = 'bar'
@@ -187,7 +191,7 @@ class UnixCCompilerTestCase(unittest.TestCase):
elif v == 'GNULD':
return 'no'
sysconfig.get_config_var = gcv
- self.assertEqual(self.cc.rpath_foo(), '-R/foo')
+ self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo')
@unittest.skipIf(sys.platform == 'win32', "can't test on Windows")
def test_cc_overrides_ldshared(self):
@@ -232,9 +236,17 @@ class UnixCCompilerTestCase(unittest.TestCase):
sysconfig.customize_compiler(self.cc)
self.assertEqual(self.cc.linker_so[0], 'my_ld')
+ def test_has_function(self):
+ # Issue https://github.com/pypa/distutils/issues/64:
+ # ensure that setting output_dir does not raise
+ # FileNotFoundError: [Errno 2] No such file or directory: 'a.out'
+ self.cc.output_dir = 'scratch'
+ os.chdir(self.mkdtemp())
+ self.cc.has_function('abort', includes=['stdlib.h'])
+
def test_suite():
- return unittest.makeSuite(UnixCCompilerTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(UnixCCompilerTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_upload.py b/setuptools/_distutils/tests/test_upload.py
index bca5516d..ce3e84a2 100644
--- a/setuptools/_distutils/tests/test_upload.py
+++ b/setuptools/_distutils/tests/test_upload.py
@@ -217,7 +217,7 @@ class uploadTestCase(BasePyPIRCCommandTestCase):
def test_suite():
- return unittest.makeSuite(uploadTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(uploadTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_util.py b/setuptools/_distutils/tests/test_util.py
index bf0d4333..2738388e 100644
--- a/setuptools/_distutils/tests/test_util.py
+++ b/setuptools/_distutils/tests/test_util.py
@@ -2,6 +2,7 @@
import os
import sys
import unittest
+import sysconfig as stdlib_sysconfig
from copy import copy
from test.support import run_unittest
from unittest import mock
@@ -10,12 +11,10 @@ from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError
from distutils.util import (get_platform, convert_path, change_root,
check_environ, split_quoted, strtobool,
rfc822_escape, byte_compile,
- grok_environment_error)
+ grok_environment_error, get_host_platform)
from distutils import util # used to patch _environ_checked
-from distutils.sysconfig import get_config_vars
from distutils import sysconfig
from distutils.tests import support
-import _osx_support
class UtilTestCase(support.EnvironGuard, unittest.TestCase):
@@ -63,110 +62,26 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase):
def _get_uname(self):
return self._uname
- def test_get_platform(self):
-
- # windows XP, 32bits
- os.name = 'nt'
- sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) '
- '[MSC v.1310 32 bit (Intel)]')
- sys.platform = 'win32'
- self.assertEqual(get_platform(), 'win32')
-
- # windows XP, amd64
- os.name = 'nt'
- sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) '
- '[MSC v.1310 32 bit (Amd64)]')
- sys.platform = 'win32'
- self.assertEqual(get_platform(), 'win-amd64')
-
- # macbook
- os.name = 'posix'
- sys.version = ('2.5 (r25:51918, Sep 19 2006, 08:49:13) '
- '\n[GCC 4.0.1 (Apple Computer, Inc. build 5341)]')
- sys.platform = 'darwin'
- self._set_uname(('Darwin', 'macziade', '8.11.1',
- ('Darwin Kernel Version 8.11.1: '
- 'Wed Oct 10 18:23:28 PDT 2007; '
- 'root:xnu-792.25.20~1/RELEASE_I386'), 'i386'))
- _osx_support._remove_original_values(get_config_vars())
- get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.3'
-
- get_config_vars()['CFLAGS'] = ('-fno-strict-aliasing -DNDEBUG -g '
- '-fwrapv -O3 -Wall -Wstrict-prototypes')
-
- cursize = sys.maxsize
- sys.maxsize = (2 ** 31)-1
- try:
- self.assertEqual(get_platform(), 'macosx-10.3-i386')
- finally:
- sys.maxsize = cursize
-
- # macbook with fat binaries (fat, universal or fat64)
- _osx_support._remove_original_values(get_config_vars())
- get_config_vars()['MACOSX_DEPLOYMENT_TARGET'] = '10.4'
- get_config_vars()['CFLAGS'] = ('-arch ppc -arch i386 -isysroot '
- '/Developer/SDKs/MacOSX10.4u.sdk '
- '-fno-strict-aliasing -fno-common '
- '-dynamic -DNDEBUG -g -O3')
-
- self.assertEqual(get_platform(), 'macosx-10.4-fat')
-
- _osx_support._remove_original_values(get_config_vars())
- os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.1'
- self.assertEqual(get_platform(), 'macosx-10.4-fat')
-
+ def test_get_host_platform(self):
+ with unittest.mock.patch('os.name', 'nt'):
+ with unittest.mock.patch('sys.version', '... [... (ARM64)]'):
+ self.assertEqual(get_host_platform(), 'win-arm64')
+ with unittest.mock.patch('sys.version', '... [... (ARM)]'):
+ self.assertEqual(get_host_platform(), 'win-arm32')
- _osx_support._remove_original_values(get_config_vars())
- get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot '
- '/Developer/SDKs/MacOSX10.4u.sdk '
- '-fno-strict-aliasing -fno-common '
- '-dynamic -DNDEBUG -g -O3')
+ with unittest.mock.patch('sys.version_info', (3, 9, 0, 'final', 0)):
+ self.assertEqual(get_host_platform(), stdlib_sysconfig.get_platform())
- self.assertEqual(get_platform(), 'macosx-10.4-intel')
-
- _osx_support._remove_original_values(get_config_vars())
- get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot '
- '/Developer/SDKs/MacOSX10.4u.sdk '
- '-fno-strict-aliasing -fno-common '
- '-dynamic -DNDEBUG -g -O3')
- self.assertEqual(get_platform(), 'macosx-10.4-fat3')
-
- _osx_support._remove_original_values(get_config_vars())
- get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot '
- '/Developer/SDKs/MacOSX10.4u.sdk '
- '-fno-strict-aliasing -fno-common '
- '-dynamic -DNDEBUG -g -O3')
- self.assertEqual(get_platform(), 'macosx-10.4-universal')
-
- _osx_support._remove_original_values(get_config_vars())
- get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot '
- '/Developer/SDKs/MacOSX10.4u.sdk '
- '-fno-strict-aliasing -fno-common '
- '-dynamic -DNDEBUG -g -O3')
-
- self.assertEqual(get_platform(), 'macosx-10.4-fat64')
-
- for arch in ('ppc', 'i386', 'x86_64', 'ppc64'):
- _osx_support._remove_original_values(get_config_vars())
- get_config_vars()['CFLAGS'] = ('-arch %s -isysroot '
- '/Developer/SDKs/MacOSX10.4u.sdk '
- '-fno-strict-aliasing -fno-common '
- '-dynamic -DNDEBUG -g -O3'%(arch,))
-
- self.assertEqual(get_platform(), 'macosx-10.4-%s'%(arch,))
-
-
- # linux debian sarge
- os.name = 'posix'
- sys.version = ('2.3.5 (#1, Jul 4 2007, 17:28:59) '
- '\n[GCC 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)]')
- sys.platform = 'linux2'
- self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7',
- '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686'))
-
- self.assertEqual(get_platform(), 'linux-i686')
-
- # XXX more platforms to tests here
+ def test_get_platform(self):
+ with unittest.mock.patch('os.name', 'nt'):
+ with unittest.mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'x86'}):
+ self.assertEqual(get_platform(), 'win32')
+ with unittest.mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'x64'}):
+ self.assertEqual(get_platform(), 'win-amd64')
+ with unittest.mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'arm'}):
+ self.assertEqual(get_platform(), 'win-arm32')
+ with unittest.mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'arm64'}):
+ self.assertEqual(get_platform(), 'win-arm64')
def test_convert_path(self):
# linux/mac
@@ -303,7 +218,7 @@ class UtilTestCase(support.EnvironGuard, unittest.TestCase):
def test_suite():
- return unittest.makeSuite(UtilTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(UtilTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/test_version.py b/setuptools/_distutils/tests/test_version.py
index 8671cd2f..8405aa3a 100644
--- a/setuptools/_distutils/tests/test_version.py
+++ b/setuptools/_distutils/tests/test_version.py
@@ -1,11 +1,19 @@
"""Tests for distutils.version."""
import unittest
+import distutils
from distutils.version import LooseVersion
from distutils.version import StrictVersion
from test.support import run_unittest
class VersionTestCase(unittest.TestCase):
+ def setUp(self):
+ self.ctx = distutils.version.suppress_known_deprecation()
+ self.ctx.__enter__()
+
+ def tearDown(self):
+ self.ctx.__exit__(None, None, None)
+
def test_prerelease(self):
version = StrictVersion('1.2.3a1')
self.assertEqual(version.version, (1, 2, 3))
@@ -81,7 +89,7 @@ class VersionTestCase(unittest.TestCase):
(v1, v2, res))
def test_suite():
- return unittest.makeSuite(VersionTestCase)
+ return unittest.TestLoader().loadTestsFromTestCase(VersionTestCase)
if __name__ == "__main__":
run_unittest(test_suite())
diff --git a/setuptools/_distutils/tests/unix_compat.py b/setuptools/_distutils/tests/unix_compat.py
new file mode 100644
index 00000000..b7718c26
--- /dev/null
+++ b/setuptools/_distutils/tests/unix_compat.py
@@ -0,0 +1,16 @@
+import sys
+import unittest
+
+try:
+ import grp
+ import pwd
+except ImportError:
+ grp = pwd = None
+
+
+UNIX_ID_SUPPORT = grp and pwd
+UID_0_SUPPORT = UNIX_ID_SUPPORT and sys.platform != "cygwin"
+
+require_unix_id = unittest.skipUnless(
+ UNIX_ID_SUPPORT, "Requires grp and pwd support")
+require_uid_0 = unittest.skipUnless(UID_0_SUPPORT, "Requires UID 0 support")
diff --git a/setuptools/_distutils/unixccompiler.py b/setuptools/_distutils/unixccompiler.py
index 349cc164..a07e5988 100644
--- a/setuptools/_distutils/unixccompiler.py
+++ b/setuptools/_distutils/unixccompiler.py
@@ -245,23 +245,16 @@ class UnixCCompiler(CCompiler):
if self._is_gcc(compiler):
return ["-Wl,+s", "-L" + dir]
return ["+s", "-L" + dir]
+
+ # For all compilers, `-Wl` is the presumed way to
+ # pass a compiler option to the linker and `-R` is
+ # the way to pass an RPATH.
+ if sysconfig.get_config_var("GNULD") == "yes":
+ # GNU ld needs an extra option to get a RUNPATH
+ # instead of just an RPATH.
+ return "-Wl,--enable-new-dtags,-R" + dir
else:
- if self._is_gcc(compiler):
- # gcc on non-GNU systems does not need -Wl, but can
- # use it anyway. Since distutils has always passed in
- # -Wl whenever gcc was used in the past it is probably
- # safest to keep doing so.
- if sysconfig.get_config_var("GNULD") == "yes":
- # GNU ld needs an extra option to get a RUNPATH
- # instead of just an RPATH.
- return "-Wl,--enable-new-dtags,-R" + dir
- else:
- return "-Wl,-R" + dir
- else:
- # No idea how --enable-new-dtags would be passed on to
- # ld if this system was using GNU ld. Don't know if a
- # system like this even exists.
- return "-R" + dir
+ return "-Wl,-R" + dir
def library_option(self, lib):
return "-l" + lib
diff --git a/setuptools/_distutils/util.py b/setuptools/_distutils/util.py
index 64f06dd4..6d506d7e 100644
--- a/setuptools/_distutils/util.py
+++ b/setuptools/_distutils/util.py
@@ -9,6 +9,7 @@ import re
import importlib.util
import string
import sys
+import sysconfig
from distutils.errors import DistutilsPlatformError
from distutils.dep_util import newer
from distutils.spawn import spawn
@@ -20,82 +21,29 @@ from .py35compat import _optim_args_from_interpreter_flags
def get_host_platform():
"""Return a string that identifies the current platform. This is used mainly to
distinguish platform-specific build directories and platform-specific built
- distributions. Typically includes the OS name and version and the
- architecture (as supplied by 'os.uname()'), although the exact information
- included depends on the OS; eg. on Linux, the kernel version isn't
- particularly important.
+ distributions.
+ """
- Examples of returned values:
- linux-i586
- linux-alpha (?)
- solaris-2.6-sun4u
+ # We initially exposed platforms as defined in Python 3.9
+ # even with older Python versions when distutils was split out.
+ # Now that we delegate to stdlib sysconfig we need to restore this
+ # in case anyone has started to depend on it.
- Windows will return one of:
- win-amd64 (64bit Windows on AMD64 (aka x86_64, Intel64, EM64T, etc)
- win32 (all others - specifically, sys.platform is returned)
+ if sys.version_info < (3, 8):
+ if os.name == 'nt':
+ if '(arm)' in sys.version.lower():
+ return 'win-arm32'
+ if '(arm64)' in sys.version.lower():
+ return 'win-arm64'
- For other non-POSIX platforms, currently just returns 'sys.platform'.
+ if sys.version_info < (3, 9):
+ if os.name == "posix" and hasattr(os, 'uname'):
+ osname, host, release, version, machine = os.uname()
+ if osname[:3] == "aix":
+ from .py38compat import aix_platform
+ return aix_platform(osname, version, release)
- """
- if os.name == 'nt':
- if 'amd64' in sys.version.lower():
- return 'win-amd64'
- if '(arm)' in sys.version.lower():
- return 'win-arm32'
- if '(arm64)' in sys.version.lower():
- return 'win-arm64'
- return sys.platform
-
- # Set for cross builds explicitly
- if "_PYTHON_HOST_PLATFORM" in os.environ:
- return os.environ["_PYTHON_HOST_PLATFORM"]
-
- if os.name != "posix" or not hasattr(os, 'uname'):
- # XXX what about the architecture? NT is Intel or Alpha,
- # Mac OS is M68k or PPC, etc.
- return sys.platform
-
- # Try to distinguish various flavours of Unix
-
- (osname, host, release, version, machine) = os.uname()
-
- # Convert the OS name to lowercase, remove '/' characters, and translate
- # spaces (for "Power Macintosh")
- osname = osname.lower().replace('/', '')
- machine = machine.replace(' ', '_')
- machine = machine.replace('/', '-')
-
- if osname[:5] == "linux":
- # At least on Linux/Intel, 'machine' is the processor --
- # i386, etc.
- # XXX what about Alpha, SPARC, etc?
- return "%s-%s" % (osname, machine)
- elif osname[:5] == "sunos":
- if release[0] >= "5": # SunOS 5 == Solaris 2
- osname = "solaris"
- release = "%d.%s" % (int(release[0]) - 3, release[2:])
- # We can't use "platform.architecture()[0]" because a
- # bootstrap problem. We use a dict to get an error
- # if some suspicious happens.
- bitness = {2147483647:"32bit", 9223372036854775807:"64bit"}
- machine += ".%s" % bitness[sys.maxsize]
- # fall through to standard osname-release-machine representation
- elif osname[:3] == "aix":
- from .py38compat import aix_platform
- return aix_platform(osname, version, release)
- elif osname[:6] == "cygwin":
- osname = "cygwin"
- rel_re = re.compile (r'[\d.]+', re.ASCII)
- m = rel_re.match(release)
- if m:
- release = m.group()
- elif osname[:6] == "darwin":
- import _osx_support, distutils.sysconfig
- osname, release, machine = _osx_support.get_platform_osx(
- distutils.sysconfig.get_config_vars(),
- osname, release, machine)
-
- return "%s-%s-%s" % (osname, release, machine)
+ return sysconfig.get_platform()
def get_platform():
if os.name == 'nt':
@@ -134,14 +82,14 @@ def get_macosx_target_ver():
"""Return the version of macOS for which we are building.
The target version defaults to the version in sysconfig latched at time
- the Python interpreter was built, unless overriden by an environment
+ the Python interpreter was built, unless overridden by an environment
variable. If neither source has a value, then None is returned"""
syscfg_ver = get_macosx_target_ver_from_syscfg()
env_ver = os.environ.get(MACOSX_VERSION_VAR)
if env_ver:
- # Validate overriden version against sysconfig version, if have both.
+ # Validate overridden version against sysconfig version, if have both.
# Ensure that the deployment target of the build process is not less
# than 10.3 if the interpreter was built for 10.3 or later. This
# ensures extension modules are built with correct compatibility
@@ -242,30 +190,43 @@ def check_environ ():
def subst_vars (s, local_vars):
- """Perform shell/Perl-style variable substitution on 'string'. Every
- occurrence of '$' followed by a name is considered a variable, and
- variable is substituted by the value found in the 'local_vars'
- dictionary, or in 'os.environ' if it's not in 'local_vars'.
+ """
+ Perform variable substitution on 'string'.
+ Variables are indicated by format-style braces ("{var}").
+ Variable is substituted by the value found in the 'local_vars'
+ dictionary or in 'os.environ' if it's not in 'local_vars'.
'os.environ' is first checked/augmented to guarantee that it contains
certain values: see 'check_environ()'. Raise ValueError for any
variables not found in either 'local_vars' or 'os.environ'.
"""
check_environ()
- def _subst (match, local_vars=local_vars):
- var_name = match.group(1)
- if var_name in local_vars:
- return str(local_vars[var_name])
- else:
- return os.environ[var_name]
-
+ lookup = dict(os.environ)
+ lookup.update((name, str(value)) for name, value in local_vars.items())
try:
- return re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s)
+ return _subst_compat(s).format_map(lookup)
except KeyError as var:
- raise ValueError("invalid variable '$%s'" % var)
+ raise ValueError(f"invalid variable {var}")
# subst_vars ()
+def _subst_compat(s):
+ """
+ Replace shell/Perl-style variable substitution with
+ format-style. For compatibility.
+ """
+ def _subst(match):
+ return f'{{{match.group(1)}}}'
+ repl = re.sub(r'\$([a-zA-Z_][a-zA-Z_0-9]*)', _subst, s)
+ if repl != s:
+ import warnings
+ warnings.warn(
+ "shell/Perl-style substitions are deprecated",
+ DeprecationWarning,
+ )
+ return repl
+
+
def grok_environment_error (exc, prefix="error: "):
# Function kept for backward compatibility.
# Used to try clever things with EnvironmentErrors,
diff --git a/setuptools/_distutils/version.py b/setuptools/_distutils/version.py
index c33bebae..35e181db 100644
--- a/setuptools/_distutils/version.py
+++ b/setuptools/_distutils/version.py
@@ -27,6 +27,20 @@ Every version number class implements the following interface:
"""
import re
+import warnings
+import contextlib
+
+
+@contextlib.contextmanager
+def suppress_known_deprecation():
+ with warnings.catch_warnings(record=True) as ctx:
+ warnings.filterwarnings(
+ action='default',
+ category=DeprecationWarning,
+ message="distutils Version classes are deprecated.",
+ )
+ yield ctx
+
class Version:
"""Abstract base class for version numbering classes. Just provides
@@ -36,6 +50,12 @@ class Version:
"""
def __init__ (self, vstring=None):
+ warnings.warn(
+ "distutils Version classes are deprecated. "
+ "Use packaging.version instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
if vstring:
self.parse(vstring)
@@ -165,7 +185,8 @@ class StrictVersion (Version):
def _cmp (self, other):
if isinstance(other, str):
- other = StrictVersion(other)
+ with suppress_known_deprecation():
+ other = StrictVersion(other)
elif not isinstance(other, StrictVersion):
return NotImplemented
@@ -301,11 +322,6 @@ class LooseVersion (Version):
component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
- def __init__ (self, vstring=None):
- if vstring:
- self.parse(vstring)
-
-
def parse (self, vstring):
# I've given up on thinking I can reconstruct the version string
# from the parsed tuple -- so I just store the string here for
diff --git a/setuptools/_distutils/versionpredicate.py b/setuptools/_distutils/versionpredicate.py
index 062c98f2..55f25d91 100644
--- a/setuptools/_distutils/versionpredicate.py
+++ b/setuptools/_distutils/versionpredicate.py
@@ -23,7 +23,9 @@ def splitUp(pred):
if not res:
raise ValueError("bad package restriction syntax: %r" % pred)
comp, verStr = res.groups()
- return (comp, distutils.version.StrictVersion(verStr))
+ with distutils.version.suppress_known_deprecation():
+ other = distutils.version.StrictVersion(verStr)
+ return (comp, other)
compmap = {"<": operator.lt, "<=": operator.le, "==": operator.eq,
">": operator.gt, ">=": operator.ge, "!=": operator.ne}
@@ -162,5 +164,6 @@ def split_provision(value):
raise ValueError("illegal provides specification: %r" % value)
ver = m.group(2) or None
if ver:
- ver = distutils.version.StrictVersion(ver)
+ with distutils.version.suppress_known_deprecation():
+ ver = distutils.version.StrictVersion(ver)
return m.group(1), ver
diff --git a/setuptools/_vendor/more_itertools-8.8.0.dist-info/INSTALLER b/setuptools/_vendor/more_itertools-8.8.0.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/setuptools/_vendor/more_itertools-8.8.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/setuptools/_vendor/more_itertools-8.8.0.dist-info/LICENSE b/setuptools/_vendor/more_itertools-8.8.0.dist-info/LICENSE
new file mode 100644
index 00000000..0a523bec
--- /dev/null
+++ b/setuptools/_vendor/more_itertools-8.8.0.dist-info/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2012 Erik Rose
+
+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/setuptools/_vendor/more_itertools-8.8.0.dist-info/METADATA b/setuptools/_vendor/more_itertools-8.8.0.dist-info/METADATA
new file mode 100644
index 00000000..bdaee655
--- /dev/null
+++ b/setuptools/_vendor/more_itertools-8.8.0.dist-info/METADATA
@@ -0,0 +1,462 @@
+Metadata-Version: 2.1
+Name: more-itertools
+Version: 8.8.0
+Summary: More routines for operating on iterables, beyond itertools
+Home-page: https://github.com/more-itertools/more-itertools
+Author: Erik Rose
+Author-email: erikrose@grinchcentral.com
+License: MIT
+Keywords: itertools,iterator,iteration,filter,peek,peekable,collate,chunk,chunked
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Natural Language :: English
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Software Development :: Libraries
+Requires-Python: >=3.5
+Description-Content-Type: text/x-rst
+
+==============
+More Itertools
+==============
+
+.. image:: https://readthedocs.org/projects/more-itertools/badge/?version=latest
+ :target: https://more-itertools.readthedocs.io/en/stable/
+
+Python's ``itertools`` library is a gem - you can compose elegant solutions
+for a variety of problems with the functions it provides. In ``more-itertools``
+we collect additional building blocks, recipes, and routines for working with
+Python iterables.
+
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Grouping | `chunked <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.chunked>`_, |
+| | `ichunked <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ichunked>`_, |
+| | `sliced <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sliced>`_, |
+| | `distribute <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distribute>`_, |
+| | `divide <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.divide>`_, |
+| | `split_at <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_at>`_, |
+| | `split_before <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_before>`_, |
+| | `split_after <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_after>`_, |
+| | `split_into <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_into>`_, |
+| | `split_when <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.split_when>`_, |
+| | `bucket <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.bucket>`_, |
+| | `unzip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unzip>`_, |
+| | `grouper <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.grouper>`_, |
+| | `partition <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.partition>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Lookahead and lookback | `spy <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.spy>`_, |
+| | `peekable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.peekable>`_, |
+| | `seekable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.seekable>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Windowing | `windowed <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.windowed>`_, |
+| | `substrings <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.substrings>`_, |
+| | `substrings_indexes <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.substrings_indexes>`_, |
+| | `stagger <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.stagger>`_, |
+| | `windowed_complete <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.windowed_complete>`_, |
+| | `pairwise <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.pairwise>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Augmenting | `count_cycle <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.count_cycle>`_, |
+| | `intersperse <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.intersperse>`_, |
+| | `padded <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.padded>`_, |
+| | `mark_ends <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.mark_ends>`_, |
+| | `repeat_last <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeat_last>`_, |
+| | `adjacent <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.adjacent>`_, |
+| | `groupby_transform <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.groupby_transform>`_, |
+| | `padnone <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.padnone>`_, |
+| | `ncycles <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ncycles>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Combining | `collapse <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.collapse>`_, |
+| | `sort_together <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sort_together>`_, |
+| | `interleave <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave>`_, |
+| | `interleave_longest <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.interleave_longest>`_, |
+| | `zip_offset <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_offset>`_, |
+| | `zip_equal <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.zip_equal>`_, |
+| | `dotproduct <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.dotproduct>`_, |
+| | `convolve <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.convolve>`_, |
+| | `flatten <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.flatten>`_, |
+| | `roundrobin <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.roundrobin>`_, |
+| | `prepend <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.prepend>`_, |
+| | `value_chain <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.value_chain>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Summarizing | `ilen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.ilen>`_, |
+| | `unique_to_each <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unique_to_each>`_, |
+| | `sample <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.sample>`_, |
+| | `consecutive_groups <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consecutive_groups>`_, |
+| | `run_length <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.run_length>`_, |
+| | `map_reduce <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.map_reduce>`_, |
+| | `exactly_n <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.exactly_n>`_, |
+| | `is_sorted <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.is_sorted>`_, |
+| | `all_equal <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.all_equal>`_, |
+| | `all_unique <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.all_unique>`_, |
+| | `first_true <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.first_true>`_, |
+| | `quantify <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.quantify>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Selecting | `islice_extended <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.islice_extended>`_, |
+| | `first <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.first>`_, |
+| | `last <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.last>`_, |
+| | `one <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.one>`_, |
+| | `only <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.only>`_, |
+| | `strip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.strip>`_, |
+| | `lstrip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.lstrip>`_, |
+| | `rstrip <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.rstrip>`_, |
+| | `filter_except <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.filter_except>`_ |
+| | `map_except <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.map_except>`_ |
+| | `nth_or_last <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_or_last>`_, |
+| | `nth <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth>`_, |
+| | `take <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.take>`_, |
+| | `tail <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.tail>`_, |
+| | `unique_everseen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertoo ls.unique_everseen>`_, |
+| | `unique_justseen <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.unique_justseen>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Combinatorics | `distinct_permutations <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distinct_permutations>`_, |
+| | `distinct_combinations <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.distinct_combinations>`_, |
+| | `circular_shifts <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.circular_shifts>`_, |
+| | `partitions <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.partitions>`_, |
+| | `set_partitions <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.set_partitions>`_, |
+| | `product_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.product_index>`_, |
+| | `combination_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.combination_index>`_, |
+| | `permutation_index <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.permutation_index>`_, |
+| | `powerset <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.powerset>`_, |
+| | `random_product <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_product>`_, |
+| | `random_permutation <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_permutation>`_, |
+| | `random_combination <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_combination>`_, |
+| | `random_combination_with_replacement <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.random_combination_with_replacement>`_, |
+| | `nth_product <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_product>`_ |
+| | `nth_permutation <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_permutation>`_ |
+| | `nth_combination <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_combination>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Wrapping | `always_iterable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_iterable>`_, |
+| | `always_reversible <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.always_reversible>`_, |
+| | `countable <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.countable>`_, |
+| | `consumer <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consumer>`_, |
+| | `with_iter <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.with_iter>`_, |
+| | `iter_except <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.iter_except>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+| Others | `locate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.locate>`_, |
+| | `rlocate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.rlocate>`_, |
+| | `replace <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.replace>`_, |
+| | `numeric_range <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.numeric_range>`_, |
+| | `side_effect <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.side_effect>`_, |
+| | `iterate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.iterate>`_, |
+| | `difference <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.difference>`_, |
+| | `make_decorator <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.make_decorator>`_, |
+| | `SequenceView <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.SequenceView>`_, |
+| | `time_limited <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.time_limited>`_, |
+| | `consume <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.consume>`_, |
+| | `tabulate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.tabulate>`_, |
+| | `repeatfunc <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.repeatfunc>`_ |
++------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
+
+
+Getting started
+===============
+
+To get started, install the library with `pip <https://pip.pypa.io/en/stable/>`_:
+
+.. code-block:: shell
+
+ pip install more-itertools
+
+The recipes from the `itertools docs <https://docs.python.org/3/library/itertools.html#itertools-recipes>`_
+are included in the top-level package:
+
+.. code-block:: python
+
+ >>> from more_itertools import flatten
+ >>> iterable = [(0, 1), (2, 3)]
+ >>> list(flatten(iterable))
+ [0, 1, 2, 3]
+
+Several new recipes are available as well:
+
+.. code-block:: python
+
+ >>> from more_itertools import chunked
+ >>> iterable = [0, 1, 2, 3, 4, 5, 6, 7, 8]
+ >>> list(chunked(iterable, 3))
+ [[0, 1, 2], [3, 4, 5], [6, 7, 8]]
+
+ >>> from more_itertools import spy
+ >>> iterable = (x * x for x in range(1, 6))
+ >>> head, iterable = spy(iterable, n=3)
+ >>> list(head)
+ [1, 4, 9]
+ >>> list(iterable)
+ [1, 4, 9, 16, 25]
+
+
+
+For the full listing of functions, see the `API documentation <https://more-itertools.readthedocs.io/en/stable/api.html>`_.
+
+
+Links elsewhere
+===============
+
+Blog posts about ``more-itertools``:
+
+* `Yo, I heard you like decorators <https://www.bbayles.com/index/decorator_factory>`__
+* `Tour of Python Itertools <https://martinheinz.dev/blog/16>`__ (`Alternate <https://dev.to/martinheinz/tour-of-python-itertools-4122>`__)
+
+
+Development
+===========
+
+``more-itertools`` is maintained by `@erikrose <https://github.com/erikrose>`_
+and `@bbayles <https://github.com/bbayles>`_, with help from `many others <https://github.com/more-itertools/more-itertools/graphs/contributors>`_.
+If you have a problem or suggestion, please file a bug or pull request in this
+repository. Thanks for contributing!
+
+
+Version History
+===============
+
+
+ :noindex:
+
+8.8.0
+-----
+
+* New functions
+ * countable (thanks to krzysieq)
+
+* Changes to existing functions
+ * split_before was updated to handle empy collections (thanks to TiunovNN)
+ * unique_everseen got a performance boost (thanks to Numerlor)
+ * The type hint for value_chain was corrected (thanks to vr2262)
+
+8.7.0
+-----
+
+* New functions
+ * convolve (from the Python itertools docs)
+ * product_index, combination_index, and permutation_index (thanks to N8Brooks)
+ * value_chain (thanks to jenstroeger)
+
+* Changes to existing functions
+ * distinct_combinations now uses a non-recursive algorithm (thanks to knutdrand)
+ * pad_none is now the preferred name for padnone, though the latter remains available.
+ * pairwise will now use the Python standard library implementation on Python 3.10+
+ * sort_together now accepts a ``key`` argument (thanks to brianmaissy)
+ * seekable now has a ``peek`` method, and can indicate whether the iterator it's wrapping is exhausted (thanks to gsakkis)
+ * time_limited can now indicate whether its iterator has expired (thanks to roysmith)
+ * The implementation of unique_everseen was improved (thanks to plammens)
+
+* Other changes:
+ * Various documentation updates (thanks to cthoyt, Evantm, and cyphase)
+
+8.6.0
+-----
+
+* New itertools
+ * all_unique (thanks to brianmaissy)
+ * nth_product and nth_permutation (thanks to N8Brooks)
+
+* Changes to existing itertools
+ * chunked and sliced now accept a ``strict`` parameter (thanks to shlomif and jtwool)
+
+* Other changes
+ * Python 3.5 has reached its end of life and is no longer supported.
+ * Python 3.9 is officially supported.
+ * Various documentation fixes (thanks to timgates42)
+
+8.5.0
+-----
+
+* New itertools
+ * windowed_complete (thanks to MarcinKonowalczyk)
+
+* Changes to existing itertools:
+ * The is_sorted implementation was improved (thanks to cool-RR)
+ * The groupby_transform now accepts a ``reducefunc`` parameter.
+ * The last implementation was improved (thanks to brianmaissy)
+
+* Other changes
+ * Various documentation fixes (thanks to craigrosie, samuelstjean, PiCT0)
+ * The tests for distinct_combinations were improved (thanks to Minabsapi)
+ * Automated tests now run on GitHub Actions. All commits now check:
+ * That unit tests pass
+ * That the examples in docstrings work
+ * That test coverage remains high (using `coverage`)
+ * For linting errors (using `flake8`)
+ * For consistent style (using `black`)
+ * That the type stubs work (using `mypy`)
+ * That the docs build correctly (using `sphinx`)
+ * That packages build correctly (using `twine`)
+
+8.4.0
+-----
+
+* New itertools
+ * mark_ends (thanks to kalekundert)
+ * is_sorted
+
+* Changes to existing itertools:
+ * islice_extended can now be used with real slices (thanks to cool-RR)
+ * The implementations for filter_except and map_except were improved (thanks to SergBobrovsky)
+
+* Other changes
+ * Automated tests now enforce code style (using `black <https://github.com/psf/black>`__)
+ * The various signatures of islice_extended and numeric_range now appear in the docs (thanks to dsfulf)
+ * The test configuration for mypy was updated (thanks to blueyed)
+
+
+8.3.0
+-----
+
+* New itertools
+ * zip_equal (thanks to frankier and alexmojaki)
+
+* Changes to existing itertools:
+ * split_at, split_before, split_after, and split_when all got a ``maxsplit`` paramter (thanks to jferard and ilai-deutel)
+ * split_at now accepts a ``keep_separator`` parameter (thanks to jferard)
+ * distinct_permutations can now generate ``r``-length permutations (thanks to SergBobrovsky and ilai-deutel)
+ * The windowed implementation was improved (thanks to SergBobrovsky)
+ * The spy implementation was improved (thanks to has2k1)
+
+* Other changes
+ * Type stubs are now tested with ``stubtest`` (thanks to ilai-deutel)
+ * Tests now run with ``python -m unittest`` instead of ``python setup.py test`` (thanks to jdufresne)
+
+8.2.0
+-----
+
+* Bug fixes
+ * The .pyi files for typing were updated. (thanks to blueyed and ilai-deutel)
+
+* Changes to existing itertools:
+ * numeric_range now behaves more like the built-in range. (thanks to jferard)
+ * bucket now allows for enumerating keys. (thanks to alexchandel)
+ * sliced now should now work for numpy arrays. (thanks to sswingle)
+ * seekable now has a ``maxlen`` parameter.
+
+8.1.0
+-----
+
+* Bug fixes
+ * partition works with ``pred=None`` again. (thanks to MSeifert04)
+
+* New itertools
+ * sample (thanks to tommyod)
+ * nth_or_last (thanks to d-ryzhikov)
+
+* Changes to existing itertools:
+ * The implementation for divide was improved. (thanks to jferard)
+
+8.0.2
+-----
+
+* Bug fixes
+ * The type stub files are now part of the wheel distribution (thanks to keisheiled)
+
+8.0.1
+-----
+
+* Bug fixes
+ * The type stub files now work for functions imported from the
+ root package (thanks to keisheiled)
+
+8.0.0
+-----
+
+* New itertools and other additions
+ * This library now ships type hints for use with mypy.
+ (thanks to ilai-deutel for the implementation, and to gabbard and fmagin for assistance)
+ * split_when (thanks to jferard)
+ * repeat_last (thanks to d-ryzhikov)
+
+* Changes to existing itertools:
+ * The implementation for set_partitions was improved. (thanks to jferard)
+ * partition was optimized for expensive predicates. (thanks to stevecj)
+ * unique_everseen and groupby_transform were re-factored. (thanks to SergBobrovsky)
+ * The implementation for difference was improved. (thanks to Jabbey92)
+
+* Other changes
+ * Python 3.4 has reached its end of life and is no longer supported.
+ * Python 3.8 is officially supported. (thanks to jdufresne)
+ * The ``collate`` function has been deprecated.
+ It raises a ``DeprecationWarning`` if used, and will be removed in a future release.
+ * one and only now provide more informative error messages. (thanks to gabbard)
+ * Unit tests were moved outside of the main package (thanks to jdufresne)
+ * Various documentation fixes (thanks to kriomant, gabbard, jdufresne)
+
+
+7.2.0
+-----
+
+* New itertools
+ * distinct_combinations
+ * set_partitions (thanks to kbarrett)
+ * filter_except
+ * map_except
+
+7.1.0
+-----
+
+* New itertools
+ * ichunked (thanks davebelais and youtux)
+ * only (thanks jaraco)
+
+* Changes to existing itertools:
+ * numeric_range now supports ranges specified by
+ ``datetime.datetime`` and ``datetime.timedelta`` objects (thanks to MSeifert04 for tests).
+ * difference now supports an *initial* keyword argument.
+
+
+* Other changes
+ * Various documentation fixes (thanks raimon49, pylang)
+
+7.0.0
+-----
+
+* New itertools:
+ * time_limited
+ * partitions (thanks to rominf and Saluev)
+ * substrings_indexes (thanks to rominf)
+
+* Changes to existing itertools:
+ * collapse now treats ``bytes`` objects the same as ``str`` objects. (thanks to Sweenpet)
+
+The major version update is due to the change in the default behavior of
+collapse. It now treats ``bytes`` objects the same as ``str`` objects.
+This aligns its behavior with always_iterable.
+
+.. code-block:: python
+
+ >>> from more_itertools import collapse
+ >>> iterable = [[1, 2], b'345', [6]]
+ >>> print(list(collapse(iterable)))
+ [1, 2, b'345', 6]
+
+6.0.0
+-----
+
+* Major changes:
+ * Python 2.7 is no longer supported. The 5.0.0 release will be the last
+ version targeting Python 2.7.
+ * All future releases will target the active versions of Python 3.
+ As of 2019, those are Python 3.4 and above.
+ * The ``six`` library is no longer a dependency.
+ * The accumulate function is no longer part of this library. You
+ may import a better version from the standard ``itertools`` module.
+
+* Changes to existing itertools:
+ * The order of the parameters in grouper have changed to match
+ the latest recipe in the itertools documentation. Use of the old order
+ will be supported in this release, but emit a ``DeprecationWarning``.
+ The legacy behavior will be dropped in a future release. (thanks to jaraco)
+ * distinct_permutations was improved (thanks to jferard - see also `permutations with unique values <https://stackoverflow.com/questions/6284396/permutations-with-unique-values>`_ at StackOverflow.)
+ * An unused parameter was removed from substrings. (thanks to pylang)
+
+* Other changes:
+ * The docs for unique_everseen were improved. (thanks to jferard and MSeifert04)
+ * Several Python 2-isms were removed. (thanks to jaraco, MSeifert04, and hugovk)
+
+
diff --git a/setuptools/_vendor/more_itertools-8.8.0.dist-info/RECORD b/setuptools/_vendor/more_itertools-8.8.0.dist-info/RECORD
new file mode 100644
index 00000000..36ffbd86
--- /dev/null
+++ b/setuptools/_vendor/more_itertools-8.8.0.dist-info/RECORD
@@ -0,0 +1,17 @@
+more_itertools-8.8.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+more_itertools-8.8.0.dist-info/LICENSE,sha256=CfHIyelBrz5YTVlkHqm4fYPAyw_QB-te85Gn4mQ8GkY,1053
+more_itertools-8.8.0.dist-info/METADATA,sha256=Gke9w7RnfiAvveik_iBBrzd0RjrDhsQ8uRYNBJdo4qQ,40482
+more_itertools-8.8.0.dist-info/RECORD,,
+more_itertools-8.8.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+more_itertools-8.8.0.dist-info/WHEEL,sha256=OqRkF0eY5GHssMorFjlbTIq072vpHpF60fIQA6lS9xA,92
+more_itertools-8.8.0.dist-info/top_level.txt,sha256=fAuqRXu9LPhxdB9ujJowcFOu1rZ8wzSpOW9_jlKis6M,15
+more_itertools/__init__.py,sha256=C7sXffHTXM3P-iaLPPfqfmDoxOflQMJLcM7ed9p3jak,82
+more_itertools/__init__.pyi,sha256=5B3eTzON1BBuOLob1vCflyEb2lSd6usXQQ-Cv-hXkeA,43
+more_itertools/__pycache__/__init__.cpython-310.pyc,,
+more_itertools/__pycache__/more.cpython-310.pyc,,
+more_itertools/__pycache__/recipes.cpython-310.pyc,,
+more_itertools/more.py,sha256=DlZa8v6JihVwfQ5zHidOA-xDE0orcQIUyxVnCaUoDKE,117968
+more_itertools/more.pyi,sha256=r32pH2raBC1zih3evK4fyvAXvrUamJqc6dgV7QCRL_M,14977
+more_itertools/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+more_itertools/recipes.py,sha256=UkNkrsZyqiwgLHANBTmvMhCvaNSvSNYhyOpz_Jc55DY,16256
+more_itertools/recipes.pyi,sha256=9BpeKd5_qalYVSnuHfqPSCfoGgqnQY2Xu9pNwrDlHU8,3551
diff --git a/setuptools/_vendor/more_itertools-8.8.0.dist-info/REQUESTED b/setuptools/_vendor/more_itertools-8.8.0.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/more_itertools-8.8.0.dist-info/REQUESTED
diff --git a/setuptools/_vendor/more_itertools-8.8.0.dist-info/WHEEL b/setuptools/_vendor/more_itertools-8.8.0.dist-info/WHEEL
new file mode 100644
index 00000000..385faab0
--- /dev/null
+++ b/setuptools/_vendor/more_itertools-8.8.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.36.2)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/setuptools/_vendor/more_itertools-8.8.0.dist-info/top_level.txt b/setuptools/_vendor/more_itertools-8.8.0.dist-info/top_level.txt
new file mode 100644
index 00000000..a5035bef
--- /dev/null
+++ b/setuptools/_vendor/more_itertools-8.8.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+more_itertools
diff --git a/setuptools/_vendor/ordered_set-3.1.1.dist-info/INSTALLER b/setuptools/_vendor/ordered_set-3.1.1.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/setuptools/_vendor/ordered_set-3.1.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/setuptools/_vendor/ordered_set-3.1.1.dist-info/METADATA b/setuptools/_vendor/ordered_set-3.1.1.dist-info/METADATA
new file mode 100644
index 00000000..db6e12f2
--- /dev/null
+++ b/setuptools/_vendor/ordered_set-3.1.1.dist-info/METADATA
@@ -0,0 +1,157 @@
+Metadata-Version: 2.1
+Name: ordered-set
+Version: 3.1.1
+Summary: A MutableSet that remembers its order, so that every entry has an index.
+Home-page: https://github.com/LuminosoInsight/ordered-set
+Maintainer: Robyn Speer
+Maintainer-email: rspeer@luminoso.com
+License: MIT-LICENSE
+Platform: any
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Requires-Python: >=2.7
+Description-Content-Type: text/markdown
+License-File: MIT-LICENSE
+
+[![Travis](https://img.shields.io/travis/LuminosoInsight/ordered-set/master.svg?label=Travis%20CI)](https://travis-ci.org/LuminosoInsight/ordered-set)
+[![Codecov](https://codecov.io/github/LuminosoInsight/ordered-set/badge.svg?branch=master&service=github)](https://codecov.io/github/LuminosoInsight/ordered-set?branch=master)
+[![Pypi](https://img.shields.io/pypi/v/ordered-set.svg)](https://pypi.python.org/pypi/ordered-set)
+
+An OrderedSet is a mutable data structure that is a hybrid of a list and a set.
+It remembers the order of its entries, and every entry has an index number that
+can be looked up.
+
+
+## Usage examples
+
+An OrderedSet is created and used like a set:
+
+ >>> from ordered_set import OrderedSet
+
+ >>> letters = OrderedSet('abracadabra')
+
+ >>> letters
+ OrderedSet(['a', 'b', 'r', 'c', 'd'])
+
+ >>> 'r' in letters
+ True
+
+It is efficient to find the index of an entry in an OrderedSet, or find an
+entry by its index. To help with this use case, the `.add()` method returns
+the index of the added item, whether it was already in the set or not.
+
+ >>> letters.index('r')
+ 2
+
+ >>> letters[2]
+ 'r'
+
+ >>> letters.add('r')
+ 2
+
+ >>> letters.add('x')
+ 5
+
+OrderedSets implement the union (`|`), intersection (`&`), and difference (`-`)
+operators like sets do.
+
+ >>> letters |= OrderedSet('shazam')
+
+ >>> letters
+ OrderedSet(['a', 'b', 'r', 'c', 'd', 'x', 's', 'h', 'z', 'm'])
+
+ >>> letters & set('aeiou')
+ OrderedSet(['a'])
+
+ >>> letters -= 'abcd'
+
+ >>> letters
+ OrderedSet(['r', 'x', 's', 'h', 'z', 'm'])
+
+The `__getitem__()` and `index()` methods have been extended to accept any
+iterable except a string, returning a list, to perform NumPy-like "fancy
+indexing".
+
+ >>> letters = OrderedSet('abracadabra')
+
+ >>> letters[[0, 2, 3]]
+ ['a', 'r', 'c']
+
+ >>> letters.index(['a', 'r', 'c'])
+ [0, 2, 3]
+
+OrderedSet implements `__getstate__` and `__setstate__` so it can be pickled,
+and implements the abstract base classes `collections.MutableSet` and
+`collections.Sequence`.
+
+
+## Interoperability with NumPy and Pandas
+
+An OrderedSet can be used as a bi-directional mapping between a sparse
+vocabulary and dense index numbers. As of version 3.1, it accepts NumPy arrays
+of index numbers as well as lists.
+
+This combination of features makes OrderedSet a simple implementation of many
+of the things that `pandas.Index` is used for, and many of its operations are
+faster than the equivalent pandas operations.
+
+For further compatibility with pandas.Index, `get_loc` (the pandas method for
+looking up a single index) and `get_indexer` (the pandas method for fancy
+indexing in reverse) are both aliases for `index` (which handles both cases
+in OrderedSet).
+
+
+## Type hinting
+To use type hinting features install `ordered-set-stubs` package from
+[PyPI](https://pypi.org/project/ordered-set-stubs/):
+
+ $ pip install ordered-set-stubs
+
+
+## Authors
+
+OrderedSet was implemented by Robyn Speer. Jon Crall contributed changes and
+tests to make it fit the Python set API.
+
+
+## Comparisons
+
+The original implementation of OrderedSet was a [recipe posted to ActiveState
+Recipes][recipe] by Raymond Hettiger, released under the MIT license.
+
+[recipe]: https://code.activestate.com/recipes/576694-orderedset/
+
+Hettiger's implementation kept its content in a doubly-linked list referenced by a
+dict. As a result, looking up an item by its index was an O(N) operation, while
+deletion was O(1).
+
+This version makes different trade-offs for the sake of efficient lookups. Its
+content is a standard Python list instead of a doubly-linked list. This
+provides O(1) lookups by index at the expense of O(N) deletion, as well as
+slightly faster iteration.
+
+In Python 3.6 and later, the built-in `dict` type is inherently ordered. If you
+ignore the dictionary values, that also gives you a simple ordered set, with
+fast O(1) insertion, deletion, iteration and membership testing. However, `dict`
+does not provide the list-like random access features of OrderedSet. You
+would have to convert it to a list in O(N) to look up the index of an entry or
+look up an entry by its index.
+
+
+## Compatibility
+
+OrderedSet is automatically tested on Python 2.7, 3.4, 3.5, 3.6, and 3.7.
+We've checked more informally that it works on PyPy and PyPy3.
+
+
diff --git a/setuptools/_vendor/ordered_set-3.1.1.dist-info/MIT-LICENSE b/setuptools/_vendor/ordered_set-3.1.1.dist-info/MIT-LICENSE
new file mode 100644
index 00000000..25117ef4
--- /dev/null
+++ b/setuptools/_vendor/ordered_set-3.1.1.dist-info/MIT-LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2018 Luminoso Technologies, Inc.
+
+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/setuptools/_vendor/ordered_set-3.1.1.dist-info/RECORD b/setuptools/_vendor/ordered_set-3.1.1.dist-info/RECORD
new file mode 100644
index 00000000..89579a07
--- /dev/null
+++ b/setuptools/_vendor/ordered_set-3.1.1.dist-info/RECORD
@@ -0,0 +1,9 @@
+__pycache__/ordered_set.cpython-310.pyc,,
+ordered_set-3.1.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+ordered_set-3.1.1.dist-info/METADATA,sha256=uGvfFaNmhcl69lGdHmyOXc30N3U6Jn8DByfh_VHEPpw,5359
+ordered_set-3.1.1.dist-info/MIT-LICENSE,sha256=TvRE7qUSUBcd0ols7wgNf3zDEEJWW7kv7WDRySrMBBE,1071
+ordered_set-3.1.1.dist-info/RECORD,,
+ordered_set-3.1.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+ordered_set-3.1.1.dist-info/WHEEL,sha256=z9j0xAa_JmUKMpmz72K0ZGALSM_n-wQVmGbleXx2VHg,110
+ordered_set-3.1.1.dist-info/top_level.txt,sha256=NTY2_aDi1Do9fl3Z9EmWPxasFkUeW2dzO2D3RDx5CfM,12
+ordered_set.py,sha256=dbaCcs27dyN9gnMWGF5nA_BrVn6Q-NrjKYJpV9_fgBs,15130
diff --git a/setuptools/_vendor/ordered_set-3.1.1.dist-info/REQUESTED b/setuptools/_vendor/ordered_set-3.1.1.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/ordered_set-3.1.1.dist-info/REQUESTED
diff --git a/setuptools/_vendor/ordered_set-3.1.1.dist-info/WHEEL b/setuptools/_vendor/ordered_set-3.1.1.dist-info/WHEEL
new file mode 100644
index 00000000..0b18a281
--- /dev/null
+++ b/setuptools/_vendor/ordered_set-3.1.1.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.1)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/setuptools/_vendor/ordered_set-3.1.1.dist-info/top_level.txt b/setuptools/_vendor/ordered_set-3.1.1.dist-info/top_level.txt
new file mode 100644
index 00000000..1c191eef
--- /dev/null
+++ b/setuptools/_vendor/ordered_set-3.1.1.dist-info/top_level.txt
@@ -0,0 +1 @@
+ordered_set
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/INSTALLER b/setuptools/_vendor/packaging-21.2.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/LICENSE b/setuptools/_vendor/packaging-21.2.dist-info/LICENSE
new file mode 100644
index 00000000..6f62d44e
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/LICENSE
@@ -0,0 +1,3 @@
+This software is made available under the terms of *either* of the licenses
+found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made
+under the terms of *both* these licenses.
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/LICENSE.APACHE b/setuptools/_vendor/packaging-21.2.dist-info/LICENSE.APACHE
new file mode 100644
index 00000000..f433b1a5
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/LICENSE.APACHE
@@ -0,0 +1,177 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/LICENSE.BSD b/setuptools/_vendor/packaging-21.2.dist-info/LICENSE.BSD
new file mode 100644
index 00000000..42ce7b75
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/LICENSE.BSD
@@ -0,0 +1,23 @@
+Copyright (c) Donald Stufft and individual contributors.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/METADATA b/setuptools/_vendor/packaging-21.2.dist-info/METADATA
new file mode 100644
index 00000000..e8ff54d7
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/METADATA
@@ -0,0 +1,446 @@
+Metadata-Version: 2.1
+Name: packaging
+Version: 21.2
+Summary: Core utilities for Python packages
+Home-page: https://github.com/pypa/packaging
+Author: Donald Stufft and individual contributors
+Author-email: donald@stufft.io
+License: BSD-2-Clause or Apache-2.0
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Requires-Python: >=3.6
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+License-File: LICENSE.APACHE
+License-File: LICENSE.BSD
+Requires-Dist: pyparsing (<3,>=2.0.2)
+
+packaging
+=========
+
+.. start-intro
+
+Reusable core utilities for various Python Packaging
+`interoperability specifications <https://packaging.python.org/specifications/>`_.
+
+This library provides utilities that implement the interoperability
+specifications which have clearly one correct behaviour (eg: :pep:`440`)
+or benefit greatly from having a single shared implementation (eg: :pep:`425`).
+
+.. end-intro
+
+The ``packaging`` project includes the following: version handling, specifiers,
+markers, requirements, tags, utilities.
+
+Documentation
+-------------
+
+The `documentation`_ provides information and the API for the following:
+
+- Version Handling
+- Specifiers
+- Markers
+- Requirements
+- Tags
+- Utilities
+
+Installation
+------------
+
+Use ``pip`` to install these utilities::
+
+ pip install packaging
+
+Discussion
+----------
+
+If you run into bugs, you can file them in our `issue tracker`_.
+
+You can also join ``#pypa`` on Freenode to ask questions or get involved.
+
+
+.. _`documentation`: https://packaging.pypa.io/
+.. _`issue tracker`: https://github.com/pypa/packaging/issues
+
+
+Code of Conduct
+---------------
+
+Everyone interacting in the packaging project's codebases, issue trackers, chat
+rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_.
+
+.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md
+
+Contributing
+------------
+
+The ``CONTRIBUTING.rst`` file outlines how to contribute to this project as
+well as how to report a potential security issue. The documentation for this
+project also covers information about `project development`_ and `security`_.
+
+.. _`project development`: https://packaging.pypa.io/en/latest/development/
+.. _`security`: https://packaging.pypa.io/en/latest/security/
+
+Project History
+---------------
+
+Please review the ``CHANGELOG.rst`` file or the `Changelog documentation`_ for
+recent changes and project history.
+
+.. _`Changelog documentation`: https://packaging.pypa.io/en/latest/changelog/
+
+Changelog
+---------
+
+21.2 - 2021-10-29
+~~~~~~~~~~~~~~~~~
+
+* Update documentation entry for 21.1.
+
+21.1 - 2021-10-29
+~~~~~~~~~~~~~~~~~
+
+* Update pin to pyparsing to exclude 3.0.0.
+
+21.0 - 2021-07-03
+~~~~~~~~~~~~~~~~~
+
+* PEP 656: musllinux support (`#411 <https://github.com/pypa/packaging/issues/411>`__)
+* Drop support for Python 2.7, Python 3.4 and Python 3.5.
+* Replace distutils usage with sysconfig (`#396 <https://github.com/pypa/packaging/issues/396>`__)
+* Add support for zip files in ``parse_sdist_filename`` (`#429 <https://github.com/pypa/packaging/issues/429>`__)
+* Use cached ``_hash`` attribute to short-circuit tag equality comparisons (`#417 <https://github.com/pypa/packaging/issues/417>`__)
+* Specify the default value for the ``specifier`` argument to ``SpecifierSet`` (`#437 <https://github.com/pypa/packaging/issues/437>`__)
+* Proper keyword-only "warn" argument in packaging.tags (`#403 <https://github.com/pypa/packaging/issues/403>`__)
+* Correctly remove prerelease suffixes from ~= check (`#366 <https://github.com/pypa/packaging/issues/366>`__)
+* Fix type hints for ``Version.post`` and ``Version.dev`` (`#393 <https://github.com/pypa/packaging/issues/393>`__)
+* Use typing alias ``UnparsedVersion`` (`#398 <https://github.com/pypa/packaging/issues/398>`__)
+* Improve type inference for ``packaging.specifiers.filter()`` (`#430 <https://github.com/pypa/packaging/issues/430>`__)
+* Tighten the return type of ``canonicalize_version()`` (`#402 <https://github.com/pypa/packaging/issues/402>`__)
+
+20.9 - 2021-01-29
+~~~~~~~~~~~~~~~~~
+
+* Run `isort <https://pypi.org/project/isort/>`_ over the code base (`#377 <https://github.com/pypa/packaging/issues/377>`__)
+* Add support for the ``macosx_10_*_universal2`` platform tags (`#379 <https://github.com/pypa/packaging/issues/379>`__)
+* Introduce ``packaging.utils.parse_wheel_filename()`` and ``parse_sdist_filename()``
+ (`#387 <https://github.com/pypa/packaging/issues/387>`__ and `#389 <https://github.com/pypa/packaging/issues/389>`__)
+
+20.8 - 2020-12-11
+~~~~~~~~~~~~~~~~~
+
+* Revert back to setuptools for compatibility purposes for some Linux distros (`#363 <https://github.com/pypa/packaging/issues/363>`__)
+* Do not insert an underscore in wheel tags when the interpreter version number
+ is more than 2 digits (`#372 <https://github.com/pypa/packaging/issues/372>`__)
+
+20.7 - 2020-11-28
+~~~~~~~~~~~~~~~~~
+
+No unreleased changes.
+
+20.6 - 2020-11-28
+~~~~~~~~~~~~~~~~~
+
+.. note:: This release was subsequently yanked, and these changes were included in 20.7.
+
+* Fix flit configuration, to include LICENSE files (`#357 <https://github.com/pypa/packaging/issues/357>`__)
+* Make `intel` a recognized CPU architecture for the `universal` macOS platform tag (`#361 <https://github.com/pypa/packaging/issues/361>`__)
+* Add some missing type hints to `packaging.requirements` (issue:`350`)
+
+20.5 - 2020-11-27
+~~~~~~~~~~~~~~~~~
+
+* Officially support Python 3.9 (`#343 <https://github.com/pypa/packaging/issues/343>`__)
+* Deprecate the ``LegacyVersion`` and ``LegacySpecifier`` classes (`#321 <https://github.com/pypa/packaging/issues/321>`__)
+* Handle ``OSError`` on non-dynamic executables when attempting to resolve
+ the glibc version string.
+
+20.4 - 2020-05-19
+~~~~~~~~~~~~~~~~~
+
+* Canonicalize version before comparing specifiers. (`#282 <https://github.com/pypa/packaging/issues/282>`__)
+* Change type hint for ``canonicalize_name`` to return
+ ``packaging.utils.NormalizedName``.
+ This enables the use of static typing tools (like mypy) to detect mixing of
+ normalized and un-normalized names.
+
+20.3 - 2020-03-05
+~~~~~~~~~~~~~~~~~
+
+* Fix changelog for 20.2.
+
+20.2 - 2020-03-05
+~~~~~~~~~~~~~~~~~
+
+* Fix a bug that caused a 32-bit OS that runs on a 64-bit ARM CPU (e.g. ARM-v8,
+ aarch64), to report the wrong bitness.
+
+20.1 - 2020-01-24
+~~~~~~~~~~~~~~~~~~~
+
+* Fix a bug caused by reuse of an exhausted iterator. (`#257 <https://github.com/pypa/packaging/issues/257>`__)
+
+20.0 - 2020-01-06
+~~~~~~~~~~~~~~~~~
+
+* Add type hints (`#191 <https://github.com/pypa/packaging/issues/191>`__)
+
+* Add proper trove classifiers for PyPy support (`#198 <https://github.com/pypa/packaging/issues/198>`__)
+
+* Scale back depending on ``ctypes`` for manylinux support detection (`#171 <https://github.com/pypa/packaging/issues/171>`__)
+
+* Use ``sys.implementation.name`` where appropriate for ``packaging.tags`` (`#193 <https://github.com/pypa/packaging/issues/193>`__)
+
+* Expand upon the API provided by ``packaging.tags``: ``interpreter_name()``, ``mac_platforms()``, ``compatible_tags()``, ``cpython_tags()``, ``generic_tags()`` (`#187 <https://github.com/pypa/packaging/issues/187>`__)
+
+* Officially support Python 3.8 (`#232 <https://github.com/pypa/packaging/issues/232>`__)
+
+* Add ``major``, ``minor``, and ``micro`` aliases to ``packaging.version.Version`` (`#226 <https://github.com/pypa/packaging/issues/226>`__)
+
+* Properly mark ``packaging`` has being fully typed by adding a `py.typed` file (`#226 <https://github.com/pypa/packaging/issues/226>`__)
+
+19.2 - 2019-09-18
+~~~~~~~~~~~~~~~~~
+
+* Remove dependency on ``attrs`` (`#178 <https://github.com/pypa/packaging/issues/178>`__, `#179 <https://github.com/pypa/packaging/issues/179>`__)
+
+* Use appropriate fallbacks for CPython ABI tag (`#181 <https://github.com/pypa/packaging/issues/181>`__, `#185 <https://github.com/pypa/packaging/issues/185>`__)
+
+* Add manylinux2014 support (`#186 <https://github.com/pypa/packaging/issues/186>`__)
+
+* Improve ABI detection (`#181 <https://github.com/pypa/packaging/issues/181>`__)
+
+* Properly handle debug wheels for Python 3.8 (`#172 <https://github.com/pypa/packaging/issues/172>`__)
+
+* Improve detection of debug builds on Windows (`#194 <https://github.com/pypa/packaging/issues/194>`__)
+
+19.1 - 2019-07-30
+~~~~~~~~~~~~~~~~~
+
+* Add the ``packaging.tags`` module. (`#156 <https://github.com/pypa/packaging/issues/156>`__)
+
+* Correctly handle two-digit versions in ``python_version`` (`#119 <https://github.com/pypa/packaging/issues/119>`__)
+
+
+19.0 - 2019-01-20
+~~~~~~~~~~~~~~~~~
+
+* Fix string representation of PEP 508 direct URL requirements with markers.
+
+* Better handling of file URLs
+
+ This allows for using ``file:///absolute/path``, which was previously
+ prevented due to the missing ``netloc``.
+
+ This allows for all file URLs that ``urlunparse`` turns back into the
+ original URL to be valid.
+
+
+18.0 - 2018-09-26
+~~~~~~~~~~~~~~~~~
+
+* Improve error messages when invalid requirements are given. (`#129 <https://github.com/pypa/packaging/issues/129>`__)
+
+
+17.1 - 2017-02-28
+~~~~~~~~~~~~~~~~~
+
+* Fix ``utils.canonicalize_version`` when supplying non PEP 440 versions.
+
+
+17.0 - 2017-02-28
+~~~~~~~~~~~~~~~~~
+
+* Drop support for python 2.6, 3.2, and 3.3.
+
+* Define minimal pyparsing version to 2.0.2 (`#91 <https://github.com/pypa/packaging/issues/91>`__).
+
+* Add ``epoch``, ``release``, ``pre``, ``dev``, and ``post`` attributes to
+ ``Version`` and ``LegacyVersion`` (`#34 <https://github.com/pypa/packaging/issues/34>`__).
+
+* Add ``Version().is_devrelease`` and ``LegacyVersion().is_devrelease`` to
+ make it easy to determine if a release is a development release.
+
+* Add ``utils.canonicalize_version`` to canonicalize version strings or
+ ``Version`` instances (`#121 <https://github.com/pypa/packaging/issues/121>`__).
+
+
+16.8 - 2016-10-29
+~~~~~~~~~~~~~~~~~
+
+* Fix markers that utilize ``in`` so that they render correctly.
+
+* Fix an erroneous test on Python RC releases.
+
+
+16.7 - 2016-04-23
+~~~~~~~~~~~~~~~~~
+
+* Add support for the deprecated ``python_implementation`` marker which was
+ an undocumented setuptools marker in addition to the newer markers.
+
+
+16.6 - 2016-03-29
+~~~~~~~~~~~~~~~~~
+
+* Add support for the deprecated, PEP 345 environment markers in addition to
+ the newer markers.
+
+
+16.5 - 2016-02-26
+~~~~~~~~~~~~~~~~~
+
+* Fix a regression in parsing requirements with whitespaces between the comma
+ separators.
+
+
+16.4 - 2016-02-22
+~~~~~~~~~~~~~~~~~
+
+* Fix a regression in parsing requirements like ``foo (==4)``.
+
+
+16.3 - 2016-02-21
+~~~~~~~~~~~~~~~~~
+
+* Fix a bug where ``packaging.requirements:Requirement`` was overly strict when
+ matching legacy requirements.
+
+
+16.2 - 2016-02-09
+~~~~~~~~~~~~~~~~~
+
+* Add a function that implements the name canonicalization from PEP 503.
+
+
+16.1 - 2016-02-07
+~~~~~~~~~~~~~~~~~
+
+* Implement requirement specifiers from PEP 508.
+
+
+16.0 - 2016-01-19
+~~~~~~~~~~~~~~~~~
+
+* Relicense so that packaging is available under *either* the Apache License,
+ Version 2.0 or a 2 Clause BSD license.
+
+* Support installation of packaging when only distutils is available.
+
+* Fix ``==`` comparison when there is a prefix and a local version in play.
+ (`#41 <https://github.com/pypa/packaging/issues/41>`__).
+
+* Implement environment markers from PEP 508.
+
+
+15.3 - 2015-08-01
+~~~~~~~~~~~~~~~~~
+
+* Normalize post-release spellings for rev/r prefixes. `#35 <https://github.com/pypa/packaging/issues/35>`__
+
+
+15.2 - 2015-05-13
+~~~~~~~~~~~~~~~~~
+
+* Fix an error where the arbitrary specifier (``===``) was not correctly
+ allowing pre-releases when it was being used.
+
+* Expose the specifier and version parts through properties on the
+ ``Specifier`` classes.
+
+* Allow iterating over the ``SpecifierSet`` to get access to all of the
+ ``Specifier`` instances.
+
+* Allow testing if a version is contained within a specifier via the ``in``
+ operator.
+
+
+15.1 - 2015-04-13
+~~~~~~~~~~~~~~~~~
+
+* Fix a logic error that was causing inconsistent answers about whether or not
+ a pre-release was contained within a ``SpecifierSet`` or not.
+
+
+15.0 - 2015-01-02
+~~~~~~~~~~~~~~~~~
+
+* Add ``Version().is_postrelease`` and ``LegacyVersion().is_postrelease`` to
+ make it easy to determine if a release is a post release.
+
+* Add ``Version().base_version`` and ``LegacyVersion().base_version`` to make
+ it easy to get the public version without any pre or post release markers.
+
+* Support the update to PEP 440 which removed the implied ``!=V.*`` when using
+ either ``>V`` or ``<V`` and which instead special cased the handling of
+ pre-releases, post-releases, and local versions when using ``>V`` or ``<V``.
+
+
+14.5 - 2014-12-17
+~~~~~~~~~~~~~~~~~
+
+* Normalize release candidates as ``rc`` instead of ``c``.
+
+* Expose the ``VERSION_PATTERN`` constant, a regular expression matching
+ a valid version.
+
+
+14.4 - 2014-12-15
+~~~~~~~~~~~~~~~~~
+
+* Ensure that versions are normalized before comparison when used in a
+ specifier with a less than (``<``) or greater than (``>``) operator.
+
+
+14.3 - 2014-11-19
+~~~~~~~~~~~~~~~~~
+
+* **BACKWARDS INCOMPATIBLE** Refactor specifier support so that it can sanely
+ handle legacy specifiers as well as PEP 440 specifiers.
+
+* **BACKWARDS INCOMPATIBLE** Move the specifier support out of
+ ``packaging.version`` into ``packaging.specifiers``.
+
+
+14.2 - 2014-09-10
+~~~~~~~~~~~~~~~~~
+
+* Add prerelease support to ``Specifier``.
+* Remove the ability to do ``item in Specifier()`` and replace it with
+ ``Specifier().contains(item)`` in order to allow flags that signal if a
+ prerelease should be accepted or not.
+* Add a method ``Specifier().filter()`` which will take an iterable and returns
+ an iterable with items that do not match the specifier filtered out.
+
+
+14.1 - 2014-09-08
+~~~~~~~~~~~~~~~~~
+
+* Allow ``LegacyVersion`` and ``Version`` to be sorted together.
+* Add ``packaging.version.parse()`` to enable easily parsing a version string
+ as either a ``Version`` or a ``LegacyVersion`` depending on it's PEP 440
+ validity.
+
+
+14.0 - 2014-09-05
+~~~~~~~~~~~~~~~~~
+
+* Initial release.
+
+
+.. _`master`: https://github.com/pypa/packaging/
+
+
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/RECORD b/setuptools/_vendor/packaging-21.2.dist-info/RECORD
new file mode 100644
index 00000000..ed2291ac
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/RECORD
@@ -0,0 +1,32 @@
+packaging-21.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+packaging-21.2.dist-info/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197
+packaging-21.2.dist-info/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174
+packaging-21.2.dist-info/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344
+packaging-21.2.dist-info/METADATA,sha256=N4A8uSYrQwV9byem7YuI9OtVkbqiNzFlDhcDVT-suAo,14754
+packaging-21.2.dist-info/RECORD,,
+packaging-21.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+packaging-21.2.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
+packaging-21.2.dist-info/top_level.txt,sha256=zFdHrhWnPslzsiP455HutQsqPB6v0KCtNUMtUtrefDw,10
+packaging/__about__.py,sha256=IIRHpOsJlJSgkjq1UoeBoMTqhvNp3gN9FyMb5Kf8El4,661
+packaging/__init__.py,sha256=b9Kk5MF7KxhhLgcDmiUWukN-LatWFxPdNug0joPhHSk,497
+packaging/__pycache__/__about__.cpython-310.pyc,,
+packaging/__pycache__/__init__.cpython-310.pyc,,
+packaging/__pycache__/_manylinux.cpython-310.pyc,,
+packaging/__pycache__/_musllinux.cpython-310.pyc,,
+packaging/__pycache__/_structures.cpython-310.pyc,,
+packaging/__pycache__/markers.cpython-310.pyc,,
+packaging/__pycache__/requirements.cpython-310.pyc,,
+packaging/__pycache__/specifiers.cpython-310.pyc,,
+packaging/__pycache__/tags.cpython-310.pyc,,
+packaging/__pycache__/utils.cpython-310.pyc,,
+packaging/__pycache__/version.cpython-310.pyc,,
+packaging/_manylinux.py,sha256=XcbiXB-qcjv3bcohp6N98TMpOP4_j3m-iOA8ptK2GWY,11488
+packaging/_musllinux.py,sha256=z5yeG1ygOPx4uUyLdqj-p8Dk5UBb5H_b0NIjW9yo8oA,4378
+packaging/_structures.py,sha256=TMiAgFbdUOPmIfDIfiHc3KFhSJ8kMjof2QS5I-2NyQ8,1629
+packaging/markers.py,sha256=Fygi3_eZnjQ-3VJizW5AhI5wvo0Hb6RMk4DidsKpOC0,8475
+packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+packaging/requirements.py,sha256=rjaGRCMepZS1mlYMjJ5Qh6rfq3gtsCRQUQmftGZ_bu8,4664
+packaging/specifiers.py,sha256=MZ-fYcNL3u7pNrt-6g2EQO7AbRXkjc-SPEYwXMQbLmc,30964
+packaging/tags.py,sha256=vGybAUQYlPKMcukzX_2e65fmafnFFuMbD25naYTEwtc,15710
+packaging/utils.py,sha256=dJjeat3BS-TYn1RrUFVwufUMasbtzLfYRoy_HXENeFQ,4200
+packaging/version.py,sha256=_fLRNrFrxYcHVfyo8vk9j8s6JM8N_xsSxVFr6RJyco8,14665
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/REQUESTED b/setuptools/_vendor/packaging-21.2.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/REQUESTED
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/WHEEL b/setuptools/_vendor/packaging-21.2.dist-info/WHEEL
new file mode 100644
index 00000000..5bad85fd
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.0)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/setuptools/_vendor/packaging-21.2.dist-info/top_level.txt b/setuptools/_vendor/packaging-21.2.dist-info/top_level.txt
new file mode 100644
index 00000000..748809f7
--- /dev/null
+++ b/setuptools/_vendor/packaging-21.2.dist-info/top_level.txt
@@ -0,0 +1 @@
+packaging
diff --git a/setuptools/_vendor/packaging/__about__.py b/setuptools/_vendor/packaging/__about__.py
index 4d998578..c359122f 100644
--- a/setuptools/_vendor/packaging/__about__.py
+++ b/setuptools/_vendor/packaging/__about__.py
@@ -1,7 +1,6 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
__all__ = [
"__title__",
@@ -18,10 +17,10 @@ __title__ = "packaging"
__summary__ = "Core utilities for Python packages"
__uri__ = "https://github.com/pypa/packaging"
-__version__ = "20.4"
+__version__ = "21.2"
__author__ = "Donald Stufft and individual contributors"
__email__ = "donald@stufft.io"
__license__ = "BSD-2-Clause or Apache-2.0"
-__copyright__ = "Copyright 2014-2019 %s" % __author__
+__copyright__ = "2014-2019 %s" % __author__
diff --git a/setuptools/_vendor/packaging/__init__.py b/setuptools/_vendor/packaging/__init__.py
index a0cf67df..3c50c5dc 100644
--- a/setuptools/_vendor/packaging/__init__.py
+++ b/setuptools/_vendor/packaging/__init__.py
@@ -1,7 +1,6 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
from .__about__ import (
__author__,
diff --git a/setuptools/_vendor/packaging/_compat.py b/setuptools/_vendor/packaging/_compat.py
deleted file mode 100644
index e54bd4ed..00000000
--- a/setuptools/_vendor/packaging/_compat.py
+++ /dev/null
@@ -1,38 +0,0 @@
-# This file is dual licensed under the terms of the Apache License, Version
-# 2.0, and the BSD License. See the LICENSE file in the root of this repository
-# for complete details.
-from __future__ import absolute_import, division, print_function
-
-import sys
-
-from ._typing import TYPE_CHECKING
-
-if TYPE_CHECKING: # pragma: no cover
- from typing import Any, Dict, Tuple, Type
-
-
-PY2 = sys.version_info[0] == 2
-PY3 = sys.version_info[0] == 3
-
-# flake8: noqa
-
-if PY3:
- string_types = (str,)
-else:
- string_types = (basestring,)
-
-
-def with_metaclass(meta, *bases):
- # type: (Type[Any], Tuple[Type[Any], ...]) -> Any
- """
- Create a base class with a metaclass.
- """
- # This requires a bit of explanation: the basic idea is to make a dummy
- # metaclass for one level of class instantiation that replaces itself with
- # the actual metaclass.
- class metaclass(meta): # type: ignore
- def __new__(cls, name, this_bases, d):
- # type: (Type[Any], str, Tuple[Any], Dict[Any, Any]) -> Any
- return meta(name, bases, d)
-
- return type.__new__(metaclass, "temporary_class", (), {})
diff --git a/setuptools/_vendor/packaging/_manylinux.py b/setuptools/_vendor/packaging/_manylinux.py
new file mode 100644
index 00000000..4c379aa6
--- /dev/null
+++ b/setuptools/_vendor/packaging/_manylinux.py
@@ -0,0 +1,301 @@
+import collections
+import functools
+import os
+import re
+import struct
+import sys
+import warnings
+from typing import IO, Dict, Iterator, NamedTuple, Optional, Tuple
+
+
+# Python does not provide platform information at sufficient granularity to
+# identify the architecture of the running executable in some cases, so we
+# determine it dynamically by reading the information from the running
+# process. This only applies on Linux, which uses the ELF format.
+class _ELFFileHeader:
+ # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
+ class _InvalidELFFileHeader(ValueError):
+ """
+ An invalid ELF file header was found.
+ """
+
+ ELF_MAGIC_NUMBER = 0x7F454C46
+ ELFCLASS32 = 1
+ ELFCLASS64 = 2
+ ELFDATA2LSB = 1
+ ELFDATA2MSB = 2
+ EM_386 = 3
+ EM_S390 = 22
+ EM_ARM = 40
+ EM_X86_64 = 62
+ EF_ARM_ABIMASK = 0xFF000000
+ EF_ARM_ABI_VER5 = 0x05000000
+ EF_ARM_ABI_FLOAT_HARD = 0x00000400
+
+ def __init__(self, file: IO[bytes]) -> None:
+ def unpack(fmt: str) -> int:
+ try:
+ data = file.read(struct.calcsize(fmt))
+ result: Tuple[int, ...] = struct.unpack(fmt, data)
+ except struct.error:
+ raise _ELFFileHeader._InvalidELFFileHeader()
+ return result[0]
+
+ self.e_ident_magic = unpack(">I")
+ if self.e_ident_magic != self.ELF_MAGIC_NUMBER:
+ raise _ELFFileHeader._InvalidELFFileHeader()
+ self.e_ident_class = unpack("B")
+ if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}:
+ raise _ELFFileHeader._InvalidELFFileHeader()
+ self.e_ident_data = unpack("B")
+ if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}:
+ raise _ELFFileHeader._InvalidELFFileHeader()
+ self.e_ident_version = unpack("B")
+ self.e_ident_osabi = unpack("B")
+ self.e_ident_abiversion = unpack("B")
+ self.e_ident_pad = file.read(7)
+ format_h = "<H" if self.e_ident_data == self.ELFDATA2LSB else ">H"
+ format_i = "<I" if self.e_ident_data == self.ELFDATA2LSB else ">I"
+ format_q = "<Q" if self.e_ident_data == self.ELFDATA2LSB else ">Q"
+ format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q
+ self.e_type = unpack(format_h)
+ self.e_machine = unpack(format_h)
+ self.e_version = unpack(format_i)
+ self.e_entry = unpack(format_p)
+ self.e_phoff = unpack(format_p)
+ self.e_shoff = unpack(format_p)
+ self.e_flags = unpack(format_i)
+ self.e_ehsize = unpack(format_h)
+ self.e_phentsize = unpack(format_h)
+ self.e_phnum = unpack(format_h)
+ self.e_shentsize = unpack(format_h)
+ self.e_shnum = unpack(format_h)
+ self.e_shstrndx = unpack(format_h)
+
+
+def _get_elf_header() -> Optional[_ELFFileHeader]:
+ try:
+ with open(sys.executable, "rb") as f:
+ elf_header = _ELFFileHeader(f)
+ except (OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader):
+ return None
+ return elf_header
+
+
+def _is_linux_armhf() -> bool:
+ # hard-float ABI can be detected from the ELF header of the running
+ # process
+ # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf
+ elf_header = _get_elf_header()
+ if elf_header is None:
+ return False
+ result = elf_header.e_ident_class == elf_header.ELFCLASS32
+ result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB
+ result &= elf_header.e_machine == elf_header.EM_ARM
+ result &= (
+ elf_header.e_flags & elf_header.EF_ARM_ABIMASK
+ ) == elf_header.EF_ARM_ABI_VER5
+ result &= (
+ elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD
+ ) == elf_header.EF_ARM_ABI_FLOAT_HARD
+ return result
+
+
+def _is_linux_i686() -> bool:
+ elf_header = _get_elf_header()
+ if elf_header is None:
+ return False
+ result = elf_header.e_ident_class == elf_header.ELFCLASS32
+ result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB
+ result &= elf_header.e_machine == elf_header.EM_386
+ return result
+
+
+def _have_compatible_abi(arch: str) -> bool:
+ if arch == "armv7l":
+ return _is_linux_armhf()
+ if arch == "i686":
+ return _is_linux_i686()
+ return arch in {"x86_64", "aarch64", "ppc64", "ppc64le", "s390x"}
+
+
+# If glibc ever changes its major version, we need to know what the last
+# minor version was, so we can build the complete list of all versions.
+# For now, guess what the highest minor version might be, assume it will
+# be 50 for testing. Once this actually happens, update the dictionary
+# with the actual value.
+_LAST_GLIBC_MINOR: Dict[int, int] = collections.defaultdict(lambda: 50)
+
+
+class _GLibCVersion(NamedTuple):
+ major: int
+ minor: int
+
+
+def _glibc_version_string_confstr() -> Optional[str]:
+ """
+ Primary implementation of glibc_version_string using os.confstr.
+ """
+ # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely
+ # to be broken or missing. This strategy is used in the standard library
+ # platform module.
+ # https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183
+ try:
+ # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17".
+ version_string = os.confstr("CS_GNU_LIBC_VERSION")
+ assert version_string is not None
+ _, version = version_string.split()
+ except (AssertionError, AttributeError, OSError, ValueError):
+ # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)...
+ return None
+ return version
+
+
+def _glibc_version_string_ctypes() -> Optional[str]:
+ """
+ Fallback implementation of glibc_version_string using ctypes.
+ """
+ try:
+ import ctypes
+ except ImportError:
+ return None
+
+ # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen
+ # manpage says, "If filename is NULL, then the returned handle is for the
+ # main program". This way we can let the linker do the work to figure out
+ # which libc our process is actually using.
+ #
+ # We must also handle the special case where the executable is not a
+ # dynamically linked executable. This can occur when using musl libc,
+ # for example. In this situation, dlopen() will error, leading to an
+ # OSError. Interestingly, at least in the case of musl, there is no
+ # errno set on the OSError. The single string argument used to construct
+ # OSError comes from libc itself and is therefore not portable to
+ # hard code here. In any case, failure to call dlopen() means we
+ # can proceed, so we bail on our attempt.
+ try:
+ process_namespace = ctypes.CDLL(None)
+ except OSError:
+ return None
+
+ try:
+ gnu_get_libc_version = process_namespace.gnu_get_libc_version
+ except AttributeError:
+ # Symbol doesn't exist -> therefore, we are not linked to
+ # glibc.
+ return None
+
+ # Call gnu_get_libc_version, which returns a string like "2.5"
+ gnu_get_libc_version.restype = ctypes.c_char_p
+ version_str: str = gnu_get_libc_version()
+ # py2 / py3 compatibility:
+ if not isinstance(version_str, str):
+ version_str = version_str.decode("ascii")
+
+ return version_str
+
+
+def _glibc_version_string() -> Optional[str]:
+ """Returns glibc version string, or None if not using glibc."""
+ return _glibc_version_string_confstr() or _glibc_version_string_ctypes()
+
+
+def _parse_glibc_version(version_str: str) -> Tuple[int, int]:
+ """Parse glibc version.
+
+ We use a regexp instead of str.split because we want to discard any
+ random junk that might come after the minor version -- this might happen
+ in patched/forked versions of glibc (e.g. Linaro's version of glibc
+ uses version strings like "2.20-2014.11"). See gh-3588.
+ """
+ m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str)
+ if not m:
+ warnings.warn(
+ "Expected glibc version with 2 components major.minor,"
+ " got: %s" % version_str,
+ RuntimeWarning,
+ )
+ return -1, -1
+ return int(m.group("major")), int(m.group("minor"))
+
+
+@functools.lru_cache()
+def _get_glibc_version() -> Tuple[int, int]:
+ version_str = _glibc_version_string()
+ if version_str is None:
+ return (-1, -1)
+ return _parse_glibc_version(version_str)
+
+
+# From PEP 513, PEP 600
+def _is_compatible(name: str, arch: str, version: _GLibCVersion) -> bool:
+ sys_glibc = _get_glibc_version()
+ if sys_glibc < version:
+ return False
+ # Check for presence of _manylinux module.
+ try:
+ import _manylinux # noqa
+ except ImportError:
+ return True
+ if hasattr(_manylinux, "manylinux_compatible"):
+ result = _manylinux.manylinux_compatible(version[0], version[1], arch)
+ if result is not None:
+ return bool(result)
+ return True
+ if version == _GLibCVersion(2, 5):
+ if hasattr(_manylinux, "manylinux1_compatible"):
+ return bool(_manylinux.manylinux1_compatible)
+ if version == _GLibCVersion(2, 12):
+ if hasattr(_manylinux, "manylinux2010_compatible"):
+ return bool(_manylinux.manylinux2010_compatible)
+ if version == _GLibCVersion(2, 17):
+ if hasattr(_manylinux, "manylinux2014_compatible"):
+ return bool(_manylinux.manylinux2014_compatible)
+ return True
+
+
+_LEGACY_MANYLINUX_MAP = {
+ # CentOS 7 w/ glibc 2.17 (PEP 599)
+ (2, 17): "manylinux2014",
+ # CentOS 6 w/ glibc 2.12 (PEP 571)
+ (2, 12): "manylinux2010",
+ # CentOS 5 w/ glibc 2.5 (PEP 513)
+ (2, 5): "manylinux1",
+}
+
+
+def platform_tags(linux: str, arch: str) -> Iterator[str]:
+ if not _have_compatible_abi(arch):
+ return
+ # Oldest glibc to be supported regardless of architecture is (2, 17).
+ too_old_glibc2 = _GLibCVersion(2, 16)
+ if arch in {"x86_64", "i686"}:
+ # On x86/i686 also oldest glibc to be supported is (2, 5).
+ too_old_glibc2 = _GLibCVersion(2, 4)
+ current_glibc = _GLibCVersion(*_get_glibc_version())
+ glibc_max_list = [current_glibc]
+ # We can assume compatibility across glibc major versions.
+ # https://sourceware.org/bugzilla/show_bug.cgi?id=24636
+ #
+ # Build a list of maximum glibc versions so that we can
+ # output the canonical list of all glibc from current_glibc
+ # down to too_old_glibc2, including all intermediary versions.
+ for glibc_major in range(current_glibc.major - 1, 1, -1):
+ glibc_minor = _LAST_GLIBC_MINOR[glibc_major]
+ glibc_max_list.append(_GLibCVersion(glibc_major, glibc_minor))
+ for glibc_max in glibc_max_list:
+ if glibc_max.major == too_old_glibc2.major:
+ min_minor = too_old_glibc2.minor
+ else:
+ # For other glibc major versions oldest supported is (x, 0).
+ min_minor = -1
+ for glibc_minor in range(glibc_max.minor, min_minor, -1):
+ glibc_version = _GLibCVersion(glibc_max.major, glibc_minor)
+ tag = "manylinux_{}_{}".format(*glibc_version)
+ if _is_compatible(tag, arch, glibc_version):
+ yield linux.replace("linux", tag)
+ # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags.
+ if glibc_version in _LEGACY_MANYLINUX_MAP:
+ legacy_tag = _LEGACY_MANYLINUX_MAP[glibc_version]
+ if _is_compatible(legacy_tag, arch, glibc_version):
+ yield linux.replace("linux", legacy_tag)
diff --git a/setuptools/_vendor/packaging/_musllinux.py b/setuptools/_vendor/packaging/_musllinux.py
new file mode 100644
index 00000000..85450faf
--- /dev/null
+++ b/setuptools/_vendor/packaging/_musllinux.py
@@ -0,0 +1,136 @@
+"""PEP 656 support.
+
+This module implements logic to detect if the currently running Python is
+linked against musl, and what musl version is used.
+"""
+
+import contextlib
+import functools
+import operator
+import os
+import re
+import struct
+import subprocess
+import sys
+from typing import IO, Iterator, NamedTuple, Optional, Tuple
+
+
+def _read_unpacked(f: IO[bytes], fmt: str) -> Tuple[int, ...]:
+ return struct.unpack(fmt, f.read(struct.calcsize(fmt)))
+
+
+def _parse_ld_musl_from_elf(f: IO[bytes]) -> Optional[str]:
+ """Detect musl libc location by parsing the Python executable.
+
+ Based on: https://gist.github.com/lyssdod/f51579ae8d93c8657a5564aefc2ffbca
+ ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html
+ """
+ f.seek(0)
+ try:
+ ident = _read_unpacked(f, "16B")
+ except struct.error:
+ return None
+ if ident[:4] != tuple(b"\x7fELF"): # Invalid magic, not ELF.
+ return None
+ f.seek(struct.calcsize("HHI"), 1) # Skip file type, machine, and version.
+
+ try:
+ # e_fmt: Format for program header.
+ # p_fmt: Format for section header.
+ # p_idx: Indexes to find p_type, p_offset, and p_filesz.
+ e_fmt, p_fmt, p_idx = {
+ 1: ("IIIIHHH", "IIIIIIII", (0, 1, 4)), # 32-bit.
+ 2: ("QQQIHHH", "IIQQQQQQ", (0, 2, 5)), # 64-bit.
+ }[ident[4]]
+ except KeyError:
+ return None
+ else:
+ p_get = operator.itemgetter(*p_idx)
+
+ # Find the interpreter section and return its content.
+ try:
+ _, e_phoff, _, _, _, e_phentsize, e_phnum = _read_unpacked(f, e_fmt)
+ except struct.error:
+ return None
+ for i in range(e_phnum + 1):
+ f.seek(e_phoff + e_phentsize * i)
+ try:
+ p_type, p_offset, p_filesz = p_get(_read_unpacked(f, p_fmt))
+ except struct.error:
+ return None
+ if p_type != 3: # Not PT_INTERP.
+ continue
+ f.seek(p_offset)
+ interpreter = os.fsdecode(f.read(p_filesz)).strip("\0")
+ if "musl" not in interpreter:
+ return None
+ return interpreter
+ return None
+
+
+class _MuslVersion(NamedTuple):
+ major: int
+ minor: int
+
+
+def _parse_musl_version(output: str) -> Optional[_MuslVersion]:
+ lines = [n for n in (n.strip() for n in output.splitlines()) if n]
+ if len(lines) < 2 or lines[0][:4] != "musl":
+ return None
+ m = re.match(r"Version (\d+)\.(\d+)", lines[1])
+ if not m:
+ return None
+ return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2)))
+
+
+@functools.lru_cache()
+def _get_musl_version(executable: str) -> Optional[_MuslVersion]:
+ """Detect currently-running musl runtime version.
+
+ This is done by checking the specified executable's dynamic linking
+ information, and invoking the loader to parse its output for a version
+ string. If the loader is musl, the output would be something like::
+
+ musl libc (x86_64)
+ Version 1.2.2
+ Dynamic Program Loader
+ """
+ with contextlib.ExitStack() as stack:
+ try:
+ f = stack.enter_context(open(executable, "rb"))
+ except IOError:
+ return None
+ ld = _parse_ld_musl_from_elf(f)
+ if not ld:
+ return None
+ proc = subprocess.run([ld], stderr=subprocess.PIPE, universal_newlines=True)
+ return _parse_musl_version(proc.stderr)
+
+
+def platform_tags(arch: str) -> Iterator[str]:
+ """Generate musllinux tags compatible to the current platform.
+
+ :param arch: Should be the part of platform tag after the ``linux_``
+ prefix, e.g. ``x86_64``. The ``linux_`` prefix is assumed as a
+ prerequisite for the current platform to be musllinux-compatible.
+
+ :returns: An iterator of compatible musllinux tags.
+ """
+ sys_musl = _get_musl_version(sys.executable)
+ if sys_musl is None: # Python not dynamically linked against musl.
+ return
+ for minor in range(sys_musl.minor, -1, -1):
+ yield f"musllinux_{sys_musl.major}_{minor}_{arch}"
+
+
+if __name__ == "__main__": # pragma: no cover
+ import sysconfig
+
+ plat = sysconfig.get_platform()
+ assert plat.startswith("linux-"), "not linux"
+
+ print("plat:", plat)
+ print("musl:", _get_musl_version(sys.executable))
+ print("tags:", end=" ")
+ for t in platform_tags(re.sub(r"[.-]", "_", plat.split("-", 1)[-1])):
+ print(t, end="\n ")
diff --git a/setuptools/_vendor/packaging/_structures.py b/setuptools/_vendor/packaging/_structures.py
index 800d5c55..95154975 100644
--- a/setuptools/_vendor/packaging/_structures.py
+++ b/setuptools/_vendor/packaging/_structures.py
@@ -1,85 +1,66 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
-class InfinityType(object):
- def __repr__(self):
- # type: () -> str
+class InfinityType:
+ def __repr__(self) -> str:
return "Infinity"
- def __hash__(self):
- # type: () -> int
+ def __hash__(self) -> int:
return hash(repr(self))
- def __lt__(self, other):
- # type: (object) -> bool
+ def __lt__(self, other: object) -> bool:
return False
- def __le__(self, other):
- # type: (object) -> bool
+ def __le__(self, other: object) -> bool:
return False
- def __eq__(self, other):
- # type: (object) -> bool
+ def __eq__(self, other: object) -> bool:
return isinstance(other, self.__class__)
- def __ne__(self, other):
- # type: (object) -> bool
+ def __ne__(self, other: object) -> bool:
return not isinstance(other, self.__class__)
- def __gt__(self, other):
- # type: (object) -> bool
+ def __gt__(self, other: object) -> bool:
return True
- def __ge__(self, other):
- # type: (object) -> bool
+ def __ge__(self, other: object) -> bool:
return True
- def __neg__(self):
- # type: (object) -> NegativeInfinityType
+ def __neg__(self: object) -> "NegativeInfinityType":
return NegativeInfinity
Infinity = InfinityType()
-class NegativeInfinityType(object):
- def __repr__(self):
- # type: () -> str
+class NegativeInfinityType:
+ def __repr__(self) -> str:
return "-Infinity"
- def __hash__(self):
- # type: () -> int
+ def __hash__(self) -> int:
return hash(repr(self))
- def __lt__(self, other):
- # type: (object) -> bool
+ def __lt__(self, other: object) -> bool:
return True
- def __le__(self, other):
- # type: (object) -> bool
+ def __le__(self, other: object) -> bool:
return True
- def __eq__(self, other):
- # type: (object) -> bool
+ def __eq__(self, other: object) -> bool:
return isinstance(other, self.__class__)
- def __ne__(self, other):
- # type: (object) -> bool
+ def __ne__(self, other: object) -> bool:
return not isinstance(other, self.__class__)
- def __gt__(self, other):
- # type: (object) -> bool
+ def __gt__(self, other: object) -> bool:
return False
- def __ge__(self, other):
- # type: (object) -> bool
+ def __ge__(self, other: object) -> bool:
return False
- def __neg__(self):
- # type: (object) -> InfinityType
+ def __neg__(self: object) -> InfinityType:
return Infinity
diff --git a/setuptools/_vendor/packaging/_typing.py b/setuptools/_vendor/packaging/_typing.py
deleted file mode 100644
index 77a8b918..00000000
--- a/setuptools/_vendor/packaging/_typing.py
+++ /dev/null
@@ -1,48 +0,0 @@
-"""For neatly implementing static typing in packaging.
-
-`mypy` - the static type analysis tool we use - uses the `typing` module, which
-provides core functionality fundamental to mypy's functioning.
-
-Generally, `typing` would be imported at runtime and used in that fashion -
-it acts as a no-op at runtime and does not have any run-time overhead by
-design.
-
-As it turns out, `typing` is not vendorable - it uses separate sources for
-Python 2/Python 3. Thus, this codebase can not expect it to be present.
-To work around this, mypy allows the typing import to be behind a False-y
-optional to prevent it from running at runtime and type-comments can be used
-to remove the need for the types to be accessible directly during runtime.
-
-This module provides the False-y guard in a nicely named fashion so that a
-curious maintainer can reach here to read this.
-
-In packaging, all static-typing related imports should be guarded as follows:
-
- from packaging._typing import TYPE_CHECKING
-
- if TYPE_CHECKING:
- from typing import ...
-
-Ref: https://github.com/python/mypy/issues/3216
-"""
-
-__all__ = ["TYPE_CHECKING", "cast"]
-
-# The TYPE_CHECKING constant defined by the typing module is False at runtime
-# but True while type checking.
-if False: # pragma: no cover
- from typing import TYPE_CHECKING
-else:
- TYPE_CHECKING = False
-
-# typing's cast syntax requires calling typing.cast at runtime, but we don't
-# want to import typing at runtime. Here, we inform the type checkers that
-# we're importing `typing.cast` as `cast` and re-implement typing.cast's
-# runtime behavior in a block that is ignored by type checkers.
-if TYPE_CHECKING: # pragma: no cover
- # not executed at runtime
- from typing import cast
-else:
- # executed at runtime
- def cast(type_, value): # noqa
- return value
diff --git a/setuptools/_vendor/packaging/markers.py b/setuptools/_vendor/packaging/markers.py
index 03fbdfcc..eb0541b8 100644
--- a/setuptools/_vendor/packaging/markers.py
+++ b/setuptools/_vendor/packaging/markers.py
@@ -1,26 +1,26 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
import operator
import os
import platform
import sys
+from typing import Any, Callable, Dict, List, Optional, Tuple, Union
+
+from setuptools.extern.pyparsing import ( # noqa: N817
+ Forward,
+ Group,
+ Literal as L,
+ ParseException,
+ ParseResults,
+ QuotedString,
+ ZeroOrMore,
+ stringEnd,
+ stringStart,
+)
-from setuptools.extern.pyparsing import ParseException, ParseResults, stringStart, stringEnd
-from setuptools.extern.pyparsing import ZeroOrMore, Group, Forward, QuotedString
-from setuptools.extern.pyparsing import Literal as L # noqa
-
-from ._compat import string_types
-from ._typing import TYPE_CHECKING
-from .specifiers import Specifier, InvalidSpecifier
-
-if TYPE_CHECKING: # pragma: no cover
- from typing import Any, Callable, Dict, List, Optional, Tuple, Union
-
- Operator = Callable[[str, str], bool]
-
+from .specifiers import InvalidSpecifier, Specifier
__all__ = [
"InvalidMarker",
@@ -30,6 +30,8 @@ __all__ = [
"default_environment",
]
+Operator = Callable[[str, str], bool]
+
class InvalidMarker(ValueError):
"""
@@ -50,39 +52,32 @@ class UndefinedEnvironmentName(ValueError):
"""
-class Node(object):
- def __init__(self, value):
- # type: (Any) -> None
+class Node:
+ def __init__(self, value: Any) -> None:
self.value = value
- def __str__(self):
- # type: () -> str
+ def __str__(self) -> str:
return str(self.value)
- def __repr__(self):
- # type: () -> str
- return "<{0}({1!r})>".format(self.__class__.__name__, str(self))
+ def __repr__(self) -> str:
+ return f"<{self.__class__.__name__}('{self}')>"
- def serialize(self):
- # type: () -> str
+ def serialize(self) -> str:
raise NotImplementedError
class Variable(Node):
- def serialize(self):
- # type: () -> str
+ def serialize(self) -> str:
return str(self)
class Value(Node):
- def serialize(self):
- # type: () -> str
- return '"{0}"'.format(self)
+ def serialize(self) -> str:
+ return f'"{self}"'
class Op(Node):
- def serialize(self):
- # type: () -> str
+ def serialize(self) -> str:
return str(self)
@@ -143,18 +138,18 @@ MARKER_EXPR << MARKER_ATOM + ZeroOrMore(BOOLOP + MARKER_EXPR)
MARKER = stringStart + MARKER_EXPR + stringEnd
-def _coerce_parse_result(results):
- # type: (Union[ParseResults, List[Any]]) -> List[Any]
+def _coerce_parse_result(results: Union[ParseResults, List[Any]]) -> List[Any]:
if isinstance(results, ParseResults):
return [_coerce_parse_result(i) for i in results]
else:
return results
-def _format_marker(marker, first=True):
- # type: (Union[List[str], Tuple[Node, ...], str], Optional[bool]) -> str
+def _format_marker(
+ marker: Union[List[str], Tuple[Node, ...], str], first: Optional[bool] = True
+) -> str:
- assert isinstance(marker, (list, tuple, string_types))
+ assert isinstance(marker, (list, tuple, str))
# Sometimes we have a structure like [[...]] which is a single item list
# where the single item is itself it's own list. In that case we want skip
@@ -179,7 +174,7 @@ def _format_marker(marker, first=True):
return marker
-_operators = {
+_operators: Dict[str, Operator] = {
"in": lambda lhs, rhs: lhs in rhs,
"not in": lambda lhs, rhs: lhs not in rhs,
"<": operator.lt,
@@ -188,11 +183,10 @@ _operators = {
"!=": operator.ne,
">=": operator.ge,
">": operator.gt,
-} # type: Dict[str, Operator]
+}
-def _eval_op(lhs, op, rhs):
- # type: (str, Op, str) -> bool
+def _eval_op(lhs: str, op: Op, rhs: str) -> bool:
try:
spec = Specifier("".join([op.serialize(), rhs]))
except InvalidSpecifier:
@@ -200,40 +194,36 @@ def _eval_op(lhs, op, rhs):
else:
return spec.contains(lhs)
- oper = _operators.get(op.serialize()) # type: Optional[Operator]
+ oper: Optional[Operator] = _operators.get(op.serialize())
if oper is None:
- raise UndefinedComparison(
- "Undefined {0!r} on {1!r} and {2!r}.".format(op, lhs, rhs)
- )
+ raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.")
return oper(lhs, rhs)
-class Undefined(object):
+class Undefined:
pass
_undefined = Undefined()
-def _get_env(environment, name):
- # type: (Dict[str, str], str) -> str
- value = environment.get(name, _undefined) # type: Union[str, Undefined]
+def _get_env(environment: Dict[str, str], name: str) -> str:
+ value: Union[str, Undefined] = environment.get(name, _undefined)
if isinstance(value, Undefined):
raise UndefinedEnvironmentName(
- "{0!r} does not exist in evaluation environment.".format(name)
+ f"{name!r} does not exist in evaluation environment."
)
return value
-def _evaluate_markers(markers, environment):
- # type: (List[Any], Dict[str, str]) -> bool
- groups = [[]] # type: List[List[bool]]
+def _evaluate_markers(markers: List[Any], environment: Dict[str, str]) -> bool:
+ groups: List[List[bool]] = [[]]
for marker in markers:
- assert isinstance(marker, (list, tuple, string_types))
+ assert isinstance(marker, (list, tuple, str))
if isinstance(marker, list):
groups[-1].append(_evaluate_markers(marker, environment))
@@ -256,8 +246,7 @@ def _evaluate_markers(markers, environment):
return any(all(item) for item in groups)
-def format_full_version(info):
- # type: (sys._version_info) -> str
+def format_full_version(info: "sys._version_info") -> str:
version = "{0.major}.{0.minor}.{0.micro}".format(info)
kind = info.releaselevel
if kind != "final":
@@ -265,18 +254,9 @@ def format_full_version(info):
return version
-def default_environment():
- # type: () -> Dict[str, str]
- if hasattr(sys, "implementation"):
- # Ignoring the `sys.implementation` reference for type checking due to
- # mypy not liking that the attribute doesn't exist in Python 2.7 when
- # run with the `--py27` flag.
- iver = format_full_version(sys.implementation.version) # type: ignore
- implementation_name = sys.implementation.name # type: ignore
- else:
- iver = "0"
- implementation_name = ""
-
+def default_environment() -> Dict[str, str]:
+ iver = format_full_version(sys.implementation.version)
+ implementation_name = sys.implementation.name
return {
"implementation_name": implementation_name,
"implementation_version": iver,
@@ -292,27 +272,23 @@ def default_environment():
}
-class Marker(object):
- def __init__(self, marker):
- # type: (str) -> None
+class Marker:
+ def __init__(self, marker: str) -> None:
try:
self._markers = _coerce_parse_result(MARKER.parseString(marker))
except ParseException as e:
- err_str = "Invalid marker: {0!r}, parse error at {1!r}".format(
- marker, marker[e.loc : e.loc + 8]
+ raise InvalidMarker(
+ f"Invalid marker: {marker!r}, parse error at "
+ f"{marker[e.loc : e.loc + 8]!r}"
)
- raise InvalidMarker(err_str)
- def __str__(self):
- # type: () -> str
+ def __str__(self) -> str:
return _format_marker(self._markers)
- def __repr__(self):
- # type: () -> str
- return "<Marker({0!r})>".format(str(self))
+ def __repr__(self) -> str:
+ return f"<Marker('{self}')>"
- def evaluate(self, environment=None):
- # type: (Optional[Dict[str, str]]) -> bool
+ def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool:
"""Evaluate a marker.
Return the boolean from evaluating the given marker against the
diff --git a/setuptools/_vendor/packaging/requirements.py b/setuptools/_vendor/packaging/requirements.py
index 5d50c7d7..0d93231b 100644
--- a/setuptools/_vendor/packaging/requirements.py
+++ b/setuptools/_vendor/packaging/requirements.py
@@ -1,23 +1,28 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
-import string
import re
+import string
+import urllib.parse
+from typing import List, Optional as TOptional, Set
+
+from setuptools.extern.pyparsing import ( # noqa
+ Combine,
+ Literal as L,
+ Optional,
+ ParseException,
+ Regex,
+ Word,
+ ZeroOrMore,
+ originalTextFor,
+ stringEnd,
+ stringStart,
+)
-from setuptools.extern.pyparsing import stringStart, stringEnd, originalTextFor, ParseException
-from setuptools.extern.pyparsing import ZeroOrMore, Word, Optional, Regex, Combine
-from setuptools.extern.pyparsing import Literal as L # noqa
-from urllib import parse as urlparse
-
-from ._typing import TYPE_CHECKING
from .markers import MARKER_EXPR, Marker
from .specifiers import LegacySpecifier, Specifier, SpecifierSet
-if TYPE_CHECKING: # pragma: no cover
- from typing import List
-
class InvalidRequirement(ValueError):
"""
@@ -55,7 +60,7 @@ VERSION_ONE = VERSION_PEP440 ^ VERSION_LEGACY
VERSION_MANY = Combine(
VERSION_ONE + ZeroOrMore(COMMA + VERSION_ONE), joinString=",", adjacent=False
)("_raw_spec")
-_VERSION_SPEC = Optional(((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY))
+_VERSION_SPEC = Optional((LPAREN + VERSION_MANY + RPAREN) | VERSION_MANY)
_VERSION_SPEC.setParseAction(lambda s, l, t: t._raw_spec or "")
VERSION_SPEC = originalTextFor(_VERSION_SPEC)("specifier")
@@ -79,7 +84,7 @@ REQUIREMENT = stringStart + NAMED_REQUIREMENT + stringEnd
REQUIREMENT.parseString("x[]")
-class Requirement(object):
+class Requirement:
"""Parse a requirement.
Parse a given requirement string into its parts, such as name, specifier,
@@ -92,54 +97,50 @@ class Requirement(object):
# the thing as well as the version? What about the markers?
# TODO: Can we normalize the name and extra name?
- def __init__(self, requirement_string):
- # type: (str) -> None
+ def __init__(self, requirement_string: str) -> None:
try:
req = REQUIREMENT.parseString(requirement_string)
except ParseException as e:
raise InvalidRequirement(
- 'Parse error at "{0!r}": {1}'.format(
- requirement_string[e.loc : e.loc + 8], e.msg
- )
+ f'Parse error at "{ requirement_string[e.loc : e.loc + 8]!r}": {e.msg}'
)
- self.name = req.name
+ self.name: str = req.name
if req.url:
- parsed_url = urlparse.urlparse(req.url)
+ parsed_url = urllib.parse.urlparse(req.url)
if parsed_url.scheme == "file":
- if urlparse.urlunparse(parsed_url) != req.url:
+ if urllib.parse.urlunparse(parsed_url) != req.url:
raise InvalidRequirement("Invalid URL given")
elif not (parsed_url.scheme and parsed_url.netloc) or (
not parsed_url.scheme and not parsed_url.netloc
):
- raise InvalidRequirement("Invalid URL: {0}".format(req.url))
- self.url = req.url
+ raise InvalidRequirement(f"Invalid URL: {req.url}")
+ self.url: TOptional[str] = req.url
else:
self.url = None
- self.extras = set(req.extras.asList() if req.extras else [])
- self.specifier = SpecifierSet(req.specifier)
- self.marker = req.marker if req.marker else None
+ self.extras: Set[str] = set(req.extras.asList() if req.extras else [])
+ self.specifier: SpecifierSet = SpecifierSet(req.specifier)
+ self.marker: TOptional[Marker] = req.marker if req.marker else None
- def __str__(self):
- # type: () -> str
- parts = [self.name] # type: List[str]
+ def __str__(self) -> str:
+ parts: List[str] = [self.name]
if self.extras:
- parts.append("[{0}]".format(",".join(sorted(self.extras))))
+ formatted_extras = ",".join(sorted(self.extras))
+ parts.append(f"[{formatted_extras}]")
if self.specifier:
parts.append(str(self.specifier))
if self.url:
- parts.append("@ {0}".format(self.url))
+ parts.append(f"@ {self.url}")
if self.marker:
parts.append(" ")
if self.marker:
- parts.append("; {0}".format(self.marker))
+ parts.append(f"; {self.marker}")
return "".join(parts)
- def __repr__(self):
- # type: () -> str
- return "<Requirement({0!r})>".format(str(self))
+ def __repr__(self) -> str:
+ return f"<Requirement('{self}')>"
diff --git a/setuptools/_vendor/packaging/specifiers.py b/setuptools/_vendor/packaging/specifiers.py
index fe09bb1d..ce66bd4a 100644
--- a/setuptools/_vendor/packaging/specifiers.py
+++ b/setuptools/_vendor/packaging/specifiers.py
@@ -1,34 +1,33 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
import abc
import functools
import itertools
import re
+import warnings
+from typing import (
+ Callable,
+ Dict,
+ Iterable,
+ Iterator,
+ List,
+ Optional,
+ Pattern,
+ Set,
+ Tuple,
+ TypeVar,
+ Union,
+)
-from ._compat import string_types, with_metaclass
-from ._typing import TYPE_CHECKING
from .utils import canonicalize_version
-from .version import Version, LegacyVersion, parse
-
-if TYPE_CHECKING: # pragma: no cover
- from typing import (
- List,
- Dict,
- Union,
- Iterable,
- Iterator,
- Optional,
- Callable,
- Tuple,
- FrozenSet,
- )
+from .version import LegacyVersion, Version, parse
- ParsedVersion = Union[Version, LegacyVersion]
- UnparsedVersion = Union[Version, LegacyVersion, str]
- CallableOperator = Callable[[ParsedVersion, str], bool]
+ParsedVersion = Union[Version, LegacyVersion]
+UnparsedVersion = Union[Version, LegacyVersion, str]
+VersionTypeVar = TypeVar("VersionTypeVar", bound=UnparsedVersion)
+CallableOperator = Callable[[ParsedVersion, str], bool]
class InvalidSpecifier(ValueError):
@@ -37,64 +36,58 @@ class InvalidSpecifier(ValueError):
"""
-class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): # type: ignore
+class BaseSpecifier(metaclass=abc.ABCMeta):
@abc.abstractmethod
- def __str__(self):
- # type: () -> str
+ def __str__(self) -> str:
"""
Returns the str representation of this Specifier like object. This
should be representative of the Specifier itself.
"""
@abc.abstractmethod
- def __hash__(self):
- # type: () -> int
+ def __hash__(self) -> int:
"""
Returns a hash value for this Specifier like object.
"""
@abc.abstractmethod
- def __eq__(self, other):
- # type: (object) -> bool
+ def __eq__(self, other: object) -> bool:
"""
Returns a boolean representing whether or not the two Specifier like
objects are equal.
"""
@abc.abstractmethod
- def __ne__(self, other):
- # type: (object) -> bool
+ def __ne__(self, other: object) -> bool:
"""
Returns a boolean representing whether or not the two Specifier like
objects are not equal.
"""
@abc.abstractproperty
- def prereleases(self):
- # type: () -> Optional[bool]
+ def prereleases(self) -> Optional[bool]:
"""
Returns whether or not pre-releases as a whole are allowed by this
specifier.
"""
@prereleases.setter
- def prereleases(self, value):
- # type: (bool) -> None
+ def prereleases(self, value: bool) -> None:
"""
Sets whether or not pre-releases as a whole are allowed by this
specifier.
"""
@abc.abstractmethod
- def contains(self, item, prereleases=None):
- # type: (str, Optional[bool]) -> bool
+ def contains(self, item: str, prereleases: Optional[bool] = None) -> bool:
"""
Determines if the given item is contained within this specifier.
"""
@abc.abstractmethod
- def filter(self, iterable, prereleases=None):
- # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion]
+ def filter(
+ self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None
+ ) -> Iterable[VersionTypeVar]:
"""
Takes an iterable of items and filters them so that only items which
are contained within this specifier are allowed in it.
@@ -103,48 +96,43 @@ class BaseSpecifier(with_metaclass(abc.ABCMeta, object)): # type: ignore
class _IndividualSpecifier(BaseSpecifier):
- _operators = {} # type: Dict[str, str]
+ _operators: Dict[str, str] = {}
+ _regex: Pattern[str]
- def __init__(self, spec="", prereleases=None):
- # type: (str, Optional[bool]) -> None
+ def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None:
match = self._regex.search(spec)
if not match:
- raise InvalidSpecifier("Invalid specifier: '{0}'".format(spec))
+ raise InvalidSpecifier(f"Invalid specifier: '{spec}'")
- self._spec = (
+ self._spec: Tuple[str, str] = (
match.group("operator").strip(),
match.group("version").strip(),
- ) # type: Tuple[str, str]
+ )
# Store whether or not this Specifier should accept prereleases
self._prereleases = prereleases
- def __repr__(self):
- # type: () -> str
+ def __repr__(self) -> str:
pre = (
- ", prereleases={0!r}".format(self.prereleases)
+ f", prereleases={self.prereleases!r}"
if self._prereleases is not None
else ""
)
- return "<{0}({1!r}{2})>".format(self.__class__.__name__, str(self), pre)
+ return "<{}({!r}{})>".format(self.__class__.__name__, str(self), pre)
- def __str__(self):
- # type: () -> str
- return "{0}{1}".format(*self._spec)
+ def __str__(self) -> str:
+ return "{}{}".format(*self._spec)
@property
- def _canonical_spec(self):
- # type: () -> Tuple[str, Union[Version, str]]
+ def _canonical_spec(self) -> Tuple[str, str]:
return self._spec[0], canonicalize_version(self._spec[1])
- def __hash__(self):
- # type: () -> int
+ def __hash__(self) -> int:
return hash(self._canonical_spec)
- def __eq__(self, other):
- # type: (object) -> bool
- if isinstance(other, string_types):
+ def __eq__(self, other: object) -> bool:
+ if isinstance(other, str):
try:
other = self.__class__(str(other))
except InvalidSpecifier:
@@ -154,9 +142,8 @@ class _IndividualSpecifier(BaseSpecifier):
return self._canonical_spec == other._canonical_spec
- def __ne__(self, other):
- # type: (object) -> bool
- if isinstance(other, string_types):
+ def __ne__(self, other: object) -> bool:
+ if isinstance(other, str):
try:
other = self.__class__(str(other))
except InvalidSpecifier:
@@ -166,45 +153,39 @@ class _IndividualSpecifier(BaseSpecifier):
return self._spec != other._spec
- def _get_operator(self, op):
- # type: (str) -> CallableOperator
- operator_callable = getattr(
- self, "_compare_{0}".format(self._operators[op])
- ) # type: CallableOperator
+ def _get_operator(self, op: str) -> CallableOperator:
+ operator_callable: CallableOperator = getattr(
+ self, f"_compare_{self._operators[op]}"
+ )
return operator_callable
- def _coerce_version(self, version):
- # type: (UnparsedVersion) -> ParsedVersion
+ def _coerce_version(self, version: UnparsedVersion) -> ParsedVersion:
if not isinstance(version, (LegacyVersion, Version)):
version = parse(version)
return version
@property
- def operator(self):
- # type: () -> str
+ def operator(self) -> str:
return self._spec[0]
@property
- def version(self):
- # type: () -> str
+ def version(self) -> str:
return self._spec[1]
@property
- def prereleases(self):
- # type: () -> Optional[bool]
+ def prereleases(self) -> Optional[bool]:
return self._prereleases
@prereleases.setter
- def prereleases(self, value):
- # type: (bool) -> None
+ def prereleases(self, value: bool) -> None:
self._prereleases = value
- def __contains__(self, item):
- # type: (str) -> bool
+ def __contains__(self, item: str) -> bool:
return self.contains(item)
- def contains(self, item, prereleases=None):
- # type: (UnparsedVersion, Optional[bool]) -> bool
+ def contains(
+ self, item: UnparsedVersion, prereleases: Optional[bool] = None
+ ) -> bool:
# Determine if prereleases are to be allowed or not.
if prereleases is None:
@@ -222,11 +203,12 @@ class _IndividualSpecifier(BaseSpecifier):
# Actually do the comparison to determine if this item is contained
# within this Specifier or not.
- operator_callable = self._get_operator(self.operator) # type: CallableOperator
+ operator_callable: CallableOperator = self._get_operator(self.operator)
return operator_callable(normalized_item, self.version)
- def filter(self, iterable, prereleases=None):
- # type: (Iterable[UnparsedVersion], Optional[bool]) -> Iterable[UnparsedVersion]
+ def filter(
+ self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None
+ ) -> Iterable[VersionTypeVar]:
yielded = False
found_prereleases = []
@@ -240,7 +222,7 @@ class _IndividualSpecifier(BaseSpecifier):
if self.contains(parsed_version, **kw):
# If our version is a prerelease, and we were not set to allow
- # prereleases, then we'll store it for later incase nothing
+ # prereleases, then we'll store it for later in case nothing
# else matches this specifier.
if parsed_version.is_prerelease and not (
prereleases or self.prereleases
@@ -285,44 +267,46 @@ class LegacySpecifier(_IndividualSpecifier):
">": "greater_than",
}
- def _coerce_version(self, version):
- # type: (Union[ParsedVersion, str]) -> LegacyVersion
+ def __init__(self, spec: str = "", prereleases: Optional[bool] = None) -> None:
+ super().__init__(spec, prereleases)
+
+ warnings.warn(
+ "Creating a LegacyVersion has been deprecated and will be "
+ "removed in the next major release",
+ DeprecationWarning,
+ )
+
+ def _coerce_version(self, version: UnparsedVersion) -> LegacyVersion:
if not isinstance(version, LegacyVersion):
version = LegacyVersion(str(version))
return version
- def _compare_equal(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_equal(self, prospective: LegacyVersion, spec: str) -> bool:
return prospective == self._coerce_version(spec)
- def _compare_not_equal(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_not_equal(self, prospective: LegacyVersion, spec: str) -> bool:
return prospective != self._coerce_version(spec)
- def _compare_less_than_equal(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_less_than_equal(self, prospective: LegacyVersion, spec: str) -> bool:
return prospective <= self._coerce_version(spec)
- def _compare_greater_than_equal(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_greater_than_equal(
+ self, prospective: LegacyVersion, spec: str
+ ) -> bool:
return prospective >= self._coerce_version(spec)
- def _compare_less_than(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_less_than(self, prospective: LegacyVersion, spec: str) -> bool:
return prospective < self._coerce_version(spec)
- def _compare_greater_than(self, prospective, spec):
- # type: (LegacyVersion, str) -> bool
+ def _compare_greater_than(self, prospective: LegacyVersion, spec: str) -> bool:
return prospective > self._coerce_version(spec)
def _require_version_compare(
- fn # type: (Callable[[Specifier, ParsedVersion, str], bool])
-):
- # type: (...) -> Callable[[Specifier, ParsedVersion, str], bool]
+ fn: Callable[["Specifier", ParsedVersion, str], bool]
+) -> Callable[["Specifier", ParsedVersion, str], bool]:
@functools.wraps(fn)
- def wrapped(self, prospective, spec):
- # type: (Specifier, ParsedVersion, str) -> bool
+ def wrapped(self: "Specifier", prospective: ParsedVersion, spec: str) -> bool:
if not isinstance(prospective, Version):
return False
return fn(self, prospective, spec)
@@ -439,8 +423,7 @@ class Specifier(_IndividualSpecifier):
}
@_require_version_compare
- def _compare_compatible(self, prospective, spec):
- # type: (ParsedVersion, str) -> bool
+ def _compare_compatible(self, prospective: ParsedVersion, spec: str) -> bool:
# Compatible releases have an equivalent combination of >= and ==. That
# is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to
@@ -449,15 +432,9 @@ class Specifier(_IndividualSpecifier):
# the other specifiers.
# We want everything but the last item in the version, but we want to
- # ignore post and dev releases and we want to treat the pre-release as
- # it's own separate segment.
+ # ignore suffix segments.
prefix = ".".join(
- list(
- itertools.takewhile(
- lambda x: (not x.startswith("post") and not x.startswith("dev")),
- _version_split(spec),
- )
- )[:-1]
+ list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1]
)
# Add the prefix notation to the end of our string
@@ -468,8 +445,7 @@ class Specifier(_IndividualSpecifier):
)
@_require_version_compare
- def _compare_equal(self, prospective, spec):
- # type: (ParsedVersion, str) -> bool
+ def _compare_equal(self, prospective: ParsedVersion, spec: str) -> bool:
# We need special logic to handle prefix matching
if spec.endswith(".*"):
@@ -509,13 +485,11 @@ class Specifier(_IndividualSpecifier):
return prospective == spec_version
@_require_version_compare
- def _compare_not_equal(self, prospective, spec):
- # type: (ParsedVersion, str) -> bool
+ def _compare_not_equal(self, prospective: ParsedVersion, spec: str) -> bool:
return not self._compare_equal(prospective, spec)
@_require_version_compare
- def _compare_less_than_equal(self, prospective, spec):
- # type: (ParsedVersion, str) -> bool
+ def _compare_less_than_equal(self, prospective: ParsedVersion, spec: str) -> bool:
# NB: Local version identifiers are NOT permitted in the version
# specifier, so local version labels can be universally removed from
@@ -523,8 +497,9 @@ class Specifier(_IndividualSpecifier):
return Version(prospective.public) <= Version(spec)
@_require_version_compare
- def _compare_greater_than_equal(self, prospective, spec):
- # type: (ParsedVersion, str) -> bool
+ def _compare_greater_than_equal(
+ self, prospective: ParsedVersion, spec: str
+ ) -> bool:
# NB: Local version identifiers are NOT permitted in the version
# specifier, so local version labels can be universally removed from
@@ -532,8 +507,7 @@ class Specifier(_IndividualSpecifier):
return Version(prospective.public) >= Version(spec)
@_require_version_compare
- def _compare_less_than(self, prospective, spec_str):
- # type: (ParsedVersion, str) -> bool
+ def _compare_less_than(self, prospective: ParsedVersion, spec_str: str) -> bool:
# Convert our spec to a Version instance, since we'll want to work with
# it as a version.
@@ -559,8 +533,7 @@ class Specifier(_IndividualSpecifier):
return True
@_require_version_compare
- def _compare_greater_than(self, prospective, spec_str):
- # type: (ParsedVersion, str) -> bool
+ def _compare_greater_than(self, prospective: ParsedVersion, spec_str: str) -> bool:
# Convert our spec to a Version instance, since we'll want to work with
# it as a version.
@@ -591,13 +564,11 @@ class Specifier(_IndividualSpecifier):
# same version in the spec.
return True
- def _compare_arbitrary(self, prospective, spec):
- # type: (Version, str) -> bool
+ def _compare_arbitrary(self, prospective: Version, spec: str) -> bool:
return str(prospective).lower() == str(spec).lower()
@property
- def prereleases(self):
- # type: () -> bool
+ def prereleases(self) -> bool:
# If there is an explicit prereleases set for this, then we'll just
# blindly use that.
@@ -622,17 +593,15 @@ class Specifier(_IndividualSpecifier):
return False
@prereleases.setter
- def prereleases(self, value):
- # type: (bool) -> None
+ def prereleases(self, value: bool) -> None:
self._prereleases = value
_prefix_regex = re.compile(r"^([0-9]+)((?:a|b|c|rc)[0-9]+)$")
-def _version_split(version):
- # type: (str) -> List[str]
- result = [] # type: List[str]
+def _version_split(version: str) -> List[str]:
+ result: List[str] = []
for item in version.split("."):
match = _prefix_regex.search(item)
if match:
@@ -642,8 +611,13 @@ def _version_split(version):
return result
-def _pad_version(left, right):
- # type: (List[str], List[str]) -> Tuple[List[str], List[str]]
+def _is_not_suffix(segment: str) -> bool:
+ return not any(
+ segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post")
+ )
+
+
+def _pad_version(left: List[str], right: List[str]) -> Tuple[List[str], List[str]]:
left_split, right_split = [], []
# Get the release segment of our versions
@@ -662,8 +636,9 @@ def _pad_version(left, right):
class SpecifierSet(BaseSpecifier):
- def __init__(self, specifiers="", prereleases=None):
- # type: (str, Optional[bool]) -> None
+ def __init__(
+ self, specifiers: str = "", prereleases: Optional[bool] = None
+ ) -> None:
# Split on , to break each individual specifier into it's own item, and
# strip each item to remove leading/trailing whitespace.
@@ -671,7 +646,7 @@ class SpecifierSet(BaseSpecifier):
# Parsed each individual specifier, attempting first to make it a
# Specifier and falling back to a LegacySpecifier.
- parsed = set()
+ parsed: Set[_IndividualSpecifier] = set()
for specifier in split_specifiers:
try:
parsed.add(Specifier(specifier))
@@ -685,27 +660,23 @@ class SpecifierSet(BaseSpecifier):
# we accept prereleases or not.
self._prereleases = prereleases
- def __repr__(self):
- # type: () -> str
+ def __repr__(self) -> str:
pre = (
- ", prereleases={0!r}".format(self.prereleases)
+ f", prereleases={self.prereleases!r}"
if self._prereleases is not None
else ""
)
- return "<SpecifierSet({0!r}{1})>".format(str(self), pre)
+ return "<SpecifierSet({!r}{})>".format(str(self), pre)
- def __str__(self):
- # type: () -> str
+ def __str__(self) -> str:
return ",".join(sorted(str(s) for s in self._specs))
- def __hash__(self):
- # type: () -> int
+ def __hash__(self) -> int:
return hash(self._specs)
- def __and__(self, other):
- # type: (Union[SpecifierSet, str]) -> SpecifierSet
- if isinstance(other, string_types):
+ def __and__(self, other: Union["SpecifierSet", str]) -> "SpecifierSet":
+ if isinstance(other, str):
other = SpecifierSet(other)
elif not isinstance(other, SpecifierSet):
return NotImplemented
@@ -727,35 +698,30 @@ class SpecifierSet(BaseSpecifier):
return specifier
- def __eq__(self, other):
- # type: (object) -> bool
- if isinstance(other, (string_types, _IndividualSpecifier)):
+ def __eq__(self, other: object) -> bool:
+ if isinstance(other, (str, _IndividualSpecifier)):
other = SpecifierSet(str(other))
elif not isinstance(other, SpecifierSet):
return NotImplemented
return self._specs == other._specs
- def __ne__(self, other):
- # type: (object) -> bool
- if isinstance(other, (string_types, _IndividualSpecifier)):
+ def __ne__(self, other: object) -> bool:
+ if isinstance(other, (str, _IndividualSpecifier)):
other = SpecifierSet(str(other))
elif not isinstance(other, SpecifierSet):
return NotImplemented
return self._specs != other._specs
- def __len__(self):
- # type: () -> int
+ def __len__(self) -> int:
return len(self._specs)
- def __iter__(self):
- # type: () -> Iterator[FrozenSet[_IndividualSpecifier]]
+ def __iter__(self) -> Iterator[_IndividualSpecifier]:
return iter(self._specs)
@property
- def prereleases(self):
- # type: () -> Optional[bool]
+ def prereleases(self) -> Optional[bool]:
# If we have been given an explicit prerelease modifier, then we'll
# pass that through here.
@@ -773,16 +739,15 @@ class SpecifierSet(BaseSpecifier):
return any(s.prereleases for s in self._specs)
@prereleases.setter
- def prereleases(self, value):
- # type: (bool) -> None
+ def prereleases(self, value: bool) -> None:
self._prereleases = value
- def __contains__(self, item):
- # type: (Union[ParsedVersion, str]) -> bool
+ def __contains__(self, item: UnparsedVersion) -> bool:
return self.contains(item)
- def contains(self, item, prereleases=None):
- # type: (Union[ParsedVersion, str], Optional[bool]) -> bool
+ def contains(
+ self, item: UnparsedVersion, prereleases: Optional[bool] = None
+ ) -> bool:
# Ensure that our item is a Version or LegacyVersion instance.
if not isinstance(item, (LegacyVersion, Version)):
@@ -810,11 +775,8 @@ class SpecifierSet(BaseSpecifier):
return all(s.contains(item, prereleases=prereleases) for s in self._specs)
def filter(
- self,
- iterable, # type: Iterable[Union[ParsedVersion, str]]
- prereleases=None, # type: Optional[bool]
- ):
- # type: (...) -> Iterable[Union[ParsedVersion, str]]
+ self, iterable: Iterable[VersionTypeVar], prereleases: Optional[bool] = None
+ ) -> Iterable[VersionTypeVar]:
# Determine if we're forcing a prerelease or not, if we're not forcing
# one for this particular filter call, then we'll use whatever the
@@ -833,8 +795,11 @@ class SpecifierSet(BaseSpecifier):
# which will filter out any pre-releases, unless there are no final
# releases, and which will filter out LegacyVersion in general.
else:
- filtered = [] # type: List[Union[ParsedVersion, str]]
- found_prereleases = [] # type: List[Union[ParsedVersion, str]]
+ filtered: List[VersionTypeVar] = []
+ found_prereleases: List[VersionTypeVar] = []
+
+ item: UnparsedVersion
+ parsed_version: Union[Version, LegacyVersion]
for item in iterable:
# Ensure that we some kind of Version class for this item.
diff --git a/setuptools/_vendor/packaging/tags.py b/setuptools/_vendor/packaging/tags.py
index 9064910b..e65890a9 100644
--- a/setuptools/_vendor/packaging/tags.py
+++ b/setuptools/_vendor/packaging/tags.py
@@ -2,62 +2,44 @@
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import
-
-import distutils.util
-
-try:
- from importlib.machinery import EXTENSION_SUFFIXES
-except ImportError: # pragma: no cover
- import imp
-
- EXTENSION_SUFFIXES = [x[0] for x in imp.get_suffixes()]
- del imp
import logging
-import os
import platform
-import re
-import struct
import sys
import sysconfig
-import warnings
-
-from ._typing import TYPE_CHECKING, cast
-
-if TYPE_CHECKING: # pragma: no cover
- from typing import (
- Dict,
- FrozenSet,
- IO,
- Iterable,
- Iterator,
- List,
- Optional,
- Sequence,
- Tuple,
- Union,
- )
-
- PythonVersion = Sequence[int]
- MacVersion = Tuple[int, int]
- GlibcVersion = Tuple[int, int]
-
+from importlib.machinery import EXTENSION_SUFFIXES
+from typing import (
+ Dict,
+ FrozenSet,
+ Iterable,
+ Iterator,
+ List,
+ Optional,
+ Sequence,
+ Tuple,
+ Union,
+ cast,
+)
+
+from . import _manylinux, _musllinux
logger = logging.getLogger(__name__)
-INTERPRETER_SHORT_NAMES = {
+PythonVersion = Sequence[int]
+MacVersion = Tuple[int, int]
+
+INTERPRETER_SHORT_NAMES: Dict[str, str] = {
"python": "py", # Generic.
"cpython": "cp",
"pypy": "pp",
"ironpython": "ip",
"jython": "jy",
-} # type: Dict[str, str]
+}
_32_BIT_INTERPRETER = sys.maxsize <= 2 ** 32
-class Tag(object):
+class Tag:
"""
A representation of the tag triple for a wheel.
@@ -65,55 +47,53 @@ class Tag(object):
is also supported.
"""
- __slots__ = ["_interpreter", "_abi", "_platform"]
+ __slots__ = ["_interpreter", "_abi", "_platform", "_hash"]
- def __init__(self, interpreter, abi, platform):
- # type: (str, str, str) -> None
+ def __init__(self, interpreter: str, abi: str, platform: str) -> None:
self._interpreter = interpreter.lower()
self._abi = abi.lower()
self._platform = platform.lower()
+ # The __hash__ of every single element in a Set[Tag] will be evaluated each time
+ # that a set calls its `.disjoint()` method, which may be called hundreds of
+ # times when scanning a page of links for packages with tags matching that
+ # Set[Tag]. Pre-computing the value here produces significant speedups for
+ # downstream consumers.
+ self._hash = hash((self._interpreter, self._abi, self._platform))
@property
- def interpreter(self):
- # type: () -> str
+ def interpreter(self) -> str:
return self._interpreter
@property
- def abi(self):
- # type: () -> str
+ def abi(self) -> str:
return self._abi
@property
- def platform(self):
- # type: () -> str
+ def platform(self) -> str:
return self._platform
- def __eq__(self, other):
- # type: (object) -> bool
+ def __eq__(self, other: object) -> bool:
if not isinstance(other, Tag):
return NotImplemented
return (
- (self.platform == other.platform)
- and (self.abi == other.abi)
- and (self.interpreter == other.interpreter)
+ (self._hash == other._hash) # Short-circuit ASAP for perf reasons.
+ and (self._platform == other._platform)
+ and (self._abi == other._abi)
+ and (self._interpreter == other._interpreter)
)
- def __hash__(self):
- # type: () -> int
- return hash((self._interpreter, self._abi, self._platform))
+ def __hash__(self) -> int:
+ return self._hash
- def __str__(self):
- # type: () -> str
- return "{}-{}-{}".format(self._interpreter, self._abi, self._platform)
+ def __str__(self) -> str:
+ return f"{self._interpreter}-{self._abi}-{self._platform}"
- def __repr__(self):
- # type: () -> str
+ def __repr__(self) -> str:
return "<{self} @ {self_id}>".format(self=self, self_id=id(self))
-def parse_tag(tag):
- # type: (str) -> FrozenSet[Tag]
+def parse_tag(tag: str) -> FrozenSet[Tag]:
"""
Parses the provided tag (e.g. `py3-none-any`) into a frozenset of Tag instances.
@@ -129,24 +109,7 @@ def parse_tag(tag):
return frozenset(tags)
-def _warn_keyword_parameter(func_name, kwargs):
- # type: (str, Dict[str, bool]) -> bool
- """
- Backwards-compatibility with Python 2.7 to allow treating 'warn' as keyword-only.
- """
- if not kwargs:
- return False
- elif len(kwargs) > 1 or "warn" not in kwargs:
- kwargs.pop("warn", None)
- arg = next(iter(kwargs.keys()))
- raise TypeError(
- "{}() got an unexpected keyword argument {!r}".format(func_name, arg)
- )
- return kwargs["warn"]
-
-
-def _get_config_var(name, warn=False):
- # type: (str, bool) -> Union[int, str, None]
+def _get_config_var(name: str, warn: bool = False) -> Union[int, str, None]:
value = sysconfig.get_config_var(name)
if value is None and warn:
logger.debug(
@@ -155,13 +118,11 @@ def _get_config_var(name, warn=False):
return value
-def _normalize_string(string):
- # type: (str) -> str
+def _normalize_string(string: str) -> str:
return string.replace(".", "_").replace("-", "_")
-def _abi3_applies(python_version):
- # type: (PythonVersion) -> bool
+def _abi3_applies(python_version: PythonVersion) -> bool:
"""
Determine if the Python version supports abi3.
@@ -170,8 +131,7 @@ def _abi3_applies(python_version):
return len(python_version) > 1 and tuple(python_version) >= (3, 2)
-def _cpython_abis(py_version, warn=False):
- # type: (PythonVersion, bool) -> List[str]
+def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> List[str]:
py_version = tuple(py_version) # To allow for version comparison.
abis = []
version = _version_nodot(py_version[:2])
@@ -197,7 +157,7 @@ def _cpython_abis(py_version, warn=False):
elif debug:
# Debug builds can also load "normal" extension modules.
# We can also assume no UCS-4 or pymalloc requirement.
- abis.append("cp{version}".format(version=version))
+ abis.append(f"cp{version}")
abis.insert(
0,
"cp{version}{debug}{pymalloc}{ucs4}".format(
@@ -208,12 +168,12 @@ def _cpython_abis(py_version, warn=False):
def cpython_tags(
- python_version=None, # type: Optional[PythonVersion]
- abis=None, # type: Optional[Iterable[str]]
- platforms=None, # type: Optional[Iterable[str]]
- **kwargs # type: bool
-):
- # type: (...) -> Iterator[Tag]
+ python_version: Optional[PythonVersion] = None,
+ abis: Optional[Iterable[str]] = None,
+ platforms: Optional[Iterable[str]] = None,
+ *,
+ warn: bool = False,
+) -> Iterator[Tag]:
"""
Yields the tags for a CPython interpreter.
@@ -229,7 +189,6 @@ def cpython_tags(
If 'abi3' or 'none' are specified in 'abis' then they will be yielded at
their normal position and not at the beginning.
"""
- warn = _warn_keyword_parameter("cpython_tags", kwargs)
if not python_version:
python_version = sys.version_info[:2]
@@ -248,15 +207,13 @@ def cpython_tags(
except ValueError:
pass
- platforms = list(platforms or _platform_tags())
+ platforms = list(platforms or platform_tags())
for abi in abis:
for platform_ in platforms:
yield Tag(interpreter, abi, platform_)
if _abi3_applies(python_version):
- for tag in (Tag(interpreter, "abi3", platform_) for platform_ in platforms):
- yield tag
- for tag in (Tag(interpreter, "none", platform_) for platform_ in platforms):
- yield tag
+ yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms)
+ yield from (Tag(interpreter, "none", platform_) for platform_ in platforms)
if _abi3_applies(python_version):
for minor_version in range(python_version[1] - 1, 1, -1):
@@ -267,20 +224,19 @@ def cpython_tags(
yield Tag(interpreter, "abi3", platform_)
-def _generic_abi():
- # type: () -> Iterator[str]
+def _generic_abi() -> Iterator[str]:
abi = sysconfig.get_config_var("SOABI")
if abi:
yield _normalize_string(abi)
def generic_tags(
- interpreter=None, # type: Optional[str]
- abis=None, # type: Optional[Iterable[str]]
- platforms=None, # type: Optional[Iterable[str]]
- **kwargs # type: bool
-):
- # type: (...) -> Iterator[Tag]
+ interpreter: Optional[str] = None,
+ abis: Optional[Iterable[str]] = None,
+ platforms: Optional[Iterable[str]] = None,
+ *,
+ warn: bool = False,
+) -> Iterator[Tag]:
"""
Yields the tags for a generic interpreter.
@@ -289,14 +245,13 @@ def generic_tags(
The "none" ABI will be added if it was not explicitly provided.
"""
- warn = _warn_keyword_parameter("generic_tags", kwargs)
if not interpreter:
interp_name = interpreter_name()
interp_version = interpreter_version(warn=warn)
interpreter = "".join([interp_name, interp_version])
if abis is None:
abis = _generic_abi()
- platforms = list(platforms or _platform_tags())
+ platforms = list(platforms or platform_tags())
abis = list(abis)
if "none" not in abis:
abis.append("none")
@@ -305,8 +260,7 @@ def generic_tags(
yield Tag(interpreter, abi, platform_)
-def _py_interpreter_range(py_version):
- # type: (PythonVersion) -> Iterator[str]
+def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]:
"""
Yields Python versions in descending order.
@@ -322,11 +276,10 @@ def _py_interpreter_range(py_version):
def compatible_tags(
- python_version=None, # type: Optional[PythonVersion]
- interpreter=None, # type: Optional[str]
- platforms=None, # type: Optional[Iterable[str]]
-):
- # type: (...) -> Iterator[Tag]
+ python_version: Optional[PythonVersion] = None,
+ interpreter: Optional[str] = None,
+ platforms: Optional[Iterable[str]] = None,
+) -> Iterator[Tag]:
"""
Yields the sequence of tags that are compatible with a specific version of Python.
@@ -337,7 +290,7 @@ def compatible_tags(
"""
if not python_version:
python_version = sys.version_info[:2]
- platforms = list(platforms or _platform_tags())
+ platforms = list(platforms or platform_tags())
for version in _py_interpreter_range(python_version):
for platform_ in platforms:
yield Tag(version, "none", platform_)
@@ -347,8 +300,7 @@ def compatible_tags(
yield Tag(version, "none", "any")
-def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER):
- # type: (str, bool) -> str
+def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str:
if not is_32bit:
return arch
@@ -358,8 +310,7 @@ def _mac_arch(arch, is_32bit=_32_BIT_INTERPRETER):
return "i386"
-def _mac_binary_formats(version, cpu_arch):
- # type: (MacVersion, str) -> List[str]
+def _mac_binary_formats(version: MacVersion, cpu_arch: str) -> List[str]:
formats = [cpu_arch]
if cpu_arch == "x86_64":
if version < (10, 4):
@@ -382,12 +333,18 @@ def _mac_binary_formats(version, cpu_arch):
return []
formats.extend(["fat32", "fat"])
- formats.append("universal")
+ if cpu_arch in {"arm64", "x86_64"}:
+ formats.append("universal2")
+
+ if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}:
+ formats.append("universal")
+
return formats
-def mac_platforms(version=None, arch=None):
- # type: (Optional[MacVersion], Optional[str]) -> Iterator[str]
+def mac_platforms(
+ version: Optional[MacVersion] = None, arch: Optional[str] = None
+) -> Iterator[str]:
"""
Yields the platform tags for a macOS system.
@@ -396,7 +353,7 @@ def mac_platforms(version=None, arch=None):
generate platform tags for. Both parameters default to the appropriate value
for the current system.
"""
- version_str, _, cpu_arch = platform.mac_ver() # type: ignore
+ version_str, _, cpu_arch = platform.mac_ver()
if version is None:
version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2])))
else:
@@ -405,283 +362,76 @@ def mac_platforms(version=None, arch=None):
arch = _mac_arch(cpu_arch)
else:
arch = arch
- for minor_version in range(version[1], -1, -1):
- compat_version = version[0], minor_version
- binary_formats = _mac_binary_formats(compat_version, arch)
- for binary_format in binary_formats:
- yield "macosx_{major}_{minor}_{binary_format}".format(
- major=compat_version[0],
- minor=compat_version[1],
- binary_format=binary_format,
- )
-
-
-# From PEP 513.
-def _is_manylinux_compatible(name, glibc_version):
- # type: (str, GlibcVersion) -> bool
- # Check for presence of _manylinux module.
- try:
- import _manylinux # noqa
-
- return bool(getattr(_manylinux, name + "_compatible"))
- except (ImportError, AttributeError):
- # Fall through to heuristic check below.
- pass
- return _have_compatible_glibc(*glibc_version)
-
-
-def _glibc_version_string():
- # type: () -> Optional[str]
- # Returns glibc version string, or None if not using glibc.
- return _glibc_version_string_confstr() or _glibc_version_string_ctypes()
-
-
-def _glibc_version_string_confstr():
- # type: () -> Optional[str]
- """
- Primary implementation of glibc_version_string using os.confstr.
- """
- # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely
- # to be broken or missing. This strategy is used in the standard library
- # platform module.
- # https://github.com/python/cpython/blob/fcf1d003bf4f0100c9d0921ff3d70e1127ca1b71/Lib/platform.py#L175-L183
- try:
- # os.confstr("CS_GNU_LIBC_VERSION") returns a string like "glibc 2.17".
- version_string = os.confstr( # type: ignore[attr-defined] # noqa: F821
- "CS_GNU_LIBC_VERSION"
- )
- assert version_string is not None
- _, version = version_string.split() # type: Tuple[str, str]
- except (AssertionError, AttributeError, OSError, ValueError):
- # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)...
- return None
- return version
+ if (10, 0) <= version and version < (11, 0):
+ # Prior to Mac OS 11, each yearly release of Mac OS bumped the
+ # "minor" version number. The major version was always 10.
+ for minor_version in range(version[1], -1, -1):
+ compat_version = 10, minor_version
+ binary_formats = _mac_binary_formats(compat_version, arch)
+ for binary_format in binary_formats:
+ yield "macosx_{major}_{minor}_{binary_format}".format(
+ major=10, minor=minor_version, binary_format=binary_format
+ )
+ if version >= (11, 0):
+ # Starting with Mac OS 11, each yearly release bumps the major version
+ # number. The minor versions are now the midyear updates.
+ for major_version in range(version[0], 10, -1):
+ compat_version = major_version, 0
+ binary_formats = _mac_binary_formats(compat_version, arch)
+ for binary_format in binary_formats:
+ yield "macosx_{major}_{minor}_{binary_format}".format(
+ major=major_version, minor=0, binary_format=binary_format
+ )
-def _glibc_version_string_ctypes():
- # type: () -> Optional[str]
- """
- Fallback implementation of glibc_version_string using ctypes.
- """
- try:
- import ctypes
- except ImportError:
- return None
-
- # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen
- # manpage says, "If filename is NULL, then the returned handle is for the
- # main program". This way we can let the linker do the work to figure out
- # which libc our process is actually using.
- #
- # Note: typeshed is wrong here so we are ignoring this line.
- process_namespace = ctypes.CDLL(None) # type: ignore
- try:
- gnu_get_libc_version = process_namespace.gnu_get_libc_version
- except AttributeError:
- # Symbol doesn't exist -> therefore, we are not linked to
- # glibc.
- return None
-
- # Call gnu_get_libc_version, which returns a string like "2.5"
- gnu_get_libc_version.restype = ctypes.c_char_p
- version_str = gnu_get_libc_version() # type: str
- # py2 / py3 compatibility:
- if not isinstance(version_str, str):
- version_str = version_str.decode("ascii")
-
- return version_str
-
-
-# Separated out from have_compatible_glibc for easier unit testing.
-def _check_glibc_version(version_str, required_major, minimum_minor):
- # type: (str, int, int) -> bool
- # Parse string and check against requested version.
- #
- # We use a regexp instead of str.split because we want to discard any
- # random junk that might come after the minor version -- this might happen
- # in patched/forked versions of glibc (e.g. Linaro's version of glibc
- # uses version strings like "2.20-2014.11"). See gh-3588.
- m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str)
- if not m:
- warnings.warn(
- "Expected glibc version with 2 components major.minor,"
- " got: %s" % version_str,
- RuntimeWarning,
- )
- return False
- return (
- int(m.group("major")) == required_major
- and int(m.group("minor")) >= minimum_minor
- )
+ if version >= (11, 0):
+ # Mac OS 11 on x86_64 is compatible with binaries from previous releases.
+ # Arm64 support was introduced in 11.0, so no Arm binaries from previous
+ # releases exist.
+ #
+ # However, the "universal2" binary format can have a
+ # macOS version earlier than 11.0 when the x86_64 part of the binary supports
+ # that version of macOS.
+ if arch == "x86_64":
+ for minor_version in range(16, 3, -1):
+ compat_version = 10, minor_version
+ binary_formats = _mac_binary_formats(compat_version, arch)
+ for binary_format in binary_formats:
+ yield "macosx_{major}_{minor}_{binary_format}".format(
+ major=compat_version[0],
+ minor=compat_version[1],
+ binary_format=binary_format,
+ )
+ else:
+ for minor_version in range(16, 3, -1):
+ compat_version = 10, minor_version
+ binary_format = "universal2"
+ yield "macosx_{major}_{minor}_{binary_format}".format(
+ major=compat_version[0],
+ minor=compat_version[1],
+ binary_format=binary_format,
+ )
-def _have_compatible_glibc(required_major, minimum_minor):
- # type: (int, int) -> bool
- version_str = _glibc_version_string()
- if version_str is None:
- return False
- return _check_glibc_version(version_str, required_major, minimum_minor)
-
-
-# Python does not provide platform information at sufficient granularity to
-# identify the architecture of the running executable in some cases, so we
-# determine it dynamically by reading the information from the running
-# process. This only applies on Linux, which uses the ELF format.
-class _ELFFileHeader(object):
- # https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header
- class _InvalidELFFileHeader(ValueError):
- """
- An invalid ELF file header was found.
- """
-
- ELF_MAGIC_NUMBER = 0x7F454C46
- ELFCLASS32 = 1
- ELFCLASS64 = 2
- ELFDATA2LSB = 1
- ELFDATA2MSB = 2
- EM_386 = 3
- EM_S390 = 22
- EM_ARM = 40
- EM_X86_64 = 62
- EF_ARM_ABIMASK = 0xFF000000
- EF_ARM_ABI_VER5 = 0x05000000
- EF_ARM_ABI_FLOAT_HARD = 0x00000400
-
- def __init__(self, file):
- # type: (IO[bytes]) -> None
- def unpack(fmt):
- # type: (str) -> int
- try:
- (result,) = struct.unpack(
- fmt, file.read(struct.calcsize(fmt))
- ) # type: (int, )
- except struct.error:
- raise _ELFFileHeader._InvalidELFFileHeader()
- return result
-
- self.e_ident_magic = unpack(">I")
- if self.e_ident_magic != self.ELF_MAGIC_NUMBER:
- raise _ELFFileHeader._InvalidELFFileHeader()
- self.e_ident_class = unpack("B")
- if self.e_ident_class not in {self.ELFCLASS32, self.ELFCLASS64}:
- raise _ELFFileHeader._InvalidELFFileHeader()
- self.e_ident_data = unpack("B")
- if self.e_ident_data not in {self.ELFDATA2LSB, self.ELFDATA2MSB}:
- raise _ELFFileHeader._InvalidELFFileHeader()
- self.e_ident_version = unpack("B")
- self.e_ident_osabi = unpack("B")
- self.e_ident_abiversion = unpack("B")
- self.e_ident_pad = file.read(7)
- format_h = "<H" if self.e_ident_data == self.ELFDATA2LSB else ">H"
- format_i = "<I" if self.e_ident_data == self.ELFDATA2LSB else ">I"
- format_q = "<Q" if self.e_ident_data == self.ELFDATA2LSB else ">Q"
- format_p = format_i if self.e_ident_class == self.ELFCLASS32 else format_q
- self.e_type = unpack(format_h)
- self.e_machine = unpack(format_h)
- self.e_version = unpack(format_i)
- self.e_entry = unpack(format_p)
- self.e_phoff = unpack(format_p)
- self.e_shoff = unpack(format_p)
- self.e_flags = unpack(format_i)
- self.e_ehsize = unpack(format_h)
- self.e_phentsize = unpack(format_h)
- self.e_phnum = unpack(format_h)
- self.e_shentsize = unpack(format_h)
- self.e_shnum = unpack(format_h)
- self.e_shstrndx = unpack(format_h)
-
-
-def _get_elf_header():
- # type: () -> Optional[_ELFFileHeader]
- try:
- with open(sys.executable, "rb") as f:
- elf_header = _ELFFileHeader(f)
- except (IOError, OSError, TypeError, _ELFFileHeader._InvalidELFFileHeader):
- return None
- return elf_header
-
-
-def _is_linux_armhf():
- # type: () -> bool
- # hard-float ABI can be detected from the ELF header of the running
- # process
- # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf
- elf_header = _get_elf_header()
- if elf_header is None:
- return False
- result = elf_header.e_ident_class == elf_header.ELFCLASS32
- result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB
- result &= elf_header.e_machine == elf_header.EM_ARM
- result &= (
- elf_header.e_flags & elf_header.EF_ARM_ABIMASK
- ) == elf_header.EF_ARM_ABI_VER5
- result &= (
- elf_header.e_flags & elf_header.EF_ARM_ABI_FLOAT_HARD
- ) == elf_header.EF_ARM_ABI_FLOAT_HARD
- return result
-
-
-def _is_linux_i686():
- # type: () -> bool
- elf_header = _get_elf_header()
- if elf_header is None:
- return False
- result = elf_header.e_ident_class == elf_header.ELFCLASS32
- result &= elf_header.e_ident_data == elf_header.ELFDATA2LSB
- result &= elf_header.e_machine == elf_header.EM_386
- return result
-
-
-def _have_compatible_manylinux_abi(arch):
- # type: (str) -> bool
- if arch == "armv7l":
- return _is_linux_armhf()
- if arch == "i686":
- return _is_linux_i686()
- return True
-
-
-def _linux_platforms(is_32bit=_32_BIT_INTERPRETER):
- # type: (bool) -> Iterator[str]
- linux = _normalize_string(distutils.util.get_platform())
+def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]:
+ linux = _normalize_string(sysconfig.get_platform())
if is_32bit:
if linux == "linux_x86_64":
linux = "linux_i686"
elif linux == "linux_aarch64":
linux = "linux_armv7l"
- manylinux_support = []
_, arch = linux.split("_", 1)
- if _have_compatible_manylinux_abi(arch):
- if arch in {"x86_64", "i686", "aarch64", "armv7l", "ppc64", "ppc64le", "s390x"}:
- manylinux_support.append(
- ("manylinux2014", (2, 17))
- ) # CentOS 7 w/ glibc 2.17 (PEP 599)
- if arch in {"x86_64", "i686"}:
- manylinux_support.append(
- ("manylinux2010", (2, 12))
- ) # CentOS 6 w/ glibc 2.12 (PEP 571)
- manylinux_support.append(
- ("manylinux1", (2, 5))
- ) # CentOS 5 w/ glibc 2.5 (PEP 513)
- manylinux_support_iter = iter(manylinux_support)
- for name, glibc_version in manylinux_support_iter:
- if _is_manylinux_compatible(name, glibc_version):
- yield linux.replace("linux", name)
- break
- # Support for a later manylinux implies support for an earlier version.
- for name, _ in manylinux_support_iter:
- yield linux.replace("linux", name)
+ yield from _manylinux.platform_tags(linux, arch)
+ yield from _musllinux.platform_tags(arch)
yield linux
-def _generic_platforms():
- # type: () -> Iterator[str]
- yield _normalize_string(distutils.util.get_platform())
+def _generic_platforms() -> Iterator[str]:
+ yield _normalize_string(sysconfig.get_platform())
-def _platform_tags():
- # type: () -> Iterator[str]
+def platform_tags() -> Iterator[str]:
"""
Provides the platform tags for this installation.
"""
@@ -693,25 +443,18 @@ def _platform_tags():
return _generic_platforms()
-def interpreter_name():
- # type: () -> str
+def interpreter_name() -> str:
"""
Returns the name of the running interpreter.
"""
- try:
- name = sys.implementation.name # type: ignore
- except AttributeError: # pragma: no cover
- # Python 2.7 compatibility.
- name = platform.python_implementation().lower()
+ name = sys.implementation.name
return INTERPRETER_SHORT_NAMES.get(name) or name
-def interpreter_version(**kwargs):
- # type: (bool) -> str
+def interpreter_version(*, warn: bool = False) -> str:
"""
Returns the version of the running interpreter.
"""
- warn = _warn_keyword_parameter("interpreter_version", kwargs)
version = _get_config_var("py_version_nodot", warn=warn)
if version:
version = str(version)
@@ -720,32 +463,22 @@ def interpreter_version(**kwargs):
return version
-def _version_nodot(version):
- # type: (PythonVersion) -> str
- if any(v >= 10 for v in version):
- sep = "_"
- else:
- sep = ""
- return sep.join(map(str, version))
+def _version_nodot(version: PythonVersion) -> str:
+ return "".join(map(str, version))
-def sys_tags(**kwargs):
- # type: (bool) -> Iterator[Tag]
+def sys_tags(*, warn: bool = False) -> Iterator[Tag]:
"""
Returns the sequence of tag triples for the running interpreter.
The order of the sequence corresponds to priority order for the
interpreter, from most to least important.
"""
- warn = _warn_keyword_parameter("sys_tags", kwargs)
interp_name = interpreter_name()
if interp_name == "cp":
- for tag in cpython_tags(warn=warn):
- yield tag
+ yield from cpython_tags(warn=warn)
else:
- for tag in generic_tags():
- yield tag
+ yield from generic_tags()
- for tag in compatible_tags():
- yield tag
+ yield from compatible_tags()
diff --git a/setuptools/_vendor/packaging/utils.py b/setuptools/_vendor/packaging/utils.py
index 19579c1a..bab11b80 100644
--- a/setuptools/_vendor/packaging/utils.py
+++ b/setuptools/_vendor/packaging/utils.py
@@ -1,65 +1,136 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
import re
+from typing import FrozenSet, NewType, Tuple, Union, cast
-from ._typing import TYPE_CHECKING, cast
+from .tags import Tag, parse_tag
from .version import InvalidVersion, Version
-if TYPE_CHECKING: # pragma: no cover
- from typing import NewType, Union
+BuildTag = Union[Tuple[()], Tuple[int, str]]
+NormalizedName = NewType("NormalizedName", str)
+
+
+class InvalidWheelFilename(ValueError):
+ """
+ An invalid wheel filename was found, users should refer to PEP 427.
+ """
+
+
+class InvalidSdistFilename(ValueError):
+ """
+ An invalid sdist filename was found, users should refer to the packaging user guide.
+ """
- NormalizedName = NewType("NormalizedName", str)
_canonicalize_regex = re.compile(r"[-_.]+")
+# PEP 427: The build number must start with a digit.
+_build_tag_regex = re.compile(r"(\d+)(.*)")
-def canonicalize_name(name):
- # type: (str) -> NormalizedName
+def canonicalize_name(name: str) -> NormalizedName:
# This is taken from PEP 503.
value = _canonicalize_regex.sub("-", name).lower()
- return cast("NormalizedName", value)
+ return cast(NormalizedName, value)
-def canonicalize_version(_version):
- # type: (str) -> Union[Version, str]
+def canonicalize_version(version: Union[Version, str]) -> str:
"""
This is very similar to Version.__str__, but has one subtle difference
with the way it handles the release segment.
"""
-
- try:
- version = Version(_version)
- except InvalidVersion:
- # Legacy versions cannot be normalized
- return _version
+ if isinstance(version, str):
+ try:
+ parsed = Version(version)
+ except InvalidVersion:
+ # Legacy versions cannot be normalized
+ return version
+ else:
+ parsed = version
parts = []
# Epoch
- if version.epoch != 0:
- parts.append("{0}!".format(version.epoch))
+ if parsed.epoch != 0:
+ parts.append(f"{parsed.epoch}!")
# Release segment
# NB: This strips trailing '.0's to normalize
- parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in version.release)))
+ parts.append(re.sub(r"(\.0)+$", "", ".".join(str(x) for x in parsed.release)))
# Pre-release
- if version.pre is not None:
- parts.append("".join(str(x) for x in version.pre))
+ if parsed.pre is not None:
+ parts.append("".join(str(x) for x in parsed.pre))
# Post-release
- if version.post is not None:
- parts.append(".post{0}".format(version.post))
+ if parsed.post is not None:
+ parts.append(f".post{parsed.post}")
# Development release
- if version.dev is not None:
- parts.append(".dev{0}".format(version.dev))
+ if parsed.dev is not None:
+ parts.append(f".dev{parsed.dev}")
# Local version segment
- if version.local is not None:
- parts.append("+{0}".format(version.local))
+ if parsed.local is not None:
+ parts.append(f"+{parsed.local}")
return "".join(parts)
+
+
+def parse_wheel_filename(
+ filename: str,
+) -> Tuple[NormalizedName, Version, BuildTag, FrozenSet[Tag]]:
+ if not filename.endswith(".whl"):
+ raise InvalidWheelFilename(
+ f"Invalid wheel filename (extension must be '.whl'): {filename}"
+ )
+
+ filename = filename[:-4]
+ dashes = filename.count("-")
+ if dashes not in (4, 5):
+ raise InvalidWheelFilename(
+ f"Invalid wheel filename (wrong number of parts): {filename}"
+ )
+
+ parts = filename.split("-", dashes - 2)
+ name_part = parts[0]
+ # See PEP 427 for the rules on escaping the project name
+ if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None:
+ raise InvalidWheelFilename(f"Invalid project name: {filename}")
+ name = canonicalize_name(name_part)
+ version = Version(parts[1])
+ if dashes == 5:
+ build_part = parts[2]
+ build_match = _build_tag_regex.match(build_part)
+ if build_match is None:
+ raise InvalidWheelFilename(
+ f"Invalid build number: {build_part} in '{filename}'"
+ )
+ build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2)))
+ else:
+ build = ()
+ tags = parse_tag(parts[-1])
+ return (name, version, build, tags)
+
+
+def parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]:
+ if filename.endswith(".tar.gz"):
+ file_stem = filename[: -len(".tar.gz")]
+ elif filename.endswith(".zip"):
+ file_stem = filename[: -len(".zip")]
+ else:
+ raise InvalidSdistFilename(
+ f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):"
+ f" {filename}"
+ )
+
+ # We are requiring a PEP 440 version, which cannot contain dashes,
+ # so we split on the last dash.
+ name_part, sep, version_part = file_stem.rpartition("-")
+ if not sep:
+ raise InvalidSdistFilename(f"Invalid sdist filename: {filename}")
+
+ name = canonicalize_name(name_part)
+ version = Version(version_part)
+ return (name, version)
diff --git a/setuptools/_vendor/packaging/version.py b/setuptools/_vendor/packaging/version.py
index 00371e86..de9a09a4 100644
--- a/setuptools/_vendor/packaging/version.py
+++ b/setuptools/_vendor/packaging/version.py
@@ -1,52 +1,45 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
-from __future__ import absolute_import, division, print_function
import collections
import itertools
import re
+import warnings
+from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union
-from ._structures import Infinity, NegativeInfinity
-from ._typing import TYPE_CHECKING
-
-if TYPE_CHECKING: # pragma: no cover
- from typing import Callable, Iterator, List, Optional, SupportsInt, Tuple, Union
-
- from ._structures import InfinityType, NegativeInfinityType
-
- InfiniteTypes = Union[InfinityType, NegativeInfinityType]
- PrePostDevType = Union[InfiniteTypes, Tuple[str, int]]
- SubLocalType = Union[InfiniteTypes, int, str]
- LocalType = Union[
- NegativeInfinityType,
- Tuple[
- Union[
- SubLocalType,
- Tuple[SubLocalType, str],
- Tuple[NegativeInfinityType, SubLocalType],
- ],
- ...,
- ],
- ]
- CmpKey = Tuple[
- int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType
- ]
- LegacyCmpKey = Tuple[int, Tuple[str, ...]]
- VersionComparisonMethod = Callable[
- [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool
- ]
+from ._structures import Infinity, InfinityType, NegativeInfinity, NegativeInfinityType
__all__ = ["parse", "Version", "LegacyVersion", "InvalidVersion", "VERSION_PATTERN"]
+InfiniteTypes = Union[InfinityType, NegativeInfinityType]
+PrePostDevType = Union[InfiniteTypes, Tuple[str, int]]
+SubLocalType = Union[InfiniteTypes, int, str]
+LocalType = Union[
+ NegativeInfinityType,
+ Tuple[
+ Union[
+ SubLocalType,
+ Tuple[SubLocalType, str],
+ Tuple[NegativeInfinityType, SubLocalType],
+ ],
+ ...,
+ ],
+]
+CmpKey = Tuple[
+ int, Tuple[int, ...], PrePostDevType, PrePostDevType, PrePostDevType, LocalType
+]
+LegacyCmpKey = Tuple[int, Tuple[str, ...]]
+VersionComparisonMethod = Callable[
+ [Union[CmpKey, LegacyCmpKey], Union[CmpKey, LegacyCmpKey]], bool
+]
_Version = collections.namedtuple(
"_Version", ["epoch", "release", "dev", "pre", "post", "local"]
)
-def parse(version):
- # type: (str) -> Union[LegacyVersion, Version]
+def parse(version: str) -> Union["LegacyVersion", "Version"]:
"""
Parse the given version string and return either a :class:`Version` object
or a :class:`LegacyVersion` object depending on if the given version is
@@ -64,112 +57,111 @@ class InvalidVersion(ValueError):
"""
-class _BaseVersion(object):
- _key = None # type: Union[CmpKey, LegacyCmpKey]
+class _BaseVersion:
+ _key: Union[CmpKey, LegacyCmpKey]
- def __hash__(self):
- # type: () -> int
+ def __hash__(self) -> int:
return hash(self._key)
- def __lt__(self, other):
- # type: (_BaseVersion) -> bool
- return self._compare(other, lambda s, o: s < o)
+ # Please keep the duplicated `isinstance` check
+ # in the six comparisons hereunder
+ # unless you find a way to avoid adding overhead function calls.
+ def __lt__(self, other: "_BaseVersion") -> bool:
+ if not isinstance(other, _BaseVersion):
+ return NotImplemented
+
+ return self._key < other._key
+
+ def __le__(self, other: "_BaseVersion") -> bool:
+ if not isinstance(other, _BaseVersion):
+ return NotImplemented
+
+ return self._key <= other._key
- def __le__(self, other):
- # type: (_BaseVersion) -> bool
- return self._compare(other, lambda s, o: s <= o)
+ def __eq__(self, other: object) -> bool:
+ if not isinstance(other, _BaseVersion):
+ return NotImplemented
- def __eq__(self, other):
- # type: (object) -> bool
- return self._compare(other, lambda s, o: s == o)
+ return self._key == other._key
- def __ge__(self, other):
- # type: (_BaseVersion) -> bool
- return self._compare(other, lambda s, o: s >= o)
+ def __ge__(self, other: "_BaseVersion") -> bool:
+ if not isinstance(other, _BaseVersion):
+ return NotImplemented
- def __gt__(self, other):
- # type: (_BaseVersion) -> bool
- return self._compare(other, lambda s, o: s > o)
+ return self._key >= other._key
- def __ne__(self, other):
- # type: (object) -> bool
- return self._compare(other, lambda s, o: s != o)
+ def __gt__(self, other: "_BaseVersion") -> bool:
+ if not isinstance(other, _BaseVersion):
+ return NotImplemented
+
+ return self._key > other._key
- def _compare(self, other, method):
- # type: (object, VersionComparisonMethod) -> Union[bool, NotImplemented]
+ def __ne__(self, other: object) -> bool:
if not isinstance(other, _BaseVersion):
return NotImplemented
- return method(self._key, other._key)
+ return self._key != other._key
class LegacyVersion(_BaseVersion):
- def __init__(self, version):
- # type: (str) -> None
+ def __init__(self, version: str) -> None:
self._version = str(version)
self._key = _legacy_cmpkey(self._version)
- def __str__(self):
- # type: () -> str
+ warnings.warn(
+ "Creating a LegacyVersion has been deprecated and will be "
+ "removed in the next major release",
+ DeprecationWarning,
+ )
+
+ def __str__(self) -> str:
return self._version
- def __repr__(self):
- # type: () -> str
- return "<LegacyVersion({0})>".format(repr(str(self)))
+ def __repr__(self) -> str:
+ return f"<LegacyVersion('{self}')>"
@property
- def public(self):
- # type: () -> str
+ def public(self) -> str:
return self._version
@property
- def base_version(self):
- # type: () -> str
+ def base_version(self) -> str:
return self._version
@property
- def epoch(self):
- # type: () -> int
+ def epoch(self) -> int:
return -1
@property
- def release(self):
- # type: () -> None
+ def release(self) -> None:
return None
@property
- def pre(self):
- # type: () -> None
+ def pre(self) -> None:
return None
@property
- def post(self):
- # type: () -> None
+ def post(self) -> None:
return None
@property
- def dev(self):
- # type: () -> None
+ def dev(self) -> None:
return None
@property
- def local(self):
- # type: () -> None
+ def local(self) -> None:
return None
@property
- def is_prerelease(self):
- # type: () -> bool
+ def is_prerelease(self) -> bool:
return False
@property
- def is_postrelease(self):
- # type: () -> bool
+ def is_postrelease(self) -> bool:
return False
@property
- def is_devrelease(self):
- # type: () -> bool
+ def is_devrelease(self) -> bool:
return False
@@ -184,8 +176,7 @@ _legacy_version_replacement_map = {
}
-def _parse_version_parts(s):
- # type: (str) -> Iterator[str]
+def _parse_version_parts(s: str) -> Iterator[str]:
for part in _legacy_version_component_re.split(s):
part = _legacy_version_replacement_map.get(part, part)
@@ -202,8 +193,7 @@ def _parse_version_parts(s):
yield "*final"
-def _legacy_cmpkey(version):
- # type: (str) -> LegacyCmpKey
+def _legacy_cmpkey(version: str) -> LegacyCmpKey:
# We hardcode an epoch of -1 here. A PEP 440 version can only have a epoch
# greater than or equal to 0. This will effectively put the LegacyVersion,
@@ -213,7 +203,7 @@ def _legacy_cmpkey(version):
# This scheme is taken from pkg_resources.parse_version setuptools prior to
# it's adoption of the packaging library.
- parts = [] # type: List[str]
+ parts: List[str] = []
for part in _parse_version_parts(version.lower()):
if part.startswith("*"):
# remove "-" before a prerelease tag
@@ -268,13 +258,12 @@ class Version(_BaseVersion):
_regex = re.compile(r"^\s*" + VERSION_PATTERN + r"\s*$", re.VERBOSE | re.IGNORECASE)
- def __init__(self, version):
- # type: (str) -> None
+ def __init__(self, version: str) -> None:
# Validate the version and parse it into pieces
match = self._regex.search(version)
if not match:
- raise InvalidVersion("Invalid version: '{0}'".format(version))
+ raise InvalidVersion(f"Invalid version: '{version}'")
# Store the parsed out pieces of the version
self._version = _Version(
@@ -298,17 +287,15 @@ class Version(_BaseVersion):
self._version.local,
)
- def __repr__(self):
- # type: () -> str
- return "<Version({0})>".format(repr(str(self)))
+ def __repr__(self) -> str:
+ return f"<Version('{self}')>"
- def __str__(self):
- # type: () -> str
+ def __str__(self) -> str:
parts = []
# Epoch
if self.epoch != 0:
- parts.append("{0}!".format(self.epoch))
+ parts.append(f"{self.epoch}!")
# Release segment
parts.append(".".join(str(x) for x in self.release))
@@ -319,67 +306,59 @@ class Version(_BaseVersion):
# Post-release
if self.post is not None:
- parts.append(".post{0}".format(self.post))
+ parts.append(f".post{self.post}")
# Development release
if self.dev is not None:
- parts.append(".dev{0}".format(self.dev))
+ parts.append(f".dev{self.dev}")
# Local version segment
if self.local is not None:
- parts.append("+{0}".format(self.local))
+ parts.append(f"+{self.local}")
return "".join(parts)
@property
- def epoch(self):
- # type: () -> int
- _epoch = self._version.epoch # type: int
+ def epoch(self) -> int:
+ _epoch: int = self._version.epoch
return _epoch
@property
- def release(self):
- # type: () -> Tuple[int, ...]
- _release = self._version.release # type: Tuple[int, ...]
+ def release(self) -> Tuple[int, ...]:
+ _release: Tuple[int, ...] = self._version.release
return _release
@property
- def pre(self):
- # type: () -> Optional[Tuple[str, int]]
- _pre = self._version.pre # type: Optional[Tuple[str, int]]
+ def pre(self) -> Optional[Tuple[str, int]]:
+ _pre: Optional[Tuple[str, int]] = self._version.pre
return _pre
@property
- def post(self):
- # type: () -> Optional[Tuple[str, int]]
+ def post(self) -> Optional[int]:
return self._version.post[1] if self._version.post else None
@property
- def dev(self):
- # type: () -> Optional[Tuple[str, int]]
+ def dev(self) -> Optional[int]:
return self._version.dev[1] if self._version.dev else None
@property
- def local(self):
- # type: () -> Optional[str]
+ def local(self) -> Optional[str]:
if self._version.local:
return ".".join(str(x) for x in self._version.local)
else:
return None
@property
- def public(self):
- # type: () -> str
+ def public(self) -> str:
return str(self).split("+", 1)[0]
@property
- def base_version(self):
- # type: () -> str
+ def base_version(self) -> str:
parts = []
# Epoch
if self.epoch != 0:
- parts.append("{0}!".format(self.epoch))
+ parts.append(f"{self.epoch}!")
# Release segment
parts.append(".".join(str(x) for x in self.release))
@@ -387,41 +366,33 @@ class Version(_BaseVersion):
return "".join(parts)
@property
- def is_prerelease(self):
- # type: () -> bool
+ def is_prerelease(self) -> bool:
return self.dev is not None or self.pre is not None
@property
- def is_postrelease(self):
- # type: () -> bool
+ def is_postrelease(self) -> bool:
return self.post is not None
@property
- def is_devrelease(self):
- # type: () -> bool
+ def is_devrelease(self) -> bool:
return self.dev is not None
@property
- def major(self):
- # type: () -> int
+ def major(self) -> int:
return self.release[0] if len(self.release) >= 1 else 0
@property
- def minor(self):
- # type: () -> int
+ def minor(self) -> int:
return self.release[1] if len(self.release) >= 2 else 0
@property
- def micro(self):
- # type: () -> int
+ def micro(self) -> int:
return self.release[2] if len(self.release) >= 3 else 0
def _parse_letter_version(
- letter, # type: str
- number, # type: Union[str, bytes, SupportsInt]
-):
- # type: (...) -> Optional[Tuple[str, int]]
+ letter: str, number: Union[str, bytes, SupportsInt]
+) -> Optional[Tuple[str, int]]:
if letter:
# We consider there to be an implicit 0 in a pre-release if there is
@@ -458,8 +429,7 @@ def _parse_letter_version(
_local_version_separators = re.compile(r"[\._-]")
-def _parse_local_version(local):
- # type: (str) -> Optional[LocalType]
+def _parse_local_version(local: str) -> Optional[LocalType]:
"""
Takes a string like abc.1.twelve and turns it into ("abc", 1, "twelve").
"""
@@ -472,14 +442,13 @@ def _parse_local_version(local):
def _cmpkey(
- epoch, # type: int
- release, # type: Tuple[int, ...]
- pre, # type: Optional[Tuple[str, int]]
- post, # type: Optional[Tuple[str, int]]
- dev, # type: Optional[Tuple[str, int]]
- local, # type: Optional[Tuple[SubLocalType]]
-):
- # type: (...) -> CmpKey
+ epoch: int,
+ release: Tuple[int, ...],
+ pre: Optional[Tuple[str, int]],
+ post: Optional[Tuple[str, int]],
+ dev: Optional[Tuple[str, int]],
+ local: Optional[Tuple[SubLocalType]],
+) -> CmpKey:
# When we compare a release version, we want to compare it with all of the
# trailing zeros removed. So we'll use a reverse the list, drop all the now
@@ -495,7 +464,7 @@ def _cmpkey(
# if there is not a pre or a post segment. If we have one of those then
# the normal sorting rules will handle this case correctly.
if pre is None and post is None and dev is not None:
- _pre = NegativeInfinity # type: PrePostDevType
+ _pre: PrePostDevType = NegativeInfinity
# Versions without a pre-release (except as noted above) should sort after
# those with one.
elif pre is None:
@@ -505,21 +474,21 @@ def _cmpkey(
# Versions without a post segment should sort before those with one.
if post is None:
- _post = NegativeInfinity # type: PrePostDevType
+ _post: PrePostDevType = NegativeInfinity
else:
_post = post
# Versions without a development segment should sort after those with one.
if dev is None:
- _dev = Infinity # type: PrePostDevType
+ _dev: PrePostDevType = Infinity
else:
_dev = dev
if local is None:
# Versions without a local segment should sort before those with one.
- _local = NegativeInfinity # type: LocalType
+ _local: LocalType = NegativeInfinity
else:
# Versions with a local segment need that segment parsed to implement
# the sorting rules in PEP440.
diff --git a/setuptools/_vendor/pyparsing-2.2.1.dist-info/DESCRIPTION.rst b/setuptools/_vendor/pyparsing-2.2.1.dist-info/DESCRIPTION.rst
new file mode 100644
index 00000000..e1187231
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/DESCRIPTION.rst
@@ -0,0 +1,3 @@
+UNKNOWN
+
+
diff --git a/setuptools/_vendor/pyparsing-2.2.1.dist-info/INSTALLER b/setuptools/_vendor/pyparsing-2.2.1.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/setuptools/_vendor/pyparsing-2.2.1.dist-info/LICENSE.txt b/setuptools/_vendor/pyparsing-2.2.1.dist-info/LICENSE.txt
new file mode 100644
index 00000000..bbc959e0
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/LICENSE.txt
@@ -0,0 +1,18 @@
+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/setuptools/_vendor/pyparsing-2.2.1.dist-info/METADATA b/setuptools/_vendor/pyparsing-2.2.1.dist-info/METADATA
new file mode 100644
index 00000000..a15c350e
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/METADATA
@@ -0,0 +1,30 @@
+Metadata-Version: 2.0
+Name: pyparsing
+Version: 2.2.1
+Summary: Python parsing module
+Home-page: https://github.com/pyparsing/pyparsing/
+Author: Paul McGuire
+Author-email: ptmcg@users.sourceforge.net
+License: MIT License
+Download-URL: https://pypi.org/project/pyparsing/
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Information Technology
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*
+
+UNKNOWN
+
+
diff --git a/setuptools/_vendor/pyparsing-2.2.1.dist-info/RECORD b/setuptools/_vendor/pyparsing-2.2.1.dist-info/RECORD
new file mode 100644
index 00000000..09cc30e3
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/RECORD
@@ -0,0 +1,11 @@
+__pycache__/pyparsing.cpython-310.pyc,,
+pyparsing-2.2.1.dist-info/DESCRIPTION.rst,sha256=OCTuuN6LcWulhHS3d5rfjdsQtW22n7HENFRh6jC6ego,10
+pyparsing-2.2.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+pyparsing-2.2.1.dist-info/LICENSE.txt,sha256=081Pq74Spe1XdwrGkewNKSqa078kLIh7UWI-wVjdj8I,1041
+pyparsing-2.2.1.dist-info/METADATA,sha256=I0jhx9vpUYlQXjn4gVDnFFoAt3nNrxwR4iuqA_pknYs,1091
+pyparsing-2.2.1.dist-info/RECORD,,
+pyparsing-2.2.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pyparsing-2.2.1.dist-info/WHEEL,sha256=kdsN-5OJAZIiHN-iO4Rhl82KyS0bDWf4uBwMbkNafr8,110
+pyparsing-2.2.1.dist-info/metadata.json,sha256=v1_77-dSdajUZSItSJg8Ov9M713STY3PzhyrRvs1ax4,1185
+pyparsing-2.2.1.dist-info/top_level.txt,sha256=eUOjGzJVhlQ3WS2rFAy2mN3LX_7FKTM5GSJ04jfnLmU,10
+pyparsing.py,sha256=tmrp-lu-qO1i75ZzIN5A12nKRRD1Cm4Vpk-5LR9rims,232055
diff --git a/setuptools/_vendor/pyparsing-2.2.1.dist-info/REQUESTED b/setuptools/_vendor/pyparsing-2.2.1.dist-info/REQUESTED
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/REQUESTED
diff --git a/setuptools/_vendor/pyparsing-2.2.1.dist-info/WHEEL b/setuptools/_vendor/pyparsing-2.2.1.dist-info/WHEEL
new file mode 100644
index 00000000..7332a419
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/WHEEL
@@ -0,0 +1,6 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.30.0)
+Root-Is-Purelib: true
+Tag: py2-none-any
+Tag: py3-none-any
+
diff --git a/setuptools/_vendor/pyparsing-2.2.1.dist-info/metadata.json b/setuptools/_vendor/pyparsing-2.2.1.dist-info/metadata.json
new file mode 100644
index 00000000..b760b766
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/metadata.json
@@ -0,0 +1 @@
+{"classifiers": ["Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7"], "download_url": "https://pypi.org/project/pyparsing/", "extensions": {"python.details": {"contacts": [{"email": "ptmcg@users.sourceforge.net", "name": "Paul McGuire", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}, "project_urls": {"Home": "https://github.com/pyparsing/pyparsing/"}}}, "generator": "bdist_wheel (0.30.0)", "license": "MIT License", "metadata_version": "2.0", "name": "pyparsing", "requires_python": ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*", "summary": "Python parsing module", "version": "2.2.1"} \ No newline at end of file
diff --git a/setuptools/_vendor/pyparsing-2.2.1.dist-info/top_level.txt b/setuptools/_vendor/pyparsing-2.2.1.dist-info/top_level.txt
new file mode 100644
index 00000000..210dfec5
--- /dev/null
+++ b/setuptools/_vendor/pyparsing-2.2.1.dist-info/top_level.txt
@@ -0,0 +1 @@
+pyparsing
diff --git a/setuptools/_vendor/pyparsing.py b/setuptools/_vendor/pyparsing.py
index 4cae7883..cf75e1e5 100644
--- a/setuptools/_vendor/pyparsing.py
+++ b/setuptools/_vendor/pyparsing.py
@@ -1625,7 +1625,7 @@ class ParserElement(object):
(see L{I{parseWithTabs}<parseWithTabs>})
- define your parse action using the full C{(s,loc,toks)} signature, and
reference the input string using the parse action's C{s} argument
- - explicitly expand the tabs in your input string before calling
+ - explictly expand the tabs in your input string before calling
C{parseString}
Example::
diff --git a/setuptools/_vendor/vendored.txt b/setuptools/_vendor/vendored.txt
index d9804741..8216ec99 100644
--- a/setuptools/_vendor/vendored.txt
+++ b/setuptools/_vendor/vendored.txt
@@ -1,4 +1,4 @@
-packaging==20.4
+packaging==21.2
pyparsing==2.2.1
ordered-set==3.1.1
more_itertools==8.8.0
diff --git a/setuptools/build_meta.py b/setuptools/build_meta.py
index 9dfb2f24..d0ac613b 100644
--- a/setuptools/build_meta.py
+++ b/setuptools/build_meta.py
@@ -33,6 +33,7 @@ import tokenize
import shutil
import contextlib
import tempfile
+import warnings
import setuptools
import distutils
@@ -118,6 +119,13 @@ def _open_setup_script(setup_script):
return getattr(tokenize, 'open', open)(setup_script)
+@contextlib.contextmanager
+def suppress_known_deprecation():
+ with warnings.catch_warnings():
+ warnings.filterwarnings('ignore', 'setup.py install is deprecated')
+ yield
+
+
class _BuildMetaBackend(object):
def _fix_config(self, config_settings):
@@ -218,8 +226,9 @@ class _BuildMetaBackend(object):
def build_wheel(self, wheel_directory, config_settings=None,
metadata_directory=None):
- return self._build_with_temp_dir(['bdist_wheel'], '.whl',
- wheel_directory, config_settings)
+ with suppress_known_deprecation():
+ return self._build_with_temp_dir(['bdist_wheel'], '.whl',
+ wheel_directory, config_settings)
def build_sdist(self, sdist_directory, config_settings=None):
return self._build_with_temp_dir(['sdist', '--formats', 'gztar'],
diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py
index 6a615433..c3fdc092 100644
--- a/setuptools/command/build_py.py
+++ b/setuptools/command/build_py.py
@@ -67,6 +67,16 @@ class build_py(orig.build_py):
self.analyze_manifest()
return list(map(self._get_pkg_data_files, self.packages or ()))
+ def get_data_files_without_manifest(self):
+ """
+ Generate list of ``(package,src_dir,build_dir,filenames)`` tuples,
+ but without triggering any attempt to analyze or build the manifest.
+ """
+ # Prevent eventual errors from unset `manifest_files`
+ # (that would otherwise be set by `analyze_manifest`)
+ self.__dict__.setdefault('manifest_files', {})
+ return list(map(self._get_pkg_data_files, self.packages or ()))
+
def _get_pkg_data_files(self, package):
# Locate package source directory
src_dir = self.get_package_dir(package)
diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py
index b88c3e9a..5fab0fdb 100644
--- a/setuptools/command/easy_install.py
+++ b/setuptools/command/easy_install.py
@@ -6,7 +6,7 @@ A tool for doing automatic download/extract/build of distutils-based Python
packages. For detailed documentation, see the accompanying EasyInstall.txt
file, or visit the `EasyInstall home page`__.
-__ https://setuptools.readthedocs.io/en/latest/deprecated/easy_install.html
+__ https://setuptools.pypa.io/en/latest/deprecated/easy_install.html
"""
@@ -17,10 +17,10 @@ from distutils.errors import (
DistutilsArgError, DistutilsOptionError,
DistutilsError, DistutilsPlatformError,
)
-from distutils.command.install import INSTALL_SCHEMES, SCHEME_KEYS
from distutils import log, dir_util
from distutils.command.build_scripts import first_line_re
from distutils.spawn import find_executable
+from distutils.command import install
import sys
import os
import zipimport
@@ -39,9 +39,10 @@ import subprocess
import shlex
import io
import configparser
+import sysconfig
-from sysconfig import get_config_vars, get_path
+from sysconfig import get_path
from setuptools import SetuptoolsDeprecationWarning
@@ -153,6 +154,12 @@ class easy_install(Command):
create_index = PackageIndex
def initialize_options(self):
+ warnings.warn(
+ "easy_install command is deprecated. "
+ "Use build and pip and other standards-based tools.",
+ EasyInstallDeprecationWarning,
+ )
+
# the --user option seems to be an opt-in one,
# so the default should be False.
self.user = 0
@@ -230,22 +237,28 @@ class easy_install(Command):
self.version and self._render_version()
py_version = sys.version.split()[0]
- prefix, exec_prefix = get_config_vars('prefix', 'exec_prefix')
- self.config_vars = {
+ self.config_vars = dict(sysconfig.get_config_vars())
+
+ self.config_vars.update({
'dist_name': self.distribution.get_name(),
'dist_version': self.distribution.get_version(),
'dist_fullname': self.distribution.get_fullname(),
'py_version': py_version,
- 'py_version_short': py_version[0:3],
- 'py_version_nodot': py_version[0] + py_version[2],
- 'sys_prefix': prefix,
- 'prefix': prefix,
- 'sys_exec_prefix': exec_prefix,
- 'exec_prefix': exec_prefix,
+ 'py_version_short': f'{sys.version_info.major}.{sys.version_info.minor}',
+ 'py_version_nodot': f'{sys.version_info.major}{sys.version_info.minor}',
+ 'sys_prefix': self.config_vars['prefix'],
+ 'sys_exec_prefix': self.config_vars['exec_prefix'],
# Only python 3.2+ has abiflags
'abiflags': getattr(sys, 'abiflags', ''),
- }
+ 'platlibdir': getattr(sys, 'platlibdir', 'lib'),
+ })
+ with contextlib.suppress(AttributeError):
+ # only for distutils outside stdlib
+ self.config_vars.update({
+ 'implementation_lower': install._get_implementation().lower(),
+ 'implementation': install._get_implementation(),
+ })
if site.ENABLE_USER_SITE:
self.config_vars['userbase'] = self.install_userbase
@@ -365,7 +378,7 @@ class easy_install(Command):
msg = "User base directory is not specified"
raise DistutilsPlatformError(msg)
self.install_base = self.install_platbase = self.install_userbase
- scheme_name = os.name.replace('posix', 'unix') + '_user'
+ scheme_name = f'{os.name}_user'
self.select_scheme(scheme_name)
def _expand_attrs(self, attrs):
@@ -513,7 +526,7 @@ class easy_install(Command):
For information on other options, you may wish to consult the
documentation at:
- https://setuptools.readthedocs.io/en/latest/deprecated/easy_install.html
+ https://setuptools.pypa.io/en/latest/deprecated/easy_install.html
Please make the appropriate changes for your system and try again.
""").lstrip() # noqa
@@ -705,13 +718,11 @@ class easy_install(Command):
return dist
def select_scheme(self, name):
- """Sets the install directories by applying the install schemes."""
- # it's the caller's problem if they supply a bad name!
- scheme = INSTALL_SCHEMES[name]
- for key in SCHEME_KEYS:
- attrname = 'install_' + key
- if getattr(self, attrname) is None:
- setattr(self, attrname, scheme[key])
+ try:
+ install._select_scheme(self, name)
+ except AttributeError:
+ # stdlib distutils
+ install.install.select_scheme(self, name.replace('posix', 'unix'))
# FIXME: 'easy_install.process_distribution' is too complex (12)
def process_distribution( # noqa: C901
@@ -1306,7 +1317,7 @@ class easy_install(Command):
* You can set up the installation directory to support ".pth" files by
using one of the approaches described here:
- https://setuptools.readthedocs.io/en/latest/deprecated/easy_install.html#custom-installation-locations
+ https://setuptools.pypa.io/en/latest/deprecated/easy_install.html#custom-installation-locations
Please make the appropriate changes for your system and try again.
@@ -1339,7 +1350,7 @@ class easy_install(Command):
if self.prefix:
# Set default install_dir/scripts from --prefix
- config_vars = config_vars.copy()
+ config_vars = dict(config_vars)
config_vars['base'] = self.prefix
scheme = self.INSTALL_SCHEMES.get(os.name, self.DEFAULT_SCHEME)
for attr, val in scheme.items():
@@ -1566,7 +1577,7 @@ class PthDistributions(Environment):
self.sitedirs = list(map(normalize_path, sitedirs))
self.basedir = normalize_path(os.path.dirname(self.filename))
self._load()
- Environment.__init__(self, [], None, None)
+ super().__init__([], None, None)
for path in yield_lines(self.paths):
list(map(self.add, find_distributions(path, True)))
diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py
index 18b81340..f2210292 100644
--- a/setuptools/command/egg_info.py
+++ b/setuptools/command/egg_info.py
@@ -608,6 +608,27 @@ class manifest_maker(sdist):
self.filelist.exclude_pattern(r'(^|' + sep + r')(RCS|CVS|\.svn)' + sep,
is_regex=1)
+ def _safe_data_files(self, build_py):
+ """
+ The parent class implementation of this method
+ (``sdist``) will try to include data files, which
+ might cause recursion problems when
+ ``include_package_data=True``.
+
+ Therefore, avoid triggering any attempt of
+ analyzing/building the manifest again.
+ """
+ if hasattr(build_py, 'get_data_files_without_manifest'):
+ return build_py.get_data_files_without_manifest()
+
+ warnings.warn(
+ "Custom 'build_py' does not implement "
+ "'get_data_files_without_manifest'.\nPlease extend command classes"
+ " from setuptools instead of distutils.",
+ SetuptoolsDeprecationWarning
+ )
+ return build_py.get_data_files()
+
def write_file(filename, contents):
"""Create a file with the specified name and write 'contents' (a
diff --git a/setuptools/command/install.py b/setuptools/command/install.py
index 72b9a3e4..35e54d20 100644
--- a/setuptools/command/install.py
+++ b/setuptools/command/install.py
@@ -30,6 +30,13 @@ class install(orig.install):
_nc = dict(new_commands)
def initialize_options(self):
+
+ warnings.warn(
+ "setup.py install is deprecated. "
+ "Use build and pip and other standards-based tools.",
+ setuptools.SetuptoolsDeprecationWarning,
+ )
+
orig.install.initialize_options(self)
self.old_and_unmanageable = None
self.single_version_externally_managed = None
diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py
index e8062f2e..0285b690 100644
--- a/setuptools/command/sdist.py
+++ b/setuptools/command/sdist.py
@@ -114,12 +114,15 @@ class sdist(sdist_add_defaults, orig.sdist):
def _safe_data_files(self, build_py):
"""
- Extracting data_files from build_py is known to cause
- infinite recursion errors when `include_package_data`
- is enabled, so suppress it in that case.
+ Since the ``sdist`` class is also used to compute the MANIFEST
+ (via :obj:`setuptools.command.egg_info.manifest_maker`),
+ there might be recursion problems when trying to obtain the list of
+ data_files and ``include_package_data=True`` (which in turn depends on
+ the files included in the MANIFEST).
+
+ To avoid that, ``manifest_maker`` should be able to overwrite this
+ method and avoid recursive attempts to build/analyze the MANIFEST.
"""
- if self.distribution.include_package_data:
- return ()
return build_py.data_files
def _add_data_files(self, data_files):
diff --git a/setuptools/config.py b/setuptools/config.py
index e3e44c25..b4e968e5 100644
--- a/setuptools/config.py
+++ b/setuptools/config.py
@@ -13,7 +13,7 @@ from glob import iglob
import contextlib
from distutils.errors import DistutilsOptionError, DistutilsFileError
-from setuptools.extern.packaging.version import LegacyVersion, parse
+from setuptools.extern.packaging.version import Version, InvalidVersion
from setuptools.extern.packaging.specifiers import SpecifierSet
@@ -585,7 +585,9 @@ class ConfigMetadataHandler(ConfigHandler):
version = version.strip()
# Be strict about versions loaded from file because it's easy to
# accidentally include newlines and other unintended content
- if isinstance(parse(version), LegacyVersion):
+ try:
+ Version(version)
+ except InvalidVersion:
tmpl = (
'Version loaded from {value} does not '
'comply with PEP 440: {version}'
diff --git a/setuptools/depends.py b/setuptools/depends.py
index 8be6928a..adffd12d 100644
--- a/setuptools/depends.py
+++ b/setuptools/depends.py
@@ -2,7 +2,8 @@ import sys
import marshal
import contextlib
import dis
-from distutils.version import StrictVersion
+
+from setuptools.extern.packaging import version
from ._imp import find_module, PY_COMPILED, PY_FROZEN, PY_SOURCE
from . import _imp
@@ -21,7 +22,7 @@ class Require:
attribute=None, format=None):
if format is None and requested_version is not None:
- format = StrictVersion
+ format = version.Version
if format is not None:
requested_version = format(requested_version)
@@ -40,7 +41,7 @@ class Require:
def version_ok(self, version):
"""Is 'version' sufficiently up-to-date?"""
return self.attribute is None or self.format is None or \
- str(version) != "unknown" and version >= self.requested_version
+ str(version) != "unknown" and self.format(version) >= self.requested_version
def get_version(self, paths=None, default="unknown"):
"""Get version number of installed module, 'None', or 'default'
@@ -78,7 +79,7 @@ class Require:
version = self.get_version(paths)
if version is None:
return False
- return self.version_ok(version)
+ return self.version_ok(str(version))
def maybe_close(f):
diff --git a/setuptools/dist.py b/setuptools/dist.py
index 61c1130b..77e63a8c 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -25,7 +25,6 @@ from email import message_from_file
from distutils.errors import DistutilsOptionError, DistutilsSetupError
from distutils.util import rfc822_escape
-from distutils.version import StrictVersion
from setuptools.extern import packaging
from setuptools.extern import ordered_set
@@ -39,6 +38,7 @@ from setuptools import windows_support
from setuptools.monkey import get_unpatched
from setuptools.config import parse_configuration
import pkg_resources
+from setuptools.extern.packaging import version
if TYPE_CHECKING:
from email.message import Message
@@ -55,7 +55,7 @@ def _get_unpatched(cls):
def get_metadata_version(self):
mv = getattr(self, 'metadata_version', None)
if mv is None:
- mv = StrictVersion('2.1')
+ mv = version.Version('2.1')
self.metadata_version = mv
return mv
@@ -103,7 +103,7 @@ def read_pkg_file(self, file):
"""Reads the metadata values from a file object."""
msg = message_from_file(file)
- self.metadata_version = StrictVersion(msg['metadata-version'])
+ self.metadata_version = version.Version(msg['metadata-version'])
self.name = _read_field_from_msg(msg, 'name')
self.version = _read_field_from_msg(msg, 'version')
self.description = _read_field_from_msg(msg, 'summary')
@@ -121,7 +121,10 @@ def read_pkg_file(self, file):
self.download_url = None
self.long_description = _read_field_unescaped_from_msg(msg, 'description')
- if self.long_description is None and self.metadata_version >= StrictVersion('2.1'):
+ if (
+ self.long_description is None and
+ self.metadata_version >= version.Version('2.1')
+ ):
self.long_description = _read_payload_from_msg(msg)
self.description = _read_field_from_msg(msg, 'summary')
@@ -132,7 +135,7 @@ def read_pkg_file(self, file):
self.classifiers = _read_list_from_msg(msg, 'classifier')
# PEP 314 - these fields only exist in 1.1
- if self.metadata_version == StrictVersion('1.1'):
+ if self.metadata_version == version.Version('1.1'):
self.requires = _read_list_from_msg(msg, 'requires')
self.provides = _read_list_from_msg(msg, 'provides')
self.obsoletes = _read_list_from_msg(msg, 'obsoletes')
@@ -145,11 +148,14 @@ def read_pkg_file(self, file):
def single_line(val):
- # quick and dirty validation for description pypa/setuptools#1390
+ """
+ Quick and dirty validation for Summary pypa/setuptools#1390.
+ """
if '\n' in val:
- # TODO after 2021-07-31: Replace with `raise ValueError("newlines not allowed")`
+ # TODO: Replace with `raise ValueError("newlines not allowed")`
+ # after reviewing #2893.
warnings.warn("newlines not allowed and will break in the future")
- val = val.replace('\n', ' ')
+ val = val.strip().split('\n')[0]
return val
diff --git a/setuptools/errors.py b/setuptools/errors.py
index 2701747f..f4d35a63 100644
--- a/setuptools/errors.py
+++ b/setuptools/errors.py
@@ -3,6 +3,7 @@
Provides exceptions used by setuptools modules.
"""
+from distutils import errors as _distutils_errors
from distutils.errors import DistutilsError
@@ -14,3 +15,26 @@ class RemovedCommandError(DistutilsError, RuntimeError):
error is raised if a command exists in ``distutils`` but has been actively
removed in ``setuptools``.
"""
+
+
+# Re-export errors from distutils to facilitate the migration to PEP632
+
+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 = _distutils_errors.DistutilsError
diff --git a/setuptools/extension.py b/setuptools/extension.py
index 1820722a..f696c9c1 100644
--- a/setuptools/extension.py
+++ b/setuptools/extension.py
@@ -34,7 +34,7 @@ class Extension(_Extension):
# 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 = kw.pop("py_limited_api", False)
- _Extension.__init__(self, name, sources, *args, **kw)
+ super().__init__(name, sources, *args, **kw)
def _convert_pyx_sources_to_lang(self):
"""
diff --git a/setuptools/installer.py b/setuptools/installer.py
index 57e2b587..b7096df1 100644
--- a/setuptools/installer.py
+++ b/setuptools/installer.py
@@ -3,11 +3,13 @@ import os
import subprocess
import sys
import tempfile
+import warnings
from distutils import log
from distutils.errors import DistutilsError
import pkg_resources
from setuptools.wheel import Wheel
+from ._deprecation_warning import SetuptoolsDeprecationWarning
def _fixup_find_links(find_links):
@@ -22,6 +24,11 @@ def fetch_build_egg(dist, req): # noqa: C901 # is too complex (16) # FIXME
"""Fetch an egg needed for building.
Use pip/wheel to fetch/build a wheel."""
+ warnings.warn(
+ "setuptools.installer is deprecated. Requirements should "
+ "be satisfied by a PEP 517 installer.",
+ SetuptoolsDeprecationWarning,
+ )
# Warn if wheel is not available
try:
pkg_resources.get_distribution('wheel')
diff --git a/setuptools/logging.py b/setuptools/logging.py
new file mode 100644
index 00000000..15b57613
--- /dev/null
+++ b/setuptools/logging.py
@@ -0,0 +1,36 @@
+import sys
+import logging
+import distutils.log
+from . import monkey
+
+
+def _not_warning(record):
+ return record.levelno < logging.WARNING
+
+
+def configure():
+ """
+ Configure logging to emit warning and above to stderr
+ and everything else to stdout. This behavior is provided
+ for compatibilty with distutils.log but may change in
+ the future.
+ """
+ err_handler = logging.StreamHandler()
+ err_handler.setLevel(logging.WARNING)
+ out_handler = logging.StreamHandler(sys.stdout)
+ out_handler.addFilter(_not_warning)
+ handlers = err_handler, out_handler
+ logging.basicConfig(
+ format="{message}", style='{', handlers=handlers, level=logging.DEBUG)
+ monkey.patch_func(set_threshold, distutils.log, 'set_threshold')
+
+ # For some reason `distutils.log` module is getting cached in `distutils.dist`
+ # and then loaded again when patched,
+ # implying: id(distutils.log) != id(distutils.dist.log).
+ # Make sure the same module object is used everywhere:
+ distutils.dist.log = distutils.log
+
+
+def set_threshold(level):
+ logging.root.setLevel(level*10)
+ return set_threshold.unpatched(level)
diff --git a/setuptools/package_index.py b/setuptools/package_index.py
index d818f44a..051e523a 100644
--- a/setuptools/package_index.py
+++ b/setuptools/package_index.py
@@ -21,7 +21,7 @@ import setuptools
from pkg_resources import (
CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST,
Environment, find_distributions, safe_name, safe_version,
- to_filename, Requirement, DEVELOP_DIST, EGG_DIST,
+ to_filename, Requirement, DEVELOP_DIST, EGG_DIST, parse_version,
)
from distutils import log
from distutils.errors import DistutilsError
@@ -285,7 +285,7 @@ class PackageIndex(Environment):
self, index_url="https://pypi.org/simple/", hosts=('*',),
ca_bundle=None, verify_ssl=True, *args, **kw
):
- Environment.__init__(self, *args, **kw)
+ super().__init__(*args, **kw)
self.index_url = index_url + "/" [:not index_url.endswith('/')]
self.scanned_urls = {}
self.fetched_urls = {}
@@ -294,6 +294,14 @@ class PackageIndex(Environment):
self.to_scan = []
self.opener = urllib.request.urlopen
+ def add(self, dist):
+ # ignore invalid versions
+ try:
+ parse_version(dist.version)
+ except Exception:
+ return
+ return super().add(dist)
+
# FIXME: 'PackageIndex.process_url' is too complex (14)
def process_url(self, url, retrieve=False): # noqa: C901
"""Evaluate a URL as a possible download, and maybe retrieve it"""
@@ -994,7 +1002,7 @@ class PyPIConfig(configparser.RawConfigParser):
Load from ~/.pypirc
"""
defaults = dict.fromkeys(['username', 'password', 'repository'], '')
- configparser.RawConfigParser.__init__(self, defaults)
+ super().__init__(defaults)
rc = os.path.join(os.path.expanduser('~'), '.pypirc')
if os.path.exists(rc):
diff --git a/setuptools/tests/contexts.py b/setuptools/tests/contexts.py
index 51ce8984..5316e599 100644
--- a/setuptools/tests/contexts.py
+++ b/setuptools/tests/contexts.py
@@ -7,6 +7,7 @@ import site
import io
import pkg_resources
+from filelock import FileLock
@contextlib.contextmanager
@@ -96,3 +97,15 @@ def suppress_exceptions(*excs):
yield
except excs:
pass
+
+
+@contextlib.contextmanager
+def session_locked_tmp_dir(tmp_path_factory, name):
+ """Uses a file lock to guarantee only one worker can access a temp dir"""
+ root_tmp_dir = tmp_path_factory.getbasetemp().parent
+ # ^-- get the temp directory shared by all workers
+ locked_dir = root_tmp_dir / name
+ with FileLock(locked_dir.with_suffix(".lock")):
+ # ^-- prevent multiple workers to access the directory at once
+ locked_dir.mkdir(exist_ok=True, parents=True)
+ yield locked_dir
diff --git a/setuptools/tests/environment.py b/setuptools/tests/environment.py
index c0274c33..a0c0ec6e 100644
--- a/setuptools/tests/environment.py
+++ b/setuptools/tests/environment.py
@@ -1,9 +1,25 @@
import os
import sys
+import subprocess
import unicodedata
-
from subprocess import Popen as _Popen, PIPE as _PIPE
+import jaraco.envs
+
+
+class VirtualEnv(jaraco.envs.VirtualEnv):
+ name = '.env'
+ # Some version of PyPy will import distutils on startup, implicitly
+ # importing setuptools, and thus leading to BackendInvalid errors
+ # when upgrading Setuptools. Bypass this behavior by avoiding the
+ # early availability and need to upgrade.
+ create_opts = ['--no-setuptools']
+
+ def run(self, cmd, *args, **kwargs):
+ cmd = [self.exe(cmd[0])] + cmd[1:]
+ kwargs = {"cwd": self.root, **kwargs} # Allow overriding
+ return subprocess.check_output(cmd, *args, **kwargs)
+
def _which_dirs(cmd):
result = set()
diff --git a/setuptools/tests/fixtures.py b/setuptools/tests/fixtures.py
index a5a172e0..9b91d7d7 100644
--- a/setuptools/tests/fixtures.py
+++ b/setuptools/tests/fixtures.py
@@ -1,11 +1,11 @@
import contextlib
import sys
-import shutil
import subprocess
import pytest
+import path
-from . import contexts
+from . import contexts, environment
@pytest.fixture
@@ -28,22 +28,6 @@ def tmpdir_cwd(tmpdir):
yield orig
-@pytest.fixture
-def tmp_src(request, tmp_path):
- """Make a copy of the source dir under `$tmp/src`.
-
- This fixture is useful whenever it's necessary to run `setup.py`
- or `pip install` against the source directory when there's no
- control over the number of simultaneous invocations. Such
- concurrent runs create and delete directories with the same names
- under the target directory and so they influence each other's runs
- when they are not being executed sequentially.
- """
- tmp_src_path = tmp_path / 'src'
- shutil.copytree(request.config.rootdir, tmp_src_path)
- return tmp_src_path
-
-
@pytest.fixture(autouse=True, scope="session")
def workaround_xdist_376(request):
"""
@@ -72,3 +56,64 @@ def sample_project(tmp_path):
except Exception:
pytest.skip("Unable to clone sampleproject")
return tmp_path / 'sampleproject'
+
+
+# sdist and wheel artifacts should be stable across a round of tests
+# so we can build them once per session and use the files as "readonly"
+
+
+@pytest.fixture(scope="session")
+def setuptools_sdist(tmp_path_factory, request):
+ with contexts.session_locked_tmp_dir(tmp_path_factory, "sdist_build") as tmp:
+ dist = next(tmp.glob("*.tar.gz"), None)
+ if dist:
+ return dist
+
+ subprocess.check_call([
+ sys.executable, "-m", "build", "--sdist",
+ "--outdir", str(tmp), str(request.config.rootdir)
+ ])
+ return next(tmp.glob("*.tar.gz"))
+
+
+@pytest.fixture(scope="session")
+def setuptools_wheel(tmp_path_factory, request):
+ with contexts.session_locked_tmp_dir(tmp_path_factory, "wheel_build") as tmp:
+ dist = next(tmp.glob("*.whl"), None)
+ if dist:
+ return dist
+
+ subprocess.check_call([
+ sys.executable, "-m", "build", "--wheel",
+ "--outdir", str(tmp) , str(request.config.rootdir)
+ ])
+ return next(tmp.glob("*.whl"))
+
+
+@pytest.fixture
+def venv(tmp_path, setuptools_wheel):
+ """Virtual env with the version of setuptools under test installed"""
+ env = environment.VirtualEnv()
+ env.root = path.Path(tmp_path / 'venv')
+ env.req = str(setuptools_wheel)
+ return env.create()
+
+
+@pytest.fixture
+def venv_without_setuptools(tmp_path):
+ """Virtual env without any version of setuptools installed"""
+ env = environment.VirtualEnv()
+ env.root = path.Path(tmp_path / 'venv_without_setuptools')
+ env.create_opts = ['--no-setuptools']
+ env.ensure_env()
+ return env
+
+
+@pytest.fixture
+def bare_venv(tmp_path):
+ """Virtual env without any common packages installed"""
+ env = environment.VirtualEnv()
+ env.root = path.Path(tmp_path / 'bare_venv')
+ env.create_opts = ['--no-setuptools', '--no-pip', '--no-wheel', '--no-seed']
+ env.ensure_env()
+ return env
diff --git a/setuptools/tests/integration/__init__.py b/setuptools/tests/integration/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/setuptools/tests/integration/__init__.py
diff --git a/setuptools/tests/integration/helpers.py b/setuptools/tests/integration/helpers.py
new file mode 100644
index 00000000..43f43902
--- /dev/null
+++ b/setuptools/tests/integration/helpers.py
@@ -0,0 +1,61 @@
+"""Reusable functions and classes for different types of integration tests.
+
+For example ``Archive`` can be used to check the contents of distribution built
+with setuptools, and ``run`` will always try to be as verbose as possible to
+facilitate debugging.
+"""
+import os
+import subprocess
+import tarfile
+from zipfile import ZipFile
+
+
+def run(cmd, env=None):
+ r = subprocess.run(
+ cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ universal_newlines=True,
+ env={**os.environ, **(env or {})}
+ # ^-- allow overwriting instead of discarding the current env
+ )
+
+ out = r.stdout + "\n" + r.stderr
+ # pytest omits stdout/err by default, if the test fails they help debugging
+ print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
+ print(f"Command: {cmd}\nreturn code: {r.returncode}\n\n{out}")
+
+ if r.returncode == 0:
+ return out
+ raise subprocess.CalledProcessError(r.returncode, cmd, r.stdout, r.stderr)
+
+
+class Archive:
+ """Compatibility layer for ZipFile/Info and TarFile/Info"""
+ def __init__(self, filename):
+ self._filename = filename
+ if filename.endswith("tar.gz"):
+ self._obj = tarfile.open(filename, "r:gz")
+ elif filename.endswith("zip"):
+ self._obj = ZipFile(filename)
+ else:
+ raise ValueError(f"{filename} doesn't seem to be a zip or tar.gz")
+
+ def __iter__(self):
+ if hasattr(self._obj, "infolist"):
+ return iter(self._obj.infolist())
+ return iter(self._obj)
+
+ def get_name(self, zip_or_tar_info):
+ if hasattr(zip_or_tar_info, "filename"):
+ return zip_or_tar_info.filename
+ return zip_or_tar_info.name
+
+ def get_content(self, zip_or_tar_info):
+ if hasattr(self._obj, "extractfile"):
+ content = self._obj.extractfile(zip_or_tar_info)
+ if content is None:
+ msg = f"Invalid {zip_or_tar_info.name} in {self._filename}"
+ raise ValueError(msg)
+ return str(content.read(), "utf-8")
+ return str(self._obj.read(zip_or_tar_info), "utf-8")
diff --git a/setuptools/tests/integration/test_pip_install_sdist.py b/setuptools/tests/integration/test_pip_install_sdist.py
new file mode 100644
index 00000000..86cc4235
--- /dev/null
+++ b/setuptools/tests/integration/test_pip_install_sdist.py
@@ -0,0 +1,218 @@
+"""Integration tests for setuptools that focus on building packages via pip.
+
+The idea behind these tests is not to exhaustively check all the possible
+combinations of packages, operating systems, supporting libraries, etc, but
+rather check a limited number of popular packages and how they interact with
+the exposed public API. This way if any change in API is introduced, we hope to
+identify backward compatibility problems before publishing a release.
+
+The number of tested packages is purposefully kept small, to minimise duration
+and the associated maintenance cost (changes in the way these packages define
+their build process may require changes in the tests).
+"""
+import json
+import os
+import shutil
+import sys
+from enum import Enum
+from glob import glob
+from hashlib import md5
+from urllib.request import urlopen
+
+import pytest
+from packaging.requirements import Requirement
+
+from .helpers import Archive, run
+
+
+pytestmark = pytest.mark.integration
+
+LATEST, = list(Enum("v", "LATEST"))
+"""Default version to be checked"""
+# There are positive and negative aspects of checking the latest version of the
+# packages.
+# The main positive aspect is that the latest version might have already
+# removed the use of APIs deprecated in previous releases of setuptools.
+
+
+# Packages to be tested:
+# (Please notice the test environment cannot support EVERY library required for
+# compiling binary extensions. In Ubuntu/Debian nomenclature, we only assume
+# that `build-essential`, `gfortran` and `libopenblas-dev` are installed,
+# due to their relevance to the numerical/scientific programming ecosystem)
+EXAMPLES = [
+ ("pandas", LATEST), # cython + custom build_ext
+ ("sphinx", LATEST), # custom setup.py
+ ("pip", LATEST), # just in case...
+ ("pytest", LATEST), # uses setuptools_scm
+ ("mypy", LATEST), # custom build_py + ext_modules
+
+ # --- Popular packages: https://hugovk.github.io/top-pypi-packages/ ---
+ ("botocore", LATEST),
+ ("kiwisolver", "1.3.2"), # build_ext, version pinned due to setup_requires
+ ("brotli", LATEST), # not in the list but used by urllib3
+
+ # When adding packages to this list, make sure they expose a `__version__`
+ # attribute, or modify the tests bellow
+]
+
+
+# Some packages have "optional" dependencies that modify their build behaviour
+# and are not listed in pyproject.toml, others still use `setup_requires`
+EXTRA_BUILD_DEPS = {
+ "sphinx": ("babel>=1.3",),
+ "kiwisolver": ("cppy>=1.1.0",)
+}
+
+
+VIRTUALENV = (sys.executable, "-m", "virtualenv")
+
+
+# By default, pip will try to build packages in isolation (PEP 517), which
+# means it will download the previous stable version of setuptools.
+# `pip` flags can avoid that (the version of setuptools under test
+# should be the one to be used)
+SDIST_OPTIONS = (
+ "--ignore-installed",
+ "--no-build-isolation",
+ # We don't need "--no-binary :all:" since we specify the path to the sdist.
+ # It also helps with performance, since dependencies can come from wheels.
+)
+# The downside of `--no-build-isolation` is that pip will not download build
+# dependencies. The test script will have to also handle that.
+
+
+@pytest.fixture
+def venv_python(tmp_path):
+ run([*VIRTUALENV, str(tmp_path / ".venv")])
+ possible_path = (str(p.parent) for p in tmp_path.glob(".venv/*/python*"))
+ return shutil.which("python", path=os.pathsep.join(possible_path))
+
+
+@pytest.fixture(autouse=True)
+def _prepare(tmp_path, venv_python, monkeypatch, request):
+ download_path = os.getenv("DOWNLOAD_PATH", str(tmp_path))
+ os.makedirs(download_path, exist_ok=True)
+
+ # Environment vars used for building some of the packages
+ monkeypatch.setenv("USE_MYPYC", "1")
+
+ def _debug_info():
+ # Let's provide the maximum amount of information possible in the case
+ # it is necessary to debug the tests directly from the CI logs.
+ print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
+ print("Temporary directory:")
+ map(print, tmp_path.glob("*"))
+ print("Virtual environment:")
+ run([venv_python, "-m", "pip", "freeze"])
+ request.addfinalizer(_debug_info)
+
+
+ALREADY_LOADED = ("pytest", "mypy") # loaded by pytest/pytest-enabler
+
+
+@pytest.mark.parametrize('package, version', EXAMPLES)
+def test_install_sdist(package, version, tmp_path, venv_python, setuptools_wheel):
+ venv_pip = (venv_python, "-m", "pip")
+ sdist = retrieve_sdist(package, version, tmp_path)
+ deps = build_deps(package, sdist)
+ if deps:
+ print("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
+ print("Dependencies:", deps)
+ run([*venv_pip, "install", *deps])
+
+ # Use a virtualenv to simulate PEP 517 isolation
+ # but install fresh setuptools wheel to ensure the version under development
+ run([*venv_pip, "install", "-I", setuptools_wheel])
+ run([*venv_pip, "install", *SDIST_OPTIONS, sdist])
+
+ # Execute a simple script to make sure the package was installed correctly
+ script = f"import {package}; print(getattr({package}, '__version__', 0))"
+ run([venv_python, "-c", script])
+
+
+# ---- Helper Functions ----
+
+
+def retrieve_sdist(package, version, tmp_path):
+ """Either use cached sdist file or download it from PyPI"""
+ # `pip download` cannot be used due to
+ # https://github.com/pypa/pip/issues/1884
+ # https://discuss.python.org/t/pep-625-file-name-of-a-source-distribution/4686
+ # We have to find the correct distribution file and download it
+ download_path = os.getenv("DOWNLOAD_PATH", str(tmp_path))
+ dist = retrieve_pypi_sdist_metadata(package, version)
+
+ # Remove old files to prevent cache to grow indefinitely
+ for file in glob(os.path.join(download_path, f"{package}*")):
+ if dist["filename"] != file:
+ os.unlink(file)
+
+ dist_file = os.path.join(download_path, dist["filename"])
+ if not os.path.exists(dist_file):
+ download(dist["url"], dist_file, dist["md5_digest"])
+ return dist_file
+
+
+def retrieve_pypi_sdist_metadata(package, version):
+ # https://warehouse.pypa.io/api-reference/json.html
+ id_ = package if version is LATEST else f"{package}/{version}"
+ with urlopen(f"https://pypi.org/pypi/{id_}/json") as f:
+ metadata = json.load(f)
+
+ if metadata["info"]["yanked"]:
+ raise ValueError(f"Release for {package} {version} was yanked")
+
+ version = metadata["info"]["version"]
+ release = metadata["releases"][version]
+ dists = [d for d in release if d["packagetype"] == "sdist"]
+ if len(dists) == 0:
+ raise ValueError(f"No sdist found for {package} {version}")
+
+ for dist in dists:
+ if dist["filename"].endswith(".tar.gz"):
+ return dist
+
+ # Not all packages are publishing tar.gz
+ return dist
+
+
+def download(url, dest, md5_digest):
+ with urlopen(url) as f:
+ data = f.read()
+
+ assert md5(data).hexdigest() == md5_digest
+
+ with open(dest, "wb") as f:
+ f.write(data)
+
+ assert os.path.exists(dest)
+
+
+def build_deps(package, sdist_file):
+ """Find out what are the build dependencies for a package.
+
+ We need to "manually" install them, since pip will not install build
+ deps with `--no-build-isolation`.
+ """
+ import tomli as toml
+
+ # delay importing, since pytest discovery phase may hit this file from a
+ # testenv without tomli
+
+ archive = Archive(sdist_file)
+ pyproject = _read_pyproject(archive)
+
+ info = toml.loads(pyproject)
+ deps = info.get("build-system", {}).get("requires", [])
+ deps += EXTRA_BUILD_DEPS.get(package, [])
+ # Remove setuptools from requirements (and deduplicate)
+ requirements = {Requirement(d).name: d for d in deps}
+ return [v for k, v in requirements.items() if k != "setuptools"]
+
+
+def _read_pyproject(archive):
+ for member in archive:
+ if os.path.basename(archive.get_name(member)) == "pyproject.toml":
+ return archive.get_content(member)
+ return ""
diff --git a/setuptools/tests/requirements.txt b/setuptools/tests/requirements.txt
deleted file mode 100644
index b2d84a94..00000000
--- a/setuptools/tests/requirements.txt
+++ /dev/null
@@ -1,14 +0,0 @@
-mock
-pytest-flake8
-flake8-2020; python_version>="3.6"
-virtualenv>=13.0.0
-pytest-virtualenv>=1.2.7
-pytest>=3.7
-wheel
-coverage>=4.5.1
-pytest-cov>=2.5.1
-paver; python_version>="3.6"
-futures; python_version=="2.7"
-pip>=19.1 # For proper file:// URLs support.
-jaraco.envs
-sphinx
diff --git a/setuptools/tests/test_bdist_deprecations.py b/setuptools/tests/test_bdist_deprecations.py
index 28482fd0..1a900c67 100644
--- a/setuptools/tests/test_bdist_deprecations.py
+++ b/setuptools/tests/test_bdist_deprecations.py
@@ -11,7 +11,7 @@ from setuptools import SetuptoolsDeprecationWarning
@pytest.mark.skipif(sys.platform == 'win32', reason='non-Windows only')
@mock.patch('distutils.command.bdist_rpm.bdist_rpm')
-def test_bdist_rpm_warning(distutils_cmd):
+def test_bdist_rpm_warning(distutils_cmd, tmpdir_cwd):
dist = Distribution(
dict(
script_name='setup.py',
diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py
index 70c5794c..c52072ac 100644
--- a/setuptools/tests/test_develop.py
+++ b/setuptools/tests/test_develop.py
@@ -6,11 +6,11 @@ import sys
import subprocess
import platform
import pathlib
-import textwrap
from setuptools.command import test
import pytest
+import pip_run.launch
from setuptools.command.develop import develop
from setuptools.dist import Distribution
@@ -166,21 +166,6 @@ class TestNamespaces:
with test.test.paths_on_pythonpath([str(target)]):
subprocess.check_call(pkg_resources_imp)
- @staticmethod
- def install_workaround(site_packages):
- site_packages.mkdir(parents=True)
- sc = site_packages / 'sitecustomize.py'
- sc.write_text(
- textwrap.dedent(
- """
- import site
- import pathlib
- here = pathlib.Path(__file__).parent
- site.addsitedir(str(here))
- """
- ).lstrip()
- )
-
@pytest.mark.xfail(
platform.python_implementation() == 'PyPy',
reason="Workaround fails on PyPy (why?)",
@@ -190,7 +175,6 @@ class TestNamespaces:
Editable install to a prefix should be discoverable.
"""
prefix = tmp_path / 'prefix'
- prefix.mkdir()
# figure out where pip will likely install the package
site_packages = prefix / next(
@@ -198,9 +182,10 @@ class TestNamespaces:
for path in sys.path
if 'site-packages' in path and path.startswith(sys.prefix)
)
+ site_packages.mkdir(parents=True)
- # install the workaround
- self.install_workaround(site_packages)
+ # install workaround
+ pip_run.launch.inject_sitecustomize(str(site_packages))
env = dict(os.environ, PYTHONPATH=str(site_packages))
cmd = [
@@ -219,6 +204,6 @@ class TestNamespaces:
# now run 'sample' with the prefix on the PYTHONPATH
bin = 'Scripts' if platform.system() == 'Windows' else 'bin'
exe = prefix / bin / 'sample'
- if sys.version_info < (3, 7) and platform.system() == 'Windows':
+ if sys.version_info < (3, 8) and platform.system() == 'Windows':
exe = str(exe)
subprocess.check_call([exe], env=env)
diff --git a/setuptools/tests/test_distutils_adoption.py b/setuptools/tests/test_distutils_adoption.py
index 0e89921c..366f2928 100644
--- a/setuptools/tests/test_distutils_adoption.py
+++ b/setuptools/tests/test_distutils_adoption.py
@@ -1,33 +1,15 @@
import os
import sys
import functools
-import subprocess
import platform
+import textwrap
import pytest
-import jaraco.envs
-import path
IS_PYPY = '__pypy__' in sys.builtin_module_names
-class VirtualEnv(jaraco.envs.VirtualEnv):
- name = '.env'
-
- def run(self, cmd, *args, **kwargs):
- cmd = [self.exe(cmd[0])] + cmd[1:]
- return subprocess.check_output(cmd, *args, cwd=self.root, **kwargs)
-
-
-@pytest.fixture
-def venv(tmp_path, tmp_src):
- env = VirtualEnv()
- env.root = path.Path(tmp_path / 'venv')
- env.req = str(tmp_src)
- return env.create()
-
-
def popen_text(call):
"""
Augment the Popen call with the parameters to ensure unicode text.
@@ -36,12 +18,35 @@ def popen_text(call):
if sys.version_info < (3, 7) else functools.partial(call, text=True)
+def win_sr(env):
+ """
+ On Windows, SYSTEMROOT must be present to avoid
+
+ > Fatal Python error: _Py_HashRandomization_Init: failed to
+ > get random numbers to initialize Python
+ """
+ if env is None:
+ return
+ if platform.system() == 'Windows':
+ env['SYSTEMROOT'] = os.environ['SYSTEMROOT']
+ return env
+
+
def find_distutils(venv, imports='distutils', env=None, **kwargs):
py_cmd = 'import {imports}; print(distutils.__file__)'.format(**locals())
cmd = ['python', '-c', py_cmd]
- if platform.system() == 'Windows':
- env['SYSTEMROOT'] = os.environ['SYSTEMROOT']
- return popen_text(venv.run)(cmd, env=env, **kwargs)
+ return popen_text(venv.run)(cmd, env=win_sr(env), **kwargs)
+
+
+def count_meta_path(venv, env=None):
+ py_cmd = textwrap.dedent(
+ """
+ import sys
+ is_distutils = lambda finder: finder.__class__.__name__ == "DistutilsMetaFinder"
+ print(len(list(filter(is_distutils, sys.meta_path))))
+ """)
+ cmd = ['python', '-c', py_cmd]
+ return int(popen_text(venv.run)(cmd, env=win_sr(env)))
def test_distutils_stdlib(venv):
@@ -50,6 +55,7 @@ def test_distutils_stdlib(venv):
"""
env = dict(SETUPTOOLS_USE_DISTUTILS='stdlib')
assert venv.name not in find_distutils(venv, env=env).split(os.sep)
+ assert count_meta_path(venv, env=env) == 0
def test_distutils_local_with_setuptools(venv):
@@ -59,6 +65,7 @@ def test_distutils_local_with_setuptools(venv):
env = dict(SETUPTOOLS_USE_DISTUTILS='local')
loc = find_distutils(venv, imports='setuptools, distutils', env=env)
assert venv.name in loc.split(os.sep)
+ assert count_meta_path(venv, env=env) <= 1
@pytest.mark.xfail('IS_PYPY', reason='pypy imports distutils on startup')
@@ -69,3 +76,20 @@ def test_distutils_local(venv):
"""
env = dict(SETUPTOOLS_USE_DISTUTILS='local')
assert venv.name in find_distutils(venv, env=env).split(os.sep)
+ assert count_meta_path(venv, env=env) <= 1
+
+
+def test_pip_import(venv):
+ """
+ Ensure pip can be imported.
+ Regression test for #3002.
+ """
+ cmd = ['python', '-c', 'import pip']
+ popen_text(venv.run)(cmd)
+
+
+def test_distutils_has_origin():
+ """
+ Distutils module spec should have an origin. #2990.
+ """
+ assert __import__('distutils').__spec__.origin
diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py
index 1e77a649..5831b267 100644
--- a/setuptools/tests/test_easy_install.py
+++ b/setuptools/tests/test_easy_install.py
@@ -17,6 +17,8 @@ import time
import re
import subprocess
import pathlib
+import warnings
+from collections import namedtuple
import pytest
from jaraco import path
@@ -1058,3 +1060,52 @@ class TestWindowsScriptWriter:
hdr = hdr.rstrip('\n')
# header should not start with an escaped quote
assert not hdr.startswith('\\"')
+
+
+VersionStub = namedtuple("VersionStub", "major, minor, micro, releaselevel, serial")
+
+
+def test_use_correct_python_version_string(tmpdir, tmpdir_cwd, monkeypatch):
+ # In issue #3001, easy_install wrongly uses the `python3.1` directory
+ # when the interpreter is `python3.10` and the `--user` option is given.
+ # See pypa/setuptools#3001.
+ dist = Distribution()
+ cmd = dist.get_command_obj('easy_install')
+ cmd.args = ['ok']
+ cmd.optimize = 0
+ cmd.user = True
+ cmd.install_userbase = str(tmpdir)
+ cmd.install_usersite = None
+ install_cmd = dist.get_command_obj('install')
+ install_cmd.install_userbase = str(tmpdir)
+ install_cmd.install_usersite = None
+
+ with monkeypatch.context() as patch, warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+ version = '3.10.1 (main, Dec 21 2021, 09:17:12) [GCC 10.2.1 20210110]'
+ info = VersionStub(3, 10, 1, "final", 0)
+ patch.setattr('site.ENABLE_USER_SITE', True)
+ patch.setattr('sys.version', version)
+ patch.setattr('sys.version_info', info)
+ patch.setattr(cmd, 'create_home_path', mock.Mock())
+ cmd.finalize_options()
+
+ name = "pypy" if hasattr(sys, 'pypy_version_info') else "python"
+ install_dir = cmd.install_dir.lower()
+
+ # In some platforms (e.g. Windows), install_dir is mostly determined
+ # via `sysconfig`, which define constants eagerly at module creation.
+ # This means that monkeypatching `sys.version` to emulate 3.10 for testing
+ # may have no effect.
+ # The safest test here is to rely on the fact that 3.1 is no longer
+ # supported/tested, and make sure that if 'python3.1' ever appears in the string
+ # it is followed by another digit (e.g. 'python3.10').
+ if re.search(name + r'3\.?1', install_dir):
+ assert re.search(name + r'3\.?1\d', install_dir)
+
+ # The following "variables" are used for interpolation in distutils
+ # installation schemes, so it should be fair to treat them as "semi-public",
+ # or at least public enough so we can have a test to make sure they are correct
+ assert cmd.config_vars['py_version'] == '3.10.1'
+ assert cmd.config_vars['py_version_short'] == '3.10'
+ assert cmd.config_vars['py_version_nodot'] == '310'
diff --git a/setuptools/tests/test_logging.py b/setuptools/tests/test_logging.py
new file mode 100644
index 00000000..a5ddd56d
--- /dev/null
+++ b/setuptools/tests/test_logging.py
@@ -0,0 +1,36 @@
+import logging
+
+import pytest
+
+
+setup_py = """\
+from setuptools import setup
+
+setup(
+ name="test_logging",
+ version="0.0"
+)
+"""
+
+
+@pytest.mark.parametrize(
+ "flag, expected_level", [("--dry-run", "INFO"), ("--verbose", "DEBUG")]
+)
+def test_verbosity_level(tmp_path, monkeypatch, flag, expected_level):
+ """Make sure the correct verbosity level is set (issue #3038)"""
+ import setuptools # noqa: Import setuptools to monkeypatch distutils
+ import distutils # <- load distutils after all the patches take place
+
+ logger = logging.Logger(__name__)
+ monkeypatch.setattr(logging, "root", logger)
+ unset_log_level = logger.getEffectiveLevel()
+ assert logging.getLevelName(unset_log_level) == "NOTSET"
+
+ setup_script = tmp_path / "setup.py"
+ setup_script.write_text(setup_py)
+ dist = distutils.core.run_setup(setup_script, stop_after="init")
+ dist.script_args = [flag, "sdist"]
+ dist.parse_command_line() # <- where the log level is set
+ log_level = logger.getEffectiveLevel()
+ log_level_name = logging.getLevelName(log_level)
+ assert log_level_name == expected_level
diff --git a/setuptools/tests/test_sdist.py b/setuptools/tests/test_sdist.py
index 049fdcc0..66f46ad0 100644
--- a/setuptools/tests/test_sdist.py
+++ b/setuptools/tests/test_sdist.py
@@ -6,10 +6,12 @@ import tempfile
import unicodedata
import contextlib
import io
+from unittest import mock
import pytest
import pkg_resources
+from setuptools import SetuptoolsDeprecationWarning
from setuptools.command.sdist import sdist
from setuptools.command.egg_info import manifest_maker
from setuptools.dist import Distribution
@@ -106,6 +108,13 @@ class TestSdistTest:
with tmpdir.as_cwd():
yield
+ def assert_package_data_in_manifest(self, cmd):
+ manifest = cmd.filelist.files
+ assert os.path.join('sdist_test', 'a.txt') in manifest
+ assert os.path.join('sdist_test', 'b.txt') in manifest
+ assert os.path.join('sdist_test', 'c.rst') not in manifest
+ assert os.path.join('d', 'e.dat') in manifest
+
def test_package_data_in_sdist(self):
"""Regression test for pull request #4: ensures that files listed in
package_data are included in the manifest even if they're not added to
@@ -120,11 +129,63 @@ class TestSdistTest:
with quiet():
cmd.run()
- manifest = cmd.filelist.files
- assert os.path.join('sdist_test', 'a.txt') in manifest
- assert os.path.join('sdist_test', 'b.txt') in manifest
- assert os.path.join('sdist_test', 'c.rst') not in manifest
- assert os.path.join('d', 'e.dat') in manifest
+ self.assert_package_data_in_manifest(cmd)
+
+ def test_package_data_and_include_package_data_in_sdist(self):
+ """
+ Ensure package_data and include_package_data work
+ together.
+ """
+ setup_attrs = {**SETUP_ATTRS, 'include_package_data': True}
+ assert setup_attrs['package_data']
+
+ dist = Distribution(setup_attrs)
+ dist.script_name = 'setup.py'
+ cmd = sdist(dist)
+ cmd.ensure_finalized()
+
+ with quiet():
+ cmd.run()
+
+ self.assert_package_data_in_manifest(cmd)
+
+ def test_custom_build_py(self):
+ """
+ Ensure projects defining custom build_py don't break
+ when creating sdists (issue #2849)
+ """
+ from distutils.command.build_py import build_py as OrigBuildPy
+
+ using_custom_command_guard = mock.Mock()
+
+ class CustomBuildPy(OrigBuildPy):
+ """
+ Some projects have custom commands inheriting from `distutils`
+ """
+
+ def get_data_files(self):
+ using_custom_command_guard()
+ return super().get_data_files()
+
+ setup_attrs = {**SETUP_ATTRS, 'include_package_data': True}
+ assert setup_attrs['package_data']
+
+ dist = Distribution(setup_attrs)
+ dist.script_name = 'setup.py'
+ cmd = sdist(dist)
+ cmd.ensure_finalized()
+
+ # Make sure we use the custom command
+ cmd.cmdclass = {'build_py': CustomBuildPy}
+ cmd.distribution.cmdclass = {'build_py': CustomBuildPy}
+ assert cmd.distribution.get_command_class('build_py') == CustomBuildPy
+
+ msg = "setuptools instead of distutils"
+ with quiet(), pytest.warns(SetuptoolsDeprecationWarning, match=msg):
+ cmd.run()
+
+ using_custom_command_guard.assert_called()
+ self.assert_package_data_in_manifest(cmd)
def test_setup_py_exists(self):
dist = Distribution(SETUP_ATTRS)
diff --git a/setuptools/tests/test_setuptools.py b/setuptools/tests/test_setuptools.py
index 42f8e18b..b97faf17 100644
--- a/setuptools/tests/test_setuptools.py
+++ b/setuptools/tests/test_setuptools.py
@@ -7,16 +7,23 @@ import distutils.cmd
from distutils.errors import DistutilsOptionError
from distutils.errors import DistutilsSetupError
from distutils.core import Extension
-from distutils.version import LooseVersion
+from zipfile import ZipFile
import pytest
+from setuptools.extern.packaging import version
+
import setuptools
import setuptools.dist
import setuptools.depends as dep
from setuptools.depends import Require
+@pytest.fixture(autouse=True)
+def isolated_dir(tmpdir_cwd):
+ yield
+
+
def makeSetup(**args):
"""Return distribution from 'setup(**args)', without executing commands"""
@@ -84,12 +91,12 @@ class TestDepends:
assert req.name == 'Json'
assert req.module == 'json'
- assert req.requested_version == '1.0.3'
+ assert req.requested_version == version.Version('1.0.3')
assert req.attribute == '__version__'
assert req.full_name() == 'Json-1.0.3'
from json import __version__
- assert req.get_version() == __version__
+ assert str(req.get_version()) == __version__
assert req.version_ok('1.0.9')
assert not req.version_ok('0.9.1')
assert not req.version_ok('unknown')
@@ -97,11 +104,6 @@ class TestDepends:
assert req.is_present()
assert req.is_current()
- req = Require('Json 3000', '03000', 'json', format=LooseVersion)
- assert req.is_present()
- assert not req.is_current()
- assert not req.version_ok('unknown')
-
req = Require('Do-what-I-mean', '1.0', 'd-w-i-m')
assert not req.is_present()
assert not req.is_current()
@@ -293,3 +295,11 @@ def test_findall_missing_symlink(tmpdir, can_symlink):
os.symlink('foo', 'bar')
found = list(setuptools.findall())
assert found == []
+
+
+def test_its_own_wheel_does_not_contain_tests(setuptools_wheel):
+ with ZipFile(setuptools_wheel) as zipfile:
+ contents = [f.replace(os.sep, '/') for f in zipfile.namelist()]
+
+ for member in contents:
+ assert '/tests/' not in member
diff --git a/setuptools/tests/test_test.py b/setuptools/tests/test_test.py
index d0a49461..530474d7 100644
--- a/setuptools/tests/test_test.py
+++ b/setuptools/tests/test_test.py
@@ -1,7 +1,5 @@
-from distutils import log
-import os
-
import pytest
+from jaraco import path
from setuptools.command.test import test
from setuptools.dist import Distribution
@@ -9,99 +7,34 @@ from setuptools.dist import Distribution
from .textwrap import DALS
-SETUP_PY = DALS(
- """
- from setuptools import setup
-
- setup(
- packages=['name', 'name.space', 'name.space.tests'],
- namespace_packages=['name'],
- test_suite='name.space.tests.test_suite',
- )
- """
-)
-
-NS_INIT = DALS(
- """
- # -*- coding: Latin-1 -*-
- # Söme Arbiträry Ünicode to test Distribute Issüé 310
- try:
- __import__('pkg_resources').declare_namespace(__name__)
- except ImportError:
- from pkgutil import extend_path
- __path__ = extend_path(__path__, __name__)
- """
-)
-
-TEST_PY = DALS(
- """
- import unittest
-
- class TestTest(unittest.TestCase):
- def test_test(self):
- print "Foo" # Should fail under Python 3
-
- test_suite = unittest.makeSuite(TestTest)
- """
-)
-
-
-@pytest.fixture
-def sample_test(tmpdir_cwd):
- os.makedirs('name/space/tests')
-
- # setup.py
- with open('setup.py', 'wt') as f:
- f.write(SETUP_PY)
-
- # name/__init__.py
- with open('name/__init__.py', 'wb') as f:
- f.write(NS_INIT.encode('Latin-1'))
-
- # name/space/__init__.py
- with open('name/space/__init__.py', 'wt') as f:
- f.write('#empty\n')
-
- # name/space/tests/__init__.py
- with open('name/space/tests/__init__.py', 'wt') as f:
- f.write(TEST_PY)
-
-
-@pytest.fixture
-def quiet_log():
- # Running some of the other tests will automatically
- # change the log level to info, messing our output.
- log.set_verbosity(0)
-
-
-@pytest.mark.usefixtures('tmpdir_cwd', 'quiet_log')
+@pytest.mark.usefixtures('tmpdir_cwd')
def test_tests_are_run_once(capfd):
params = dict(
packages=['dummy'],
)
- with open('setup.py', 'wt') as f:
- f.write('from setuptools import setup; setup(\n')
- for k, v in sorted(params.items()):
- f.write(' %s=%r,\n' % (k, v))
- f.write(')\n')
- os.makedirs('dummy')
- with open('dummy/__init__.py', 'wt'):
- pass
- with open('dummy/test_dummy.py', 'wt') as f:
- f.write(
- DALS(
+ files = {
+ 'setup.py':
+ 'from setuptools import setup; setup('
+ + ','.join(f'{name}={params[name]!r}' for name in params)
+ + ')',
+ 'dummy': {
+ '__init__.py': '',
+ 'test_dummy.py': DALS(
+ """
+ import unittest
+ class TestTest(unittest.TestCase):
+ def test_test(self):
+ print('Foo')
"""
- import unittest
- class TestTest(unittest.TestCase):
- def test_test(self):
- print('Foo')
- """
- )
- )
+ ),
+ },
+ }
+ path.build(files)
dist = Distribution(params)
dist.script_name = 'setup.py'
cmd = test(dist)
cmd.ensure_finalized()
cmd.run()
out, err = capfd.readouterr()
- assert out == 'Foo\n'
+ assert out.endswith('Foo\n')
+ assert len(out.split('Foo')) == 2
diff --git a/setuptools/tests/test_virtualenv.py b/setuptools/tests/test_virtualenv.py
index 399dbaf0..0ba89643 100644
--- a/setuptools/tests/test_virtualenv.py
+++ b/setuptools/tests/test_virtualenv.py
@@ -1,51 +1,34 @@
-import glob
import os
import sys
import itertools
+import subprocess
import pathlib
import pytest
-from pytest_fixture_config import yield_requires_config
-
-import pytest_virtualenv
+from . import contexts
from .textwrap import DALS
from .test_easy_install import make_nspkg_sdist
@pytest.fixture(autouse=True)
-def pytest_virtualenv_works(virtualenv):
+def pytest_virtualenv_works(venv):
"""
pytest_virtualenv may not work. if it doesn't, skip these
tests. See #1284.
"""
- venv_prefix = virtualenv.run(
- 'python -c "import sys; print(sys.prefix)"',
- capture=True,
- ).strip()
+ venv_prefix = venv.run(["python" , "-c", "import sys; print(sys.prefix)"]).strip()
if venv_prefix == sys.prefix:
pytest.skip("virtualenv is broken (see pypa/setuptools#1284)")
-@yield_requires_config(pytest_virtualenv.CONFIG, ['virtualenv_executable'])
-@pytest.fixture(scope='function')
-def bare_virtualenv():
- """ Bare virtualenv (no pip/setuptools/wheel).
- """
- with pytest_virtualenv.VirtualEnv(args=(
- '--no-wheel',
- '--no-pip',
- '--no-setuptools',
- )) as venv:
- yield venv
-
-
-def test_clean_env_install(bare_virtualenv, tmp_src):
+def test_clean_env_install(venv_without_setuptools, setuptools_wheel):
"""
Check setuptools can be installed in a clean environment.
"""
- bare_virtualenv.run(['python', 'setup.py', 'install'], cd=tmp_src)
+ cmd = ["python", "-m", "pip", "install", str(setuptools_wheel)]
+ venv_without_setuptools.run(cmd)
def _get_pip_versions():
@@ -74,18 +57,15 @@ def _get_pip_versions():
def skip_network(param):
return param if network else mark(param, pytest.mark.skip(reason="no network"))
- issue2599 = pytest.mark.skipif(
- sys.version_info > (3, 10),
- reason="pypa/setuptools#2599",
- )
-
network_versions = [
- mark('pip==9.0.3', issue2599),
- mark('pip==10.0.1', issue2599),
- mark('pip==18.1', issue2599),
- mark('pip==19.3.1', pytest.mark.xfail(reason='pypa/pip#6599')),
- 'pip==20.0.2',
- 'https://github.com/pypa/pip/archive/main.zip',
+ mark('pip<20', pytest.mark.xfail(reason='pypa/pip#6599')),
+ 'pip<20.1',
+ 'pip<21',
+ 'pip<22',
+ mark(
+ 'https://github.com/pypa/pip/archive/main.zip',
+ pytest.mark.xfail(reason='#2975'),
+ ),
]
versions = itertools.chain(
@@ -96,42 +76,36 @@ def _get_pip_versions():
return list(versions)
+@pytest.mark.skipif(
+ 'platform.python_implementation() == "PyPy"',
+ reason="https://github.com/pypa/setuptools/pull/2865#issuecomment-965834995",
+)
@pytest.mark.parametrize('pip_version', _get_pip_versions())
-def test_pip_upgrade_from_source(pip_version, tmp_src, virtualenv):
+def test_pip_upgrade_from_source(pip_version, venv_without_setuptools,
+ setuptools_wheel, setuptools_sdist):
"""
Check pip can upgrade setuptools from source.
"""
- # Install pip/wheel, and remove setuptools (as it
+ # Install pip/wheel, in a venv without setuptools (as it
# should not be needed for bootstraping from source)
- if pip_version is None:
- upgrade_pip = ()
- else:
- upgrade_pip = ('python -m pip install -U {pip_version} --retries=1',)
- virtualenv.run(' && '.join((
- 'pip uninstall -y setuptools',
- 'pip install -U wheel',
- ) + upgrade_pip).format(pip_version=pip_version))
- dist_dir = virtualenv.workspace
- # Generate source distribution / wheel.
- virtualenv.run(' && '.join((
- 'python setup.py -q sdist -d {dist}',
- 'python setup.py -q bdist_wheel -d {dist}',
- )).format(dist=dist_dir), cd=tmp_src)
- sdist = glob.glob(os.path.join(dist_dir, '*.zip'))[0]
- wheel = glob.glob(os.path.join(dist_dir, '*.whl'))[0]
- # Then update from wheel.
- virtualenv.run('pip install ' + wheel)
+ venv = venv_without_setuptools
+ venv.run(["pip", "install", "-U", "wheel"])
+ if pip_version is not None:
+ venv.run(["python", "-m", "pip", "install", "-U", pip_version, "--retries=1"])
+ with pytest.raises(subprocess.CalledProcessError):
+ # Meta-test to make sure setuptools is not installed
+ venv.run(["python", "-c", "import setuptools"])
+
+ # Then install from wheel.
+ venv.run(["pip", "install", str(setuptools_wheel)])
# And finally try to upgrade from source.
- virtualenv.run('pip install --no-cache-dir --upgrade ' + sdist)
+ venv.run(["pip", "install", "--no-cache-dir", "--upgrade", str(setuptools_sdist)])
-def _check_test_command_install_requirements(virtualenv, tmpdir, cwd):
+def _check_test_command_install_requirements(venv, tmpdir):
"""
Check the test command will install all required dependencies.
"""
- # Install setuptools.
- virtualenv.run('python setup.py develop', cd=cwd)
-
def sdist(distname, version):
dist_path = tmpdir.join('%s-%s.tar.gz' % (distname, version))
make_nspkg_sdist(str(dist_path), distname, version)
@@ -180,28 +154,24 @@ def _check_test_command_install_requirements(virtualenv, tmpdir, cwd):
open('success', 'w').close()
'''))
- # Run test command for test package.
- # use 'virtualenv.python' as workaround for man-group/pytest-plugins#166
- cmd = [virtualenv.python, 'setup.py', 'test', '-s', 'test']
- virtualenv.run(cmd, cd=str(tmpdir))
+
+ cmd = ["python", 'setup.py', 'test', '-s', 'test']
+ venv.run(cmd, cwd=str(tmpdir))
assert tmpdir.join('success').check()
-def test_test_command_install_requirements(virtualenv, tmpdir, request):
+def test_test_command_install_requirements(venv, tmpdir, tmpdir_cwd):
# Ensure pip/wheel packages are installed.
- virtualenv.run(
- "python -c \"__import__('pkg_resources').require(['pip', 'wheel'])\"")
- # uninstall setuptools so that 'setup.py develop' works
- virtualenv.run("python -m pip uninstall -y setuptools")
+ venv.run(["python", "-c", "__import__('pkg_resources').require(['pip', 'wheel'])"])
# disable index URL so bits and bobs aren't requested from PyPI
- virtualenv.env['PIP_NO_INDEX'] = '1'
- _check_test_command_install_requirements(virtualenv, tmpdir, request.config.rootdir)
+ with contexts.environment(PYTHONPATH=None, PIP_NO_INDEX="1"):
+ _check_test_command_install_requirements(venv, tmpdir)
-def test_no_missing_dependencies(bare_virtualenv, request):
+def test_no_missing_dependencies(bare_venv, request):
"""
Quick and dirty test to ensure all external dependencies are vendored.
"""
+ setuptools_dir = request.config.rootdir
for command in ('upload',): # sorted(distutils.command.__all__):
- cmd = ['python', 'setup.py', command, '-h']
- bare_virtualenv.run(cmd, cd=request.config.rootdir)
+ bare_venv.run(['python', 'setup.py', command, '-h'], cwd=setuptools_dir)
diff --git a/setuptools/tests/test_wheel.py b/setuptools/tests/test_wheel.py
index 7345b135..a15c3a46 100644
--- a/setuptools/tests/test_wheel.py
+++ b/setuptools/tests/test_wheel.py
@@ -148,6 +148,7 @@ def _check_wheel_install(filename, install_dir, install_tree_includes,
if requires_txt is None:
assert not dist.has_metadata('requires.txt')
else:
+ # Order must match to ensure reproducibility.
assert requires_txt == dist.get_metadata('requires.txt').lstrip()
@@ -420,6 +421,38 @@ WHEEL_INSTALL_TESTS = (
),
dict(
+ id='requires_ensure_order',
+ install_requires='''
+ foo
+ bar
+ baz
+ qux
+ ''',
+ extras_require={
+ 'extra': '''
+ foobar>3
+ barbaz>4
+ bazqux>5
+ quxzap>6
+ ''',
+ },
+ requires_txt=DALS(
+ '''
+ foo
+ bar
+ baz
+ qux
+
+ [extra]
+ foobar>3
+ barbaz>4
+ bazqux>5
+ quxzap>6
+ '''
+ ),
+ ),
+
+ dict(
id='namespace_package',
file_defs={
'foo': {
diff --git a/setuptools/wheel.py b/setuptools/wheel.py
index 0be811af..9819e8b9 100644
--- a/setuptools/wheel.py
+++ b/setuptools/wheel.py
@@ -136,13 +136,13 @@ class Wheel:
def raw_req(req):
req.marker = None
return str(req)
- install_requires = list(sorted(map(raw_req, dist.requires())))
+ install_requires = list(map(raw_req, dist.requires()))
extras_require = {
- extra: sorted(
+ extra: [
req
for req in map(raw_req, dist.requires((extra,)))
if req not in install_requires
- )
+ ]
for extra in dist.extras
}
os.rename(dist_info, egg_info)