Imported Upstream version 62.1.0 upstream/62.1.0
authorJinWang An <jinwang.an@samsung.com>
Mon, 27 Mar 2023 08:02:48 +0000 (17:02 +0900)
committerJinWang An <jinwang.an@samsung.com>
Mon, 27 Mar 2023 08:02:48 +0000 (17:02 +0900)
13 files changed:
.bumpversion.cfg
CHANGES.rst
docs/userguide/dependency_management.rst
setup.cfg
setuptools/_distutils/command/build.py
setuptools/_distutils/py39compat.py [new file with mode: 0644]
setuptools/_distutils/sysconfig.py
setuptools/_distutils/tests/test_build.py
setuptools/_distutils/tests/test_install.py
setuptools/_distutils/tests/test_sysconfig.py
setuptools/_distutils/version.py
setuptools/config/expand.py
setuptools/tests/config/test_expand.py

index 5c2f2e45779d76a555dcb64496cceaedb0b4fac7..1125d38d3638ff2ab14046d2f4cbc37055413f4e 100644 (file)
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 62.0.0
+current_version = 62.1.0
 commit = True
 tag = True
 
index 126457be2cdcc530fc352d41e5991fcf564abf07..5061ecb999f01693bdf7db932143631b5baf41df 100644 (file)
@@ -1,3 +1,16 @@
+v62.1.0
+-------
+
+
+Changes
+^^^^^^^
+* #3258: Merge pypa/distutils@5229dad46b.
+
+Misc
+^^^^
+* #3249: Simplified ``package_dir`` obtained via auto-discovery.
+
+
 v62.0.0
 -------
 
index 279f794da127e90eb40d262760780eed09f560fe..d15b45cb6ebe574d47ab278afcd0e9b4db774251 100644 (file)
@@ -43,7 +43,7 @@ other two types of dependency keyword, this one is specified in your
 Declaring required dependency
 =============================
 This is where a package declares its core dependencies, without which it won't
-be able to run. ``setuptools`` support automatically download and install
+be able to run. ``setuptools`` supports automatically downloading and installing
 these dependencies when the package is installed. Although there is more
 finesse to it, let's start with a simple example.
 
@@ -90,7 +90,7 @@ that verify the availability of the specified dependencies at runtime.
 
 Platform specific dependencies
 ------------------------------
-Setuptools offer the capability to evaluate certain conditions before blindly
+Setuptools offers the capability to evaluate certain conditions before blindly
 installing everything listed in ``install_requires``. This is great for platform
 specific dependencies. For example, the ``enum`` package was added in Python
 3.4, therefore, package that depends on it can elect to install it only when
@@ -250,9 +250,9 @@ distributions, if the package's dependencies aren't already installed:
 Optional dependencies
 =====================
 Setuptools allows you to declare dependencies that only get installed under
-specific circumstances. These dependencies are specified with ``extras_require``
+specific circumstances. These dependencies are specified with the ``extras_require``
 keyword and are only installed if another package depends on it (either
-directly or indirectly) This makes it convenient to declare dependencies for
+directly or indirectly). This makes it convenient to declare dependencies for
 ancillary functions such as "tests" and "docs".
 
 .. note::
index 78c088a15be8372fd8e22635c28a88148f85dd0d..4b386243a47e3a9d233e7f17091e611beb04dbf0 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = setuptools
-version = 62.0.0
+version = 62.1.0
 author = Python Packaging Authority
 author_email = distutils-sig@python.org
 description = Easily download, build, install, upgrade, and uninstall Python packages
index 4355a63235c7b6d8a9b8389c3fc203b9b3529a36..9606b81a4645df9aff30d3fb33d7d34cd104a1ae 100644 (file)
@@ -81,7 +81,8 @@ class build(Command):
                             "--plat-name only supported on Windows (try "
                             "using './configure --help' on your platform)")
 
-        plat_specifier = ".%s-%d.%d" % (self.plat_name, *sys.version_info[:2])
+        plat_specifier = ".%s-%s" % (self.plat_name,
+                                     sys.implementation.cache_tag)
 
         # Make it so Python 2.x and Python 2.x with --with-pydebug don't
         # share the same build directories. Doing so confuses the build
diff --git a/setuptools/_distutils/py39compat.py b/setuptools/_distutils/py39compat.py
new file mode 100644 (file)
index 0000000..9de9501
--- /dev/null
@@ -0,0 +1,21 @@
+import sys
+import platform
+
+
+def add_ext_suffix_39(vars):
+    """
+    Ensure vars contains 'EXT_SUFFIX'. pypa/distutils#130
+    """
+    import _imp
+    ext_suffix = _imp.extension_suffixes()[0]
+    vars.update(
+        EXT_SUFFIX=ext_suffix,
+        # sysconfig sets SO to match EXT_SUFFIX, so maintain
+        # that expectation.
+        # https://github.com/python/cpython/blob/785cc6770588de087d09e89a69110af2542be208/Lib/sysconfig.py#L671-L673
+        SO=ext_suffix,
+    )
+
+
+needs_ext_suffix = sys.version_info < (3, 10) and platform.system() == 'Windows'
+add_ext_suffix = add_ext_suffix_39 if needs_ext_suffix else lambda vars: None
index 9fad3835a2f064c1e44e164094388f5a3635f1ca..55a42e169dc80decca130737ef595fa32abc559e 100644 (file)
@@ -9,13 +9,13 @@ Written by:   Fred L. Drake, Jr.
 Email:        <fdrake@acm.org>
 """
 
-import _imp
 import os
 import re
 import sys
 import sysconfig
 
 from .errors import DistutilsPlatformError
+from . import py39compat
 
 IS_PYPY = '__pypy__' in sys.builtin_module_names
 
@@ -48,6 +48,7 @@ def _is_python_source_dir(d):
             return True
     return False
 
+
 _sys_home = getattr(sys, '_home', None)
 
 if os.name == 'nt':
@@ -59,11 +60,13 @@ if os.name == 'nt':
     project_base = _fix_pcbuild(project_base)
     _sys_home = _fix_pcbuild(_sys_home)
 
+
 def _python_build():
     if _sys_home:
         return _is_python_source_dir(_sys_home)
     return _is_python_source_dir(project_base)
 
+
 python_build = _python_build()
 
 
@@ -79,6 +82,7 @@ except AttributeError:
     # this attribute, which is fine.
     pass
 
+
 def get_python_version():
     """Return a string containing the major and minor Python version,
     leaving off the patchlevel.  Sample return values could be '1.5'
@@ -192,7 +196,6 @@ def get_python_lib(plat_specific=0, standard_lib=0, prefix=None):
             "on platform '%s'" % os.name)
 
 
-
 def customize_compiler(compiler):
     """Do any platform-specific customization of a CCompiler instance.
 
@@ -217,8 +220,9 @@ def customize_compiler(compiler):
                 _config_vars['CUSTOMIZED_OSX_COMPILER'] = 'True'
 
         (cc, cxx, cflags, ccshared, ldshared, shlib_suffix, ar, ar_flags) = \
-            get_config_vars('CC', 'CXX', 'CFLAGS',
-                            'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS')
+            get_config_vars(
+                'CC', 'CXX', 'CFLAGS',
+                'CCSHARED', 'LDSHARED', 'SHLIB_SUFFIX', 'AR', 'ARFLAGS')
 
         if 'CC' in os.environ:
             newcc = os.environ['CC']
@@ -280,7 +284,6 @@ def get_config_h_filename():
         return sysconfig.get_config_h_filename()
 
 
-
 def get_makefile_filename():
     """Return full pathname of installed Makefile from the Python build."""
     return sysconfig.get_makefile_filename()
@@ -302,6 +305,7 @@ _variable_rx = re.compile(r"([a-zA-Z][a-zA-Z0-9_]+)\s*=\s*(.*)")
 _findvar1_rx = re.compile(r"\$\(([A-Za-z][A-Za-z0-9_]*)\)")
 _findvar2_rx = re.compile(r"\${([A-Za-z][A-Za-z0-9_]*)}")
 
+
 def parse_makefile(fn, g=None):
     """Parse a Makefile-style file.
 
@@ -310,7 +314,9 @@ def parse_makefile(fn, g=None):
     used instead of a new dictionary.
     """
     from distutils.text_file import TextFile
-    fp = TextFile(fn, strip_comments=1, skip_blanks=1, join_lines=1, errors="surrogateescape")
+    fp = TextFile(
+        fn, strip_comments=1, skip_blanks=1, join_lines=1,
+        errors="surrogateescape")
 
     if g is None:
         g = {}
@@ -319,7 +325,7 @@ def parse_makefile(fn, g=None):
 
     while True:
         line = fp.readline()
-        if line is None: # eof
+        if line is None:  # eof
             break
         m = _variable_rx.match(line)
         if m:
@@ -363,7 +369,8 @@ def parse_makefile(fn, g=None):
                     item = os.environ[n]
 
                 elif n in renamed_variables:
-                    if name.startswith('PY_') and name[3:] in renamed_variables:
+                    if name.startswith('PY_') and \
+                            name[3:] in renamed_variables:
                         item = ""
 
                     elif 'PY_' + n in notdone:
@@ -379,7 +386,8 @@ def parse_makefile(fn, g=None):
                     if "$" in after:
                         notdone[name] = value
                     else:
-                        try: value = int(value)
+                        try:
+                            value = int(value)
                         except ValueError:
                             done[name] = value.strip()
                         else:
@@ -387,7 +395,7 @@ def parse_makefile(fn, g=None):
                         del notdone[name]
 
                         if name.startswith('PY_') \
-                            and name[3:] in renamed_variables:
+                                and name[3:] in renamed_variables:
 
                             name = name[3:]
                             if name not in done:
@@ -449,6 +457,7 @@ def get_config_vars(*args):
     global _config_vars
     if _config_vars is None:
         _config_vars = sysconfig.get_config_vars().copy()
+        py39compat.add_ext_suffix(_config_vars)
 
     if args:
         vals = []
@@ -458,6 +467,7 @@ def get_config_vars(*args):
     else:
         return _config_vars
 
+
 def get_config_var(name):
     """Return the value of a single variable using the dictionary
     returned by 'get_config_vars()'.  Equivalent to
@@ -465,5 +475,6 @@ def get_config_var(name):
     """
     if name == 'SO':
         import warnings
-        warnings.warn('SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2)
+        warnings.warn(
+            'SO is deprecated, use EXT_SUFFIX', DeprecationWarning, 2)
     return get_config_vars().get(name)
index 83a9e4f4dd2f43b7fe93ba05d165757c1201b949..937244195bf798a0891d29695cf93959aecfe95f 100644 (file)
@@ -24,10 +24,10 @@ class BuildTestCase(support.TempdirManager,
         wanted = os.path.join(cmd.build_base, 'lib')
         self.assertEqual(cmd.build_purelib, wanted)
 
-        # build_platlib is 'build/lib.platform-x.x[-pydebug]'
+        # build_platlib is 'build/lib.platform-cache_tag[-pydebug]'
         # examples:
-        #   build/lib.macosx-10.3-i386-2.7
-        plat_spec = '.%s-%d.%d' % (cmd.plat_name, *sys.version_info[:2])
+        #   build/lib.macosx-10.3-i386-cpython39
+        plat_spec = '.%s-%s' % (cmd.plat_name, sys.implementation.cache_tag)
         if hasattr(sys, 'gettotalrefcount'):
             self.assertTrue(cmd.build_platlib.endswith('-pydebug'))
             plat_spec += '-pydebug'
index 5dbc06b0902d53beae6f179b2aa4ad2744241aac..3aef9e432e2bdfcb8bbabefe5ade8e7086ef2a9d 100644 (file)
@@ -56,14 +56,15 @@ class InstallTestCase(support.TempdirManager,
             expected = os.path.normpath(expected)
             self.assertEqual(got, expected)
 
-        libdir = os.path.join(destination, "lib", "python")
+        impl_name = sys.implementation.name.replace("cpython", "python")
+        libdir = os.path.join(destination, "lib", impl_name)
         check_path(cmd.install_lib, libdir)
         _platlibdir = getattr(sys, "platlibdir", "lib")
-        platlibdir = os.path.join(destination, _platlibdir, "python")
+        platlibdir = os.path.join(destination, _platlibdir, impl_name)
         check_path(cmd.install_platlib, platlibdir)
         check_path(cmd.install_purelib, libdir)
         check_path(cmd.install_headers,
-                   os.path.join(destination, "include", "python", "foopkg"))
+                   os.path.join(destination, "include", impl_name, "foopkg"))
         check_path(cmd.install_scripts, os.path.join(destination, "bin"))
         check_path(cmd.install_data, destination)
 
index 9de3cb7018f0e096c2a1c8a727552fdea42b086e..e671f9e09b7ca430e9e01b0fda12a7c93b0a2cc3 100644 (file)
@@ -40,6 +40,8 @@ class SysconfigTestCase(support.EnvironGuard, unittest.TestCase):
 
     @unittest.skipIf(sys.platform == 'win32',
                      'Makefile only exists on Unix like systems')
+    @unittest.skipIf(sys.implementation.name != 'cpython',
+                     'Makefile only exists in CPython')
     def test_get_makefile_filename(self):
         makefile = sysconfig.get_makefile_filename()
         self.assertTrue(os.path.isfile(makefile), makefile)
@@ -299,6 +301,14 @@ class SysconfigTestCase(support.EnvironGuard, unittest.TestCase):
             result = sysconfig.parse_config_h(f)
         self.assertTrue(isinstance(result, dict))
 
+    @unittest.skipUnless(sys.platform == 'win32',
+                     'Testing windows pyd suffix')
+    @unittest.skipUnless(sys.implementation.name == 'cpython',
+                     'Need cpython for this test')
+    def test_win_ext_suffix(self):
+        self.assertTrue(sysconfig.get_config_var("EXT_SUFFIX").endswith(".pyd"))
+        self.assertNotEqual(sysconfig.get_config_var("EXT_SUFFIX"), ".pyd")
+
 def test_suite():
     suite = unittest.TestSuite()
     suite.addTest(unittest.TestLoader().loadTestsFromTestCase(SysconfigTestCase))
index 35e181dbb6dc23fa4ceb6c6b6a552f82aad038de..31f504e4311936ee98da74cd552936317d487861 100644 (file)
@@ -50,14 +50,14 @@ class Version:
     """
 
     def __init__ (self, vstring=None):
+        if vstring:
+            self.parse(vstring)
         warnings.warn(
             "distutils Version classes are deprecated. "
             "Use packaging.version instead.",
             DeprecationWarning,
             stacklevel=2,
         )
-        if vstring:
-            self.parse(vstring)
 
     def __repr__ (self):
         return "%s ('%s')" % (self.__class__.__name__, str(self))
index ff9b2c9bdf64a5ac06b83f9b14136561a577d8be..da55d4eeb62f8de49845750b650c004f5b963b7e 100644 (file)
@@ -312,8 +312,12 @@ def find_packages(
     where = kwargs.pop('where', ['.'])
     packages: List[str] = []
     fill_package_dir = {} if fill_package_dir is None else fill_package_dir
+    search = list(unique_everseen(always_iterable(where)))
 
-    for path in unique_everseen(always_iterable(where)):
+    if len(search) == 1 and all(not _same_path(search[0], x) for x in (".", root_dir)):
+        fill_package_dir.setdefault("", search[0])
+
+    for path in search:
         package_path = _nest_path(root_dir, path)
         pkgs = PackageFinder.find(package_path, **kwargs)
         packages.extend(pkgs)
@@ -326,8 +330,27 @@ def find_packages(
     return packages
 
 
+def _same_path(p1: _Path, p2: _Path) -> bool:
+    """Differs from os.path.samefile because it does not require paths to exist.
+    Purely string based (no comparison between i-nodes).
+    >>> _same_path("a/b", "./a/b")
+    True
+    >>> _same_path("a/b", "a/./b")
+    True
+    >>> _same_path("a/b", "././a/b")
+    True
+    >>> _same_path("a/b", "./a/b/c/..")
+    True
+    >>> _same_path("a/b", "../a/b/c")
+    False
+    >>> _same_path("a", "a/b")
+    False
+    """
+    return os.path.normpath(p1) == os.path.normpath(p2)
+
+
 def _nest_path(parent: _Path, path: _Path) -> str:
-    path = parent if path == "." else os.path.join(parent, path)
+    path = parent if path in {".", ""} else os.path.join(parent, path)
     return os.path.normpath(path)
 
 
index 3a59edbb74158b16c5d921bb1f55a416d6cbd93a..15053c8f244ecdd23e089867d994974204747adf 100644 (file)
@@ -130,7 +130,7 @@ def test_resolve_class(tmp_path, package_dir, file, module, return_value):
         ({}, {"pkg", "other", "dir1", "dir1.dir2"}),  # default value for `namespaces`
     ]
 )
-def test_find_packages(tmp_path, monkeypatch, args, pkgs):
+def test_find_packages(tmp_path, args, pkgs):
     files = {
         "pkg/__init__.py",
         "other/__init__.py",
@@ -153,3 +153,33 @@ def test_find_packages(tmp_path, monkeypatch, args, pkgs):
     ]
 
     assert set(expand.find_packages(where=where, **args)) == pkgs
+
+
+@pytest.mark.parametrize(
+    "files, where, expected_package_dir",
+    [
+        (["pkg1/__init__.py", "pkg1/other.py"], ["."], {}),
+        (["pkg1/__init__.py", "pkg2/__init__.py"], ["."], {}),
+        (["src/pkg1/__init__.py", "src/pkg1/other.py"], ["src"], {"": "src"}),
+        (["src/pkg1/__init__.py", "src/pkg2/__init__.py"], ["src"], {"": "src"}),
+        (
+            ["src1/pkg1/__init__.py", "src2/pkg2/__init__.py"],
+            ["src1", "src2"],
+            {"pkg1": "src1/pkg1", "pkg2": "src2/pkg2"},
+        ),
+        (
+            ["src/pkg1/__init__.py", "pkg2/__init__.py"],
+            ["src", "."],
+            {"pkg1": "src/pkg1"},
+        ),
+    ],
+)
+def test_fill_package_dir(tmp_path, files, where, expected_package_dir):
+    write_files({k: "" for k in files}, tmp_path)
+    pkg_dir = {}
+    kwargs = {"root_dir": tmp_path, "fill_package_dir": pkg_dir, "namespaces": False}
+    pkgs = expand.find_packages(where=where, **kwargs)
+    assert set(pkg_dir.items()) == set(expected_package_dir.items())
+    for pkg in pkgs:
+        pkg_path = find_package_path(pkg, pkg_dir, tmp_path)
+        assert os.path.exists(pkg_path)