diff options
author | Jason R. Coombs <jaraco@jaraco.com> | 2021-11-18 18:21:49 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-11-18 18:21:49 -0500 |
commit | d508e3841d3a40aeb602b0a9a883b227a27a3842 (patch) | |
tree | dc28abfe97d83d0d23e4b4f59b49c9becdf5e6aa | |
parent | 24c766c034c7eb3ed784033a19587d835a7f92a8 (diff) | |
parent | 4a8a79d722d4b29ab2a0fef88cf2c2230141547e (diff) | |
download | python-setuptools-git-d508e3841d3a40aeb602b0a9a883b227a27a3842.tar.gz |
Merge pull request #2875 from pypa/feature/local-schemes
Update distutils @514e9d0
-rw-r--r-- | changelog.d/2875.change.rst | 1 | ||||
-rw-r--r-- | setuptools/_distutils/__init__.py | 11 | ||||
-rw-r--r-- | setuptools/_distutils/command/install.py | 118 | ||||
-rw-r--r-- | setuptools/_distutils/command/install_egg_info.py | 15 | ||||
-rw-r--r-- | setuptools/_distutils/cygwinccompiler.py | 9 | ||||
-rw-r--r-- | setuptools/_distutils/sysconfig.py | 49 | ||||
-rw-r--r-- | setuptools/_distutils/tests/test_cygwinccompiler.py | 5 | ||||
-rw-r--r-- | setuptools/_distutils/tests/test_install.py | 2 | ||||
-rw-r--r-- | setuptools/_distutils/util.py | 39 |
9 files changed, 172 insertions, 77 deletions
diff --git a/changelog.d/2875.change.rst b/changelog.d/2875.change.rst new file mode 100644 index 00000000..a9d3a5f2 --- /dev/null +++ b/changelog.d/2875.change.rst @@ -0,0 +1 @@ +Introduce changes from pypa/distutils@514e9d0, including support for overrides from Debian and pkgsrc, unlocking the possibility of making SETUPTOOLS_USE_DISTUTILS=local the default again. 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/command/install.py b/setuptools/_distutils/command/install.py index e98f0491..c756b6db 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 @@ -20,62 +23,62 @@ 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/$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', + '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/$implementation_lower', - 'platlib': '$base/$platlibdir/$implementation_lower', - 'headers': '$base/include/$implementation_lower/$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/$implementation$py_version_nodot/Include/$dist_name', - 'scripts': '$userbase/$implementation$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/$implementation_lower$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 @@ -83,6 +86,31 @@ if HAS_USER_SITE: # and to SCHEME_KEYS here. 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' @@ -284,7 +312,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}") @@ -335,6 +363,8 @@ class install(Command): # everything else. self.config_vars['base'] = self.install_base self.config_vars['platbase'] = self.install_platbase + self.config_vars['installed_base'] = ( + sysconfig.get_config_vars()['installed_base']) if DEBUG: from pprint import pprint @@ -431,10 +461,10 @@ 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: @@ -450,7 +480,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""" @@ -462,7 +492,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) @@ -484,7 +514,7 @@ class install(Command): name = 'pypy_nt' else: name = 'pypy' - scheme = INSTALL_SCHEMES[name] + scheme = _load_schemes()[name] for key in SCHEME_KEYS: attrname = 'install_' + key if getattr(self, attrname) is None: 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/cygwinccompiler.py b/setuptools/_distutils/cygwinccompiler.py index f1c38e39..f80ca622 100644 --- a/setuptools/_distutils/cygwinccompiler.py +++ b/setuptools/_distutils/cygwinccompiler.py @@ -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) diff --git a/setuptools/_distutils/sysconfig.py b/setuptools/_distutils/sysconfig.py index 8832b3ec..d36d94f7 100644 --- a/setuptools/_distutils/sysconfig.py +++ b/setuptools/_distutils/sysconfig.py @@ -129,6 +129,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 +160,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 +179,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") @@ -273,14 +280,24 @@ def get_config_h_filename(): return os.path.join(inc_dir, 'pyconfig.h') +# Allow this value to be patched by pkgsrc. Ref pypa/distutils#16. +_makefile_tmpl = 'config-{python_ver}{build_flags}{multiarch}' + + 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 + multiarch = ( + '-%s' % sys.implementation._multiarch + if hasattr(sys.implementation, '_multiarch') else '' + ) + config_file = _makefile_tmpl.format( + python_ver=get_python_version(), + build_flags=build_flags, + multiarch=multiarch, + ) return os.path.join(lib_dir, config_file, 'Makefile') @@ -452,15 +469,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/test_cygwinccompiler.py b/setuptools/_distutils/tests/test_cygwinccompiler.py index 9dc869de..2a02eed4 100644 --- a/setuptools/_distutils/tests/test_cygwinccompiler.py +++ b/setuptools/_distutils/tests/test_cygwinccompiler.py @@ -141,10 +141,13 @@ 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(): diff --git a/setuptools/_distutils/tests/test_install.py b/setuptools/_distutils/tests/test_install.py index eb684a09..cce973dc 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'}) diff --git a/setuptools/_distutils/util.py b/setuptools/_distutils/util.py index afc23c4e..ac6d446d 100644 --- a/setuptools/_distutils/util.py +++ b/setuptools/_distutils/util.py @@ -242,30 +242,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, |