Imported Upstream version 59.2.0 upstream/59.2.0
authorDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 18 Jul 2022 06:39:04 +0000 (15:39 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 18 Jul 2022 06:39:04 +0000 (15:39 +0900)
19 files changed:
CHANGES.rst
PKG-INFO
README.rst
docs/deprecated/distutils/_setuptools_disclaimer.rst
docs/deprecated/distutils/apiref.rst
docs/development/developer-guide.rst
docs/pkg_resources.rst
pkg_resources/__init__.py
setup.cfg
setuptools.egg-info/PKG-INFO
setuptools/_distutils/__init__.py
setuptools/_distutils/command/install.py
setuptools/_distutils/command/install_egg_info.py
setuptools/_distutils/cygwinccompiler.py
setuptools/_distutils/sysconfig.py
setuptools/_distutils/tests/test_cygwinccompiler.py
setuptools/_distutils/tests/test_install.py
setuptools/_distutils/util.py
setuptools/command/easy_install.py

index b87e436..3206647 100644 (file)
@@ -1,3 +1,12 @@
+v59.2.0
+-------
+
+
+Changes
+^^^^^^^
+* #2875: 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.
+
+
 v59.1.1
 -------
 
@@ -2273,7 +2282,7 @@ v30.3.0
 
 * #394 via #862: Added support for `declarative package
   config in a setup.cfg file
-  <https://setuptools.readthedocs.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files>`_.
+  <https://setuptools.pypa.io/en/latest/setuptools.html#configuring-setup-using-setup-cfg-files>`_.
 
 v30.2.1
 -------
@@ -2734,7 +2743,7 @@ v23.0.0
   about, please comment in the ticket.
 * #604: Removed docs building support. The project
   now relies on documentation hosted at
-  https://setuptools.readthedocs.io/.
+  https://setuptools.pypa.io/.
 
 v22.0.5
 -------
@@ -2884,7 +2893,7 @@ v20.6.0
   `semver <https://semver.org>`_ precisely.
   The 'v' prefix on version numbers now also allows
   version numbers to be referenced in the changelog,
-  e.g. http://setuptools.readthedocs.io/en/latest/history.html#v20-6-0.
+  e.g. http://setuptools.pypa.io/en/latest/history.html#v20-6-0.
 
 20.5
 ----
@@ -2964,7 +2973,7 @@ v20.6.0
 
 * Added support for using passwords from keyring in the upload
   command. See `the upload docs
-  <https://setuptools.readthedocs.io/en/latest/setuptools.html#upload-upload-source-and-or-egg-distributions-to-pypi>`_
+  <https://setuptools.pypa.io/en/latest/setuptools.html#upload-upload-source-and-or-egg-distributions-to-pypi>`_
   for details.
 
 20.0
@@ -3718,7 +3727,7 @@ process to fail and PyPI uploads no longer accept files for 13.0.
 ---
 
 * Added a `Developer Guide
-  <https://setuptools.readthedocs.io/en/latest/developer-guide.html>`_ to the official
+  <https://setuptools.pypa.io/en/latest/developer-guide.html>`_ to the official
   documentation.
 * Some code refactoring and cleanup was done with no intended behavioral
   changes.
@@ -5425,7 +5434,7 @@ easy_install
  * ``setuptools`` now finds its commands, ``setup()`` argument validators, and
    metadata writers using entry points, so that they can be extended by
    third-party packages. See `Creating distutils Extensions
-   <https://setuptools.readthedocs.io/en/latest/setuptools.html#creating-distutils-extensions>`_
+   <https://setuptools.pypa.io/en/latest/setuptools.html#creating-distutils-extensions>`_
    for more details.
 
  * The vestigial ``depends`` command has been removed. It was never finished
index 2d7e3c8..1084e33 100644 (file)
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,12 +1,12 @@
 Metadata-Version: 2.1
 Name: setuptools
-Version: 59.1.1
+Version: 59.2.0
 Summary: Easily download, build, install, upgrade, and uninstall Python packages
 Home-page: https://github.com/pypa/setuptools
 Author: Python Packaging Authority
 Author-email: distutils-sig@python.org
 License: UNKNOWN
-Project-URL: Documentation, https://setuptools.readthedocs.io/
+Project-URL: Documentation, https://setuptools.pypa.io/
 Keywords: CPAN PyPI distutils eggs package management
 Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
@@ -47,7 +47,7 @@ License-File: LICENSE
    :alt: Code style: Black
 
 .. image:: https://img.shields.io/readthedocs/setuptools/latest.svg
-    :target: https://setuptools.readthedocs.io
+    :target: https://setuptools.pypa.io
 
 .. image:: https://img.shields.io/badge/skeleton-2021-informational
    :target: https://blog.jaraco.com/skeleton
index 6e7b95c..fab4111 100644 (file)
@@ -20,7 +20,7 @@
    :alt: Code style: Black
 
 .. image:: https://img.shields.io/readthedocs/setuptools/latest.svg
-    :target: https://setuptools.readthedocs.io
+    :target: https://setuptools.pypa.io
 
 .. image:: https://img.shields.io/badge/skeleton-2021-informational
    :target: https://blog.jaraco.com/skeleton
index cc75858..628c2e4 100644 (file)
@@ -1,5 +1,5 @@
 .. note::
 
    This document is being retained solely until the ``setuptools`` documentation
-   at https://setuptools.readthedocs.io/en/latest/setuptools.html
+   at https://setuptools.pypa.io/en/latest/setuptools.html
    independently covers all of the relevant information currently included here.
index f721fc1..f00ed74 100644 (file)
@@ -11,7 +11,7 @@ API Reference
       and other APIs, makes the API consistent across different Python versions,
       and is hence recommended over using ``distutils`` directly.
 
-.. _New and changed setup.py arguments in setuptools: https://setuptools.readthedocs.io/en/latest/setuptools.html#new-and-changed-setup-keywords
+.. _New and changed setup.py arguments in setuptools: https://setuptools.pypa.io/en/latest/setuptools.html#new-and-changed-setup-keywords
 
 .. include:: ./_setuptools_disclaimer.rst
 
index 052ca76..f29c1a8 100644 (file)
@@ -110,7 +110,7 @@ To build the docs locally, use tox::
     $ tox -e docs
 
 .. _Sphinx: http://www.sphinx-doc.org/en/master/
-.. _published documentation: https://setuptools.readthedocs.io/en/latest/
+.. _published documentation: https://setuptools.pypa.io/en/latest/
 
 ---------------------
 Vendored Dependencies
index fb5fc07..c115818 100644 (file)
@@ -1151,7 +1151,7 @@ paths.
     will be read as-is.
 
 ``resource_string(package_or_requirement, resource_name)``
-    Return the specified resource as a string.  The resource is read in
+    Return the specified resource as ``bytes``.  The resource is read in
     binary fashion, such that the returned string contains exactly the bytes
     that are stored in the resource.
 
index c615bc0..42129d5 100644 (file)
@@ -1484,7 +1484,7 @@ class NullProvider:
     def _validate_resource_path(path):
         """
         Validate the resource paths according to the docs.
-        https://setuptools.readthedocs.io/en/latest/pkg_resources.html#basic-resource-access
+        https://setuptools.pypa.io/en/latest/pkg_resources.html#basic-resource-access
 
         >>> warned = getfixture('recwarn')
         >>> warnings.simplefilter('always')
index 22c735a..2b528c8 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = setuptools
-version = 59.1.1
+version = 59.2.0
 author = Python Packaging Authority
 author_email = distutils-sig@python.org
 description = Easily download, build, install, upgrade, and uninstall Python packages
@@ -18,7 +18,7 @@ classifiers =
        Topic :: Utilities
 keywords = CPAN PyPI distutils eggs package management
 project_urls = 
-       Documentation = https://setuptools.readthedocs.io/
+       Documentation = https://setuptools.pypa.io/
 
 [options]
 packages = find_namespace:
index 2d7e3c8..1084e33 100644 (file)
@@ -1,12 +1,12 @@
 Metadata-Version: 2.1
 Name: setuptools
-Version: 59.1.1
+Version: 59.2.0
 Summary: Easily download, build, install, upgrade, and uninstall Python packages
 Home-page: https://github.com/pypa/setuptools
 Author: Python Packaging Authority
 Author-email: distutils-sig@python.org
 License: UNKNOWN
-Project-URL: Documentation, https://setuptools.readthedocs.io/
+Project-URL: Documentation, https://setuptools.pypa.io/
 Keywords: CPAN PyPI distutils eggs package management
 Platform: UNKNOWN
 Classifier: Development Status :: 5 - Production/Stable
@@ -47,7 +47,7 @@ License-File: LICENSE
    :alt: Code style: Black
 
 .. image:: https://img.shields.io/readthedocs/setuptools/latest.svg
-    :target: https://setuptools.readthedocs.io
+    :target: https://setuptools.pypa.io
 
 .. image:: https://img.shields.io/badge/skeleton-2021-informational
    :target: https://blog.jaraco.com/skeleton
index 7dac55b..8fd493b 100644 (file)
@@ -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
index e98f049..c756b6d 100644 (file)
@@ -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:
index 0ddc736..adc0323 100644 (file)
@@ -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):
index f1c38e3..f80ca62 100644 (file)
@@ -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)
 
index 8832b3e..d36d94f 100644 (file)
@@ -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:
index 9dc869d..2a02eed 100644 (file)
@@ -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():
index eb684a0..cce973d 100644 (file)
@@ -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'})
index afc23c4..ac6d446 100644 (file)
@@ -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,
index 1aed0e8..fc848d0 100644 (file)
@@ -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
 
 """
 
@@ -519,7 +519,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
@@ -1312,7 +1312,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.