Imported Upstream version 57.1.0 upstream/57.1.0
authorDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 18 Jul 2022 06:39:00 +0000 (15:39 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 18 Jul 2022 06:39:00 +0000 (15:39 +0900)
26 files changed:
CHANGES.rst
PKG-INFO
docs/build_meta.rst
docs/userguide/declarative_config.rst
pytest.ini
setup.cfg
setup.py
setuptools.egg-info/PKG-INFO
setuptools.egg-info/SOURCES.txt
setuptools.egg-info/dependency_links.txt
setuptools.egg-info/requires.txt
setuptools/_distutils/command/build.py
setuptools/_distutils/command/build_ext.py
setuptools/_distutils/command/install.py
setuptools/_distutils/cygwinccompiler.py
setuptools/_distutils/filelist.py
setuptools/_distutils/spawn.py
setuptools/_distutils/tests/test_build_ext.py
setuptools/_distutils/tests/test_filelist.py
setuptools/_distutils/tests/test_unixccompiler.py
setuptools/_distutils/unixccompiler.py
setuptools/_distutils/util.py
setuptools/dist.py
setuptools/package_index.py
setuptools/ssl_support.py [deleted file]
tox.ini

index 416ad89..3024ed6 100644 (file)
@@ -1,3 +1,23 @@
+v57.1.0
+-------
+
+
+Changes
+^^^^^^^
+* #2692: Globs are now sorted in 'license_files' restoring reproducibility by eliminating variance from disk order.
+* #2714: Update to distutils at pypa/distutils@e2627b7.
+* #2715: Removed reliance on deprecated ssl.match_hostname by removing the ssl support. Now any index operations rely on the native SSL implementation.
+
+Documentation changes
+^^^^^^^^^^^^^^^^^^^^^
+* #2604: Revamped the backward/cross tool compatibility section to remove
+  some confusion.
+  Add some examples and the version since when ``entry_points`` are
+  supported in declarative configuration.
+  Tried to make the reading flow a bit leaner, gather some informations
+  that were a bit dispersed.
+
+
 v57.0.0
 -------
 
index 022f20a..a8327b2 100644 (file)
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: setuptools
-Version: 57.0.0
+Version: 57.1.0
 Summary: Easily download, build, install, upgrade, and uninstall Python packages
 Home-page: https://github.com/pypa/setuptools
 Author: Python Packaging Authority
index 2ad5ae2..006ac3f 100644 (file)
@@ -5,7 +5,7 @@ Build System Support
 What is it?
 -------------
 
-Python packaging has come `a long way <https://www.bernat.tech/pep-517-518/>`_.
+Python packaging has come `a long way <https://bernat.tech/posts/pep-517-518/>`_.
 
 The traditional ``setuptools`` way of packaging Python modules
 uses a ``setup()`` function within the ``setup.py`` script. Commands such as
index 6d9d2e2..128d9f9 100644 (file)
@@ -18,15 +18,7 @@ to the ``setup()`` function (declarative config).
 This approach not only allows automation scenarios but also reduces
 boilerplate code in some cases.
 
-.. note::
-
-    This implementation has limited compatibility with the distutils2-like
-    ``setup.cfg`` sections used by the ``pbr`` and ``d2to1`` packages.
-
-    Namely: only metadata-related keys from ``metadata`` section are supported
-    (except for ``description-file``); keys from ``files``, ``entry_points``
-    and ``backwards_compat`` are not supported.
-
+.. _example-setup-config:
 
 .. code-block:: ini
 
@@ -58,6 +50,10 @@ boilerplate code in some cases.
     * = *.txt, *.rst
     hello = *.msg
 
+    [options.entry_points]
+    console_scripts =
+        executable-name = package.module:function
+
     [options.extras_require]
     pdf = ReportLab>=1.2; RXP
     rest = docutils>=0.3; pack ==1.1, ==1.3
@@ -156,10 +152,9 @@ Special directives:
 
 * ``file:`` - Value is read from a list of files and then concatenated
 
-
-.. note::
-    The ``file:`` directive is sandboxed and won't reach anything outside
-    the directory containing ``setup.py``.
+  .. note::
+      The ``file:`` directive is sandboxed and won't reach anything outside
+      the directory containing ``setup.py``.
 
 
 Metadata
@@ -169,11 +164,11 @@ Metadata
     The aliases given below are supported for compatibility reasons,
     but their use is not advised.
 
-==============================  =================  =================  =============== =====
+==============================  =================  =================  =============== ==========
 Key                             Aliases            Type               Minimum Version Notes
-==============================  =================  =================  =============== =====
+==============================  =================  =================  =============== ==========
 name                                               str
-version                                            attr:, file:, str  39.2.0          (1)
+version                                            attr:, file:, str  39.2.0          [#meta-1]_
 url                             home-page          str
 download_url                    download-url       str
 project_urls                                       dict               38.3.0
@@ -192,28 +187,29 @@ platforms                       platform           list-comma
 provides                                           list-comma
 requires                                           list-comma
 obsoletes                                          list-comma
-==============================  =================  =================  =============== =====
+==============================  =================  =================  =============== ==========
 
-.. note::
-    A version loaded using the ``file:`` directive must comply with PEP 440.
-    It is easy to accidentally put something other than a valid version
-    string in such a file, so validation is stricter in this case.
+**Notes**:
+
+.. [#meta-1] The ``version`` file attribute has only been supported since 39.2.0.
+
+   A version loaded using the ``file:`` directive must comply with PEP 440.
+   It is easy to accidentally put something other than a valid version
+   string in such a file, so validation is stricter in this case.
 
-Notes:
-1. The ``version`` file attribute has only been supported since 39.2.0.
 
 Options
 -------
 
-=======================  ===================================  =============== =====
+=======================  ===================================  =============== =========
 Key                      Type                                 Minimum Version Notes
-=======================  ===================================  =============== =====
+=======================  ===================================  =============== =========
 zip_safe                 bool
 setup_requires           list-semi
 install_requires         list-semi
-extras_require           section
+extras_require           section                                              [#opt-2]_
 python_requires          str
-entry_points             file:, section
+entry_points             file:, section                       51.0.0
 use_2to3                 bool
 use_2to3_fixers          list-comma
 use_2to3_exclude_fixers  list-comma
@@ -223,38 +219,74 @@ eager_resources          list-comma
 dependency_links         list-comma
 tests_require            list-semi
 include_package_data     bool
-packages                 find:, find_namespace:, list-comma
+packages                 find:, find_namespace:, list-comma                   [#opt-3]_
 package_dir              dict
-package_data             section                                              (1)
+package_data             section                                              [#opt-1]_
 exclude_package_data     section
 namespace_packages       list-comma
 py_modules               list-comma
 data_files               dict                                 40.6.0
-=======================  ===================================  =============== =====
-
-.. note::
-
-    **packages** - The ``find:`` and ``find_namespace:`` directive can be further configured
-    in a dedicated subsection ``options.packages.find``. This subsection
-    accepts the same keys as the ``setuptools.find_packages`` and the
-    ``setuptools.find_namespace_packages`` function:
-    ``where``, ``include``, and ``exclude``.
-
-    **find_namespace directive** - The ``find_namespace:`` directive is supported since Python >=3.3.
-
-
-Notes:
-1. In the ``package_data`` section, a key named with a single asterisk (``*``)
-refers to all packages, in lieu of the empty string used in ``setup.py``.
-
-2. In the ``extras_require`` section, values are parsed as ``list-semi``. This implies that in
-order to include markers, they **must** be *dangling*:
-
-.. code-block:: ini
-
-    [options.extras_require]
-    rest = docutils>=0.3; pack ==1.1, ==1.3
-    pdf =
-      ReportLab>=1.2
-      RXP
-      importlib-metadata; python_version < "3.8"
+=======================  ===================================  =============== =========
+
+**Notes**:
+
+.. [#opt-1] In the ``package_data`` section, a key named with a single asterisk
+   (``*``) refers to all packages, in lieu of the empty string used in ``setup.py``.
+.. [#opt-2] In the ``extras_require`` section, values are parsed as ``list-semi``.
+   This implies that in order to include markers, they **must** be *dangling*:
+   .. code-block:: ini
+
+      [options.extras_require]
+      rest = docutils>=0.3; pack ==1.1, ==1.3
+      pdf =
+        ReportLab>=1.2
+        RXP
+        importlib-metadata; python_version < "3.8"
+
+.. [#opt-3] The ``find:`` and ``find_namespace:`` directive can be further configured
+   in a dedicated subsection ``options.packages.find``. This subsection accepts the
+   same keys as the ``setuptools.find_packages`` and the
+   ``setuptools.find_namespace_packages`` function:
+   ``where``, ``include``, and ``exclude``.
+
+   The ``find_namespace:`` directive is supported since Python >=3.3.
+
+
+Compatibility with other tools
+==============================
+
+Historically, several tools explored declarative package configuration
+in parallel. And several of them chose to place the packaging
+configuration within the project's :file:`setup.cfg` file.
+One of the first was ``distutils2``, which development has stopped in
+2013. Other include ``pbr`` which is still under active development or
+``d2to1``, which was a plug-in that backports declarative configuration
+to ``distutils``, but has had no release since Oct. 2015.
+As a way to harmonize packaging tools, ``setuptools``, having held the
+position of *de facto* standard, has gradually integrated those
+features as part of its core features.
+
+Still this has lead to some confusion and feature incompatibilities:
+
+- some tools support features others don't;
+- some have similar features but the declarative syntax differs;
+
+The table below tries to summarize the differences. But, please, refer
+to each tool documentation for up-to-date information.
+
+=========================== ========== ========== ===== ===
+feature                     setuptools distutils2 d2to1 pbr
+=========================== ========== ========== ===== ===
+[metadata] description-file S          Y          Y     Y
+[files]                     S          Y          Y     Y
+entry_points                Y          Y          Y     S
+[backwards_compat]          N          Y          Y     Y
+=========================== ========== ========== ===== ===
+
+Y: supported, N: unsupported, S: syntax differs (see
+:ref:`above example<example-setup-config>`).
+
+Also note that some features were only recently added to ``setuptools``.
+Please refer to the previous sections to find out when.
index 9aa1b17..307c553 100644 (file)
@@ -10,6 +10,14 @@ junit_family=xunit2
 filterwarnings=
     # Fail on warnings
     error
+
+    ## upstream
+       # Suppress deprecation warning in flake8
+       ignore:SelectableGroups dict interface is deprecated::flake8
+       # Suppress deprecation warning in pypa/packaging#433
+       ignore:The distutils package is deprecated::packaging.tags
+       ## end upstream
+
     # https://github.com/pypa/setuptools/issues/1823
     ignore:bdist_wininst command is deprecated
     # Suppress this error; unimportant for CI tests
index c8dc93c..b51b962 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -2,7 +2,7 @@
 license_files = 
        LICENSE
 name = setuptools
-version = 57.0.0
+version = 57.1.0
 author = Python Packaging Authority
 author_email = distutils-sig@python.org
 description = Easily download, build, install, upgrade, and uninstall Python packages
@@ -66,9 +66,7 @@ docs =
        sphinx-inline-tabs
        sphinxcontrib-towncrier
 ssl = 
-       wincertstore==0.2; sys_platform=='win32'
 certs = 
-       certifi==2016.9.26
 
 [options.entry_points]
 distutils.commands = 
index 1b51bf2..c6affe9 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -19,11 +19,7 @@ force_windows_specific_files = (
     not in ("", "0", "false", "no")
 )
 
-include_windows_files = (
-    sys.platform == 'win32' or
-    os.name == 'java' and os._name == 'nt' or
-    force_windows_specific_files
-)
+include_windows_files = sys.platform == 'win32' or force_windows_specific_files
 
 if include_windows_files:
     package_data.setdefault('setuptools', []).extend(['*.exe'])
@@ -81,17 +77,8 @@ class install_with_pth(install):
 
 
 setup_params = dict(
-    src_root=None,
     cmdclass={'install': install_with_pth},
     package_data=package_data,
-    dependency_links=[
-        pypi_link(
-            'certifi-2016.9.26.tar.gz#md5=baa81e951a29958563689d868ef1064d',
-        ),
-        pypi_link(
-            'wincertstore-0.2.zip#md5=ae728f2f007185648d0c7a8679b361e2',
-        ),
-    ],
 )
 
 if __name__ == '__main__':
index 022f20a..a8327b2 100644 (file)
@@ -1,6 +1,6 @@
 Metadata-Version: 2.1
 Name: setuptools
-Version: 57.0.0
+Version: 57.1.0
 Summary: Easily download, build, install, upgrade, and uninstall Python packages
 Home-page: https://github.com/pypa/setuptools
 Author: Python Packaging Authority
index fc88dd4..1221256 100644 (file)
@@ -128,7 +128,6 @@ setuptools/py34compat.py
 setuptools/sandbox.py
 setuptools/script (dev).tmpl
 setuptools/script.tmpl
-setuptools/ssl_support.py
 setuptools/unicode_utils.py
 setuptools/version.py
 setuptools/wheel.py
index e87d021..8b13789 100644 (file)
@@ -1,2 +1 @@
-https://files.pythonhosted.org/packages/source/c/certifi/certifi-2016.9.26.tar.gz#md5=baa81e951a29958563689d868ef1064d
-https://files.pythonhosted.org/packages/source/w/wincertstore/wincertstore-0.2.zip#md5=ae728f2f007185648d0c7a8679b361e2
+
index 3061375..1ce8c19 100644 (file)
@@ -1,6 +1,5 @@
 
 [certs]
-certifi==2016.9.26
 
 [docs]
 sphinx
@@ -12,9 +11,6 @@ sphinxcontrib-towncrier
 
 [ssl]
 
-[ssl:sys_platform == "win32"]
-wincertstore==0.2
-
 [testing]
 pytest>=4.6
 pytest-checkdocs>=2.4
index a86df0b..4355a63 100644 (file)
@@ -102,7 +102,7 @@ class build(Command):
         # particular module distribution -- if user didn't supply it, pick
         # one of 'build_purelib' or 'build_platlib'.
         if self.build_lib is None:
-            if self.distribution.ext_modules:
+            if self.distribution.has_ext_modules():
                 self.build_lib = self.build_platlib
             else:
                 self.build_lib = self.build_purelib
index bbb3483..f7ab32c 100644 (file)
@@ -690,13 +690,15 @@ class build_ext(Command):
         provided, "PyInit_" + module_name.  Only relevant on Windows, where
         the .pyd file (DLL) must export the module "PyInit_" function.
         """
-        suffix = '_' + ext.name.split('.')[-1]
+        name = ext.name.split('.')[-1]
         try:
             # Unicode module name support as defined in PEP-489
             # https://www.python.org/dev/peps/pep-0489/#export-hook-name
-            suffix.encode('ascii')
+            name.encode('ascii')
         except UnicodeEncodeError:
-            suffix = 'U' + suffix.encode('punycode').replace(b'-', b'_').decode('ascii')
+            suffix = 'U_' + name.encode('punycode').replace(b'-', b'_').decode('ascii')
+        else:
+            suffix = "_" + name
 
         initfunc_name = "PyInit" + suffix
         if initfunc_name not in ext.export_symbols:
index 13feeb8..400fb45 100644 (file)
@@ -348,7 +348,7 @@ class install(Command):
         # module distribution is pure or not.  Of course, if the user
         # already specified install_lib, use their selection.
         if self.install_lib is None:
-            if self.distribution.ext_modules: # has extensions: non-pure
+            if self.distribution.has_ext_modules(): # has extensions: non-pure
                 self.install_lib = self.install_platlib
             else:
                 self.install_lib = self.install_purelib
index 66c12dd..f1c38e3 100644 (file)
@@ -44,6 +44,8 @@ cygwin in no-cygwin mode).
 #   (ld supports -shared)
 # * mingw gcc 3.2/ld 2.13 works
 #   (ld supports -shared)
+# * llvm-mingw with Clang 11 works
+#   (lld supports -shared)
 
 import os
 import sys
@@ -109,41 +111,46 @@ class CygwinCCompiler(UnixCCompiler):
                 "Compiling may fail because of undefined preprocessor macros."
                 % details)
 
-        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 = "gcc"
-        else:
-            self.linker_dll = "dllwrap"
+        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":
+            # 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"
-        else:
-            shared_option = "-mdll -static"
 
-        # Hard-code GCC because that's what this is all about.
-        # XXX optimization, warnings etc. should be customizable.
-        self.set_executables(compiler='gcc -mcygwin -O -Wall',
-                             compiler_so='gcc -mcygwin -mdll -O -Wall',
-                             compiler_cxx='g++ -mcygwin -O -Wall',
-                             linker_exe='gcc -mcygwin',
+        self.set_executables(compiler='%s -mcygwin -O -Wall' % self.cc,
+                             compiler_so='%s -mcygwin -mdll -O -Wall' % self.cc,
+                             compiler_cxx='%s -mcygwin -O -Wall' % self.cxx,
+                             linker_exe='%s -mcygwin' % self.cc,
                              linker_so=('%s -mcygwin %s' %
                                         (self.linker_dll, shared_option)))
 
         # cygwin and mingw32 need different sets of libraries
-        if self.gcc_version == "2.91.57":
+        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"]
@@ -281,26 +288,26 @@ class Mingw32CCompiler(CygwinCCompiler):
 
         # ld_version >= "2.13" support -shared so use it instead of
         # -mdll -static
-        if self.ld_version >= "2.13":
-            shared_option = "-shared"
-        else:
+        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 self.gcc_version <= "2.91.57":
+        if ('gcc' in self.cc and self.gcc_version <= "2.91.57"):
             entry_point = '--entry _DllMain@12'
         else:
             entry_point = ''
 
-        if is_cygwingcc():
+        if is_cygwincc(self.cc):
             raise CCompilerError(
                 'Cygwin gcc cannot be used with --compiler=mingw32')
 
-        self.set_executables(compiler='gcc -O -Wall',
-                             compiler_so='gcc -mdll -O -Wall',
-                             compiler_cxx='g++ -O -Wall',
-                             linker_exe='gcc',
+        self.set_executables(compiler='%s -O -Wall' % self.cc,
+                             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))
@@ -351,6 +358,10 @@ def check_config_h():
     if "GCC" in sys.version:
         return CONFIG_H_OK, "sys.version mentions 'GCC'"
 
+    # Clang would also work
+    if "Clang" in sys.version:
+        return CONFIG_H_OK, "sys.version mentions 'Clang'"
+
     # let's see if __GNUC__ is mentioned in python.h
     fn = sysconfig.get_config_h_filename()
     try:
@@ -397,7 +408,7 @@ def get_versions():
     commands = ['gcc -dumpversion', 'ld -v', 'dllwrap --version']
     return tuple([_find_exe_version(cmd) for cmd in commands])
 
-def is_cygwingcc():
-    '''Try to determine if the gcc that would be used is from cygwin.'''
-    out_string = check_output(['gcc', '-dumpmachine'])
+def is_cygwincc(cc):
+    '''Try to determine if the compiler that would be used is from cygwin.'''
+    out_string = check_output([cc, '-dumpmachine'])
     return out_string.strip().endswith(b'cygwin')
index c92d5fd..82a7738 100644 (file)
@@ -4,13 +4,16 @@ Provides the FileList class, used for poking about the filesystem
 and building lists of files.
 """
 
-import os, re
+import os
+import re
 import fnmatch
 import functools
+
 from distutils.util import convert_path
 from distutils.errors import DistutilsTemplateError, DistutilsInternalError
 from distutils import log
 
+
 class FileList:
     """A list of files built by on exploring the filesystem and filtered by
     applying various patterns to what we find there.
@@ -46,7 +49,7 @@ class FileList:
         if DEBUG:
             print(msg)
 
-    # -- List-like methods ---------------------------------------------
+    # Collection methods
 
     def append(self, item):
         self.files.append(item)
@@ -61,8 +64,7 @@ class FileList:
         for sort_tuple in sortable_files:
             self.files.append(os.path.join(*sort_tuple))
 
-
-    # -- Other miscellaneous utility methods ---------------------------
+    # Other miscellaneous utility methods
 
     def remove_duplicates(self):
         # Assumes list has been sorted!
@@ -70,8 +72,7 @@ class FileList:
             if self.files[i] == self.files[i - 1]:
                 del self.files[i]
 
-
-    # -- "File template" methods ---------------------------------------
+    # "File template" methods
 
     def _parse_template_line(self, line):
         words = line.split()
@@ -146,9 +147,11 @@ class FileList:
                              (dir, ' '.join(patterns)))
             for pattern in patterns:
                 if not self.include_pattern(pattern, prefix=dir):
-                    log.warn(("warning: no files found matching '%s' "
-                                "under directory '%s'"),
-                             pattern, dir)
+                    msg = (
+                        "warning: no files found matching '%s' "
+                        "under directory '%s'"
+                    )
+                    log.warn(msg, pattern, dir)
 
         elif action == 'recursive-exclude':
             self.debug_print("recursive-exclude %s %s" %
@@ -174,8 +177,7 @@ class FileList:
             raise DistutilsInternalError(
                   "this cannot happen: invalid action '%s'" % action)
 
-
-    # -- Filtering/selection methods -----------------------------------
+    # Filtering/selection methods
 
     def include_pattern(self, pattern, anchor=1, prefix=None, is_regex=0):
         """Select strings (presumably filenames) from 'self.files' that
@@ -219,9 +221,8 @@ class FileList:
                 files_found = True
         return files_found
 
-
-    def exclude_pattern (self, pattern,
-                         anchor=1, prefix=None, is_regex=0):
+    def exclude_pattern(
+            self, pattern, anchor=1, prefix=None, is_regex=0):
         """Remove strings (presumably filenames) from 'files' that match
         'pattern'.  Other parameters are the same as for
         'include_pattern()', above.
@@ -240,21 +241,47 @@ class FileList:
         return files_found
 
 
-# ----------------------------------------------------------------------
 # Utility functions
 
 def _find_all_simple(path):
     """
     Find all files under 'path'
     """
+    all_unique = _UniqueDirs.filter(os.walk(path, followlinks=True))
     results = (
         os.path.join(base, file)
-        for base, dirs, files in os.walk(path, followlinks=True)
+        for base, dirs, files in all_unique
         for file in files
     )
     return filter(os.path.isfile, results)
 
 
+class _UniqueDirs(set):
+    """
+    Exclude previously-seen dirs from walk results,
+    avoiding infinite recursion.
+    Ref https://bugs.python.org/issue44497.
+    """
+    def __call__(self, walk_item):
+        """
+        Given an item from an os.walk result, determine
+        if the item represents a unique dir for this instance
+        and if not, prevent further traversal.
+        """
+        base, dirs, files = walk_item
+        stat = os.stat(base)
+        candidate = stat.st_dev, stat.st_ino
+        found = candidate in self
+        if found:
+            del dirs[:]
+        self.add(candidate)
+        return not found
+
+    @classmethod
+    def filter(cls, items):
+        return filter(cls(), items)
+
+
 def findall(dir=os.curdir):
     """
     Find all files under 'dir' and return the list of full filenames.
@@ -319,7 +346,8 @@ def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0):
         if os.sep == '\\':
             sep = r'\\'
         pattern_re = pattern_re[len(start): len(pattern_re) - len(end)]
-        pattern_re = r'%s\A%s%s.*%s%s' % (start, prefix_re, sep, pattern_re, end)
+        pattern_re = r'%s\A%s%s.*%s%s' % (
+            start, prefix_re, sep, pattern_re, end)
     else:                               # no prefix -- respect anchor flag
         if anchor:
             pattern_re = r'%s\A%s' % (start, pattern_re[len(start):])
index b012d00..6e1c89f 100644 (file)
@@ -15,11 +15,6 @@ from distutils.debug import DEBUG
 from distutils import log
 
 
-if sys.platform == 'darwin':
-    _cfg_target = None
-    _cfg_target_split = None
-
-
 def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None):
     """Run another program, specified as a command list 'cmd', in a new process.
 
@@ -52,28 +47,10 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None):
     env = env if env is not None else dict(os.environ)
 
     if sys.platform == 'darwin':
-        global _cfg_target, _cfg_target_split
-        if _cfg_target is None:
-            from distutils import sysconfig
-            _cfg_target = sysconfig.get_config_var(
-                                  'MACOSX_DEPLOYMENT_TARGET') or ''
-            if _cfg_target:
-                _cfg_target_split = [int(x) for x in _cfg_target.split('.')]
-        if _cfg_target:
-            # 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 values, specifically LDSHARED which can use
-            # '-undefined dynamic_lookup' which only works on >= 10.3.
-            cur_target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', _cfg_target)
-            cur_target_split = [int(x) for x in cur_target.split('.')]
-            if _cfg_target_split[:2] >= [10, 3] and cur_target_split[:2] < [10, 3]:
-                my_msg = ('$MACOSX_DEPLOYMENT_TARGET mismatch: '
-                          'now "%s" but "%s" during configure;'
-                          'must use 10.3 or later'
-                                % (cur_target, _cfg_target))
-                raise DistutilsPlatformError(my_msg)
-            env.update(MACOSX_DEPLOYMENT_TARGET=cur_target)
+        from distutils.util import MACOSX_VERSION_VAR, get_macosx_target_ver
+        macosx_target_ver = get_macosx_target_ver()
+        if macosx_target_ver:
+            env[MACOSX_VERSION_VAR] = macosx_target_ver
 
     try:
         proc = subprocess.Popen(cmd, env=env)
index 5a72458..85ecf4b 100644 (file)
@@ -316,7 +316,7 @@ class BuildExtTestCase(TempdirManager,
         self.assertRegex(cmd.get_ext_filename(modules[0].name), r'foo(_d)?\..*')
         self.assertRegex(cmd.get_ext_filename(modules[1].name), r'föö(_d)?\..*')
         self.assertEqual(cmd.get_export_symbols(modules[0]), ['PyInit_foo'])
-        self.assertEqual(cmd.get_export_symbols(modules[1]), ['PyInitU_f_gkaa'])
+        self.assertEqual(cmd.get_export_symbols(modules[1]), ['PyInitU_f_1gaa'])
 
     def test_compiler_option(self):
         # cmd.compiler is an option and
index d8e4b39..9ec507b 100644 (file)
@@ -331,6 +331,16 @@ class FindAllTestCase(unittest.TestCase):
             expected = [file1]
             self.assertEqual(filelist.findall(temp_dir), expected)
 
+    @os_helper.skip_unless_symlink
+    def test_symlink_loop(self):
+        with os_helper.temp_dir() as temp_dir:
+            link = os.path.join(temp_dir, 'link-to-parent')
+            content = os.path.join(temp_dir, 'somefile')
+            os_helper.create_empty_file(content)
+            os.symlink('.', link)
+            files = filelist.findall(temp_dir)
+            assert len(files) == 1
+
 
 def test_suite():
     return unittest.TestSuite([
index 1828ba1..ebd7c16 100644 (file)
@@ -1,4 +1,5 @@
 """Tests for distutils.unixccompiler."""
+import os
 import sys
 import unittest
 from test.support import run_unittest
@@ -6,7 +7,9 @@ from test.support import run_unittest
 from .py38compat import EnvironmentVarGuard
 
 from distutils import sysconfig
+from distutils.errors import DistutilsPlatformError
 from distutils.unixccompiler import UnixCCompiler
+from distutils.util import _clear_cached_macosx_ver
 
 class UnixCCompilerTestCase(unittest.TestCase):
 
@@ -26,18 +29,90 @@ class UnixCCompilerTestCase(unittest.TestCase):
 
     @unittest.skipIf(sys.platform == 'win32', "can't test on Windows")
     def test_runtime_libdir_option(self):
-        # Issue#5900
+        # Issue #5900; GitHub Issue #37
         #
         # Ensure RUNPATH is added to extension modules with RPATH if
         # GNU ld is used
 
         # darwin
         sys.platform = 'darwin'
-        self.assertEqual(self.cc.rpath_foo(), '-L/foo')
+        darwin_ver_var = 'MACOSX_DEPLOYMENT_TARGET'
+        darwin_rpath_flag = '-Wl,-rpath,/foo'
+        darwin_lib_flag = '-L/foo'
+
+        # (macOS version from syscfg, macOS version from env var) -> flag
+        # Version value of None generates two tests: as None and as empty string
+        # Expected flag value of None means an mismatch exception is expected
+        darwin_test_cases = [
+            ((None    , None    ), darwin_lib_flag),
+            ((None    , '11'    ), darwin_rpath_flag),
+            (('10'    , None    ), darwin_lib_flag),
+            (('10.3'  , None    ), darwin_lib_flag),
+            (('10.3.1', None    ), darwin_lib_flag),
+            (('10.5'  , None    ), darwin_rpath_flag),
+            (('10.5.1', None    ), darwin_rpath_flag),
+            (('10.3'  , '10.3'  ), darwin_lib_flag),
+            (('10.3'  , '10.5'  ), darwin_rpath_flag),
+            (('10.5'  , '10.3'  ), darwin_lib_flag),
+            (('10.5'  , '11'    ), darwin_rpath_flag),
+            (('10.4'  , '10'    ), None),
+        ]
+
+        def make_darwin_gcv(syscfg_macosx_ver):
+            def gcv(var):
+                if var == darwin_ver_var:
+                    return syscfg_macosx_ver
+                return "xxx"
+            return gcv
+
+        def do_darwin_test(syscfg_macosx_ver, env_macosx_ver, expected_flag):
+            env = os.environ
+            msg = "macOS version = (sysconfig=%r, env=%r)" % \
+                    (syscfg_macosx_ver, env_macosx_ver)
+
+            # Save
+            old_gcv = sysconfig.get_config_var
+            old_env_macosx_ver = env.get(darwin_ver_var)
+
+            # Setup environment
+            _clear_cached_macosx_ver()
+            sysconfig.get_config_var = make_darwin_gcv(syscfg_macosx_ver)
+            if env_macosx_ver is not None:
+                env[darwin_ver_var] = env_macosx_ver
+            elif darwin_ver_var in env:
+                env.pop(darwin_ver_var)
+
+            # Run the test
+            if expected_flag is not None:
+                self.assertEqual(self.cc.rpath_foo(), expected_flag, msg=msg)
+            else:
+                with self.assertRaisesRegex(DistutilsPlatformError,
+                        darwin_ver_var + r' mismatch', msg=msg):
+                    self.cc.rpath_foo()
+
+            # Restore
+            if old_env_macosx_ver is not None:
+                env[darwin_ver_var] = old_env_macosx_ver
+            elif darwin_ver_var in env:
+                env.pop(darwin_ver_var)
+            sysconfig.get_config_var = old_gcv
+            _clear_cached_macosx_ver()
+
+        for macosx_vers, expected_flag in darwin_test_cases:
+            syscfg_macosx_ver, env_macosx_ver = macosx_vers
+            do_darwin_test(syscfg_macosx_ver, env_macosx_ver, expected_flag)
+            # Bonus test cases with None interpreted as empty string
+            if syscfg_macosx_ver is None:
+                do_darwin_test("", env_macosx_ver, expected_flag)
+            if env_macosx_ver is None:
+                do_darwin_test(syscfg_macosx_ver, "", expected_flag)
+            if syscfg_macosx_ver is None and env_macosx_ver is None:
+                do_darwin_test("", "", expected_flag)
+
+        old_gcv = sysconfig.get_config_var
 
         # hp-ux
         sys.platform = 'hp-ux'
-        old_gcv = sysconfig.get_config_var
         def gcv(v):
             return 'xxx'
         sysconfig.get_config_var = gcv
index 4d7a6de..f51977a 100644 (file)
@@ -233,8 +233,12 @@ class UnixCCompiler(CCompiler):
         # we use this hack.
         compiler = os.path.basename(sysconfig.get_config_var("CC"))
         if sys.platform[:6] == "darwin":
-            # MacOSX's linker doesn't understand the -R flag at all
-            return "-L" + dir
+            from distutils.util import get_macosx_target_ver, split_version
+            macosx_target_ver = get_macosx_target_ver()
+            if macosx_target_ver and split_version(macosx_target_ver) >= [10, 5]:
+                return "-Wl,-rpath," + dir
+            else: # no support for -rpath on earlier macOS versions
+                return "-L" + dir
         elif sys.platform[:7] == "freebsd":
             return "-Wl,-rpath=" + dir
         elif sys.platform[:5] == "hp-ux":
index f5aca79..76657d2 100644 (file)
@@ -108,6 +108,60 @@ def get_platform():
     else:
         return get_host_platform()
 
+
+if sys.platform == 'darwin':
+    _syscfg_macosx_ver = None # cache the version pulled from sysconfig
+MACOSX_VERSION_VAR = 'MACOSX_DEPLOYMENT_TARGET'
+
+def _clear_cached_macosx_ver():
+    """For testing only. Do not call."""
+    global _syscfg_macosx_ver
+    _syscfg_macosx_ver = None
+
+def get_macosx_target_ver_from_syscfg():
+    """Get the version of macOS latched in the Python interpreter configuration.
+    Returns the version as a string or None if can't obtain one. Cached."""
+    global _syscfg_macosx_ver
+    if _syscfg_macosx_ver is None:
+        from distutils import sysconfig
+        ver = sysconfig.get_config_var(MACOSX_VERSION_VAR) or ''
+        if ver:
+            _syscfg_macosx_ver = ver
+    return _syscfg_macosx_ver
+
+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
+    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.
+        # 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
+        # values, specifically LDSHARED which can use
+        # '-undefined dynamic_lookup' which only works on >= 10.3.
+        if syscfg_ver and split_version(syscfg_ver) >= [10, 3] and \
+            split_version(env_ver) < [10, 3]:
+            my_msg = ('$' + MACOSX_VERSION_VAR + ' mismatch: '
+                      'now "%s" but "%s" during configure; '
+                      'must use 10.3 or later'
+                      % (env_ver, syscfg_ver))
+            raise DistutilsPlatformError(my_msg)
+        return env_ver
+    return syscfg_ver
+
+
+def split_version(s):
+    """Convert a dot-separated string into a list of numbers for comparisons"""
+    return [int(n) for n in s.split('.')]
+
+
 def convert_path (pathname):
     """Return 'pathname' as a name that will work on the native filesystem,
     i.e. split it on '/' and put it back together again using the current
index 6e3f25f..df071c1 100644 (file)
@@ -600,10 +600,16 @@ class Distribution(_Distribution):
 
     @staticmethod
     def _expand_patterns(patterns):
+        """
+        >>> list(Distribution._expand_patterns(['LICENSE']))
+        ['LICENSE']
+        >>> list(Distribution._expand_patterns(['setup.cfg', 'LIC*']))
+        ['setup.cfg', 'LICENSE']
+        """
         return (
             path
             for pattern in patterns
-            for path in iglob(pattern)
+            for path in sorted(iglob(pattern))
             if not path.endswith('~')
             and os.path.isfile(path)
         )
index d1f1337..d818f44 100644 (file)
@@ -23,7 +23,6 @@ from pkg_resources import (
     Environment, find_distributions, safe_name, safe_version,
     to_filename, Requirement, DEVELOP_DIST, EGG_DIST,
 )
-from setuptools import ssl_support
 from distutils import log
 from distutils.errors import DistutilsError
 from fnmatch import translate
@@ -293,15 +292,7 @@ class PackageIndex(Environment):
         self.package_pages = {}
         self.allows = re.compile('|'.join(map(translate, hosts))).match
         self.to_scan = []
-        use_ssl = (
-            verify_ssl
-            and ssl_support.is_available
-            and (ca_bundle or ssl_support.find_ca_bundle())
-        )
-        if use_ssl:
-            self.opener = ssl_support.opener_for(ca_bundle)
-        else:
-            self.opener = urllib.request.urlopen
+        self.opener = urllib.request.urlopen
 
     # FIXME: 'PackageIndex.process_url' is too complex (14)
     def process_url(self, url, retrieve=False):  # noqa: C901
diff --git a/setuptools/ssl_support.py b/setuptools/ssl_support.py
deleted file mode 100644 (file)
index b58cca3..0000000
+++ /dev/null
@@ -1,266 +0,0 @@
-import os
-import socket
-import atexit
-import re
-import functools
-import urllib.request
-import http.client
-
-
-from pkg_resources import ResolutionError, ExtractionError
-
-try:
-    import ssl
-except ImportError:
-    ssl = None
-
-__all__ = [
-    'VerifyingHTTPSHandler', 'find_ca_bundle', 'is_available', 'cert_paths',
-    'opener_for'
-]
-
-cert_paths = """
-/etc/pki/tls/certs/ca-bundle.crt
-/etc/ssl/certs/ca-certificates.crt
-/usr/share/ssl/certs/ca-bundle.crt
-/usr/local/share/certs/ca-root.crt
-/etc/ssl/cert.pem
-/System/Library/OpenSSL/certs/cert.pem
-/usr/local/share/certs/ca-root-nss.crt
-/etc/ssl/ca-bundle.pem
-""".strip().split()
-
-try:
-    HTTPSHandler = urllib.request.HTTPSHandler
-    HTTPSConnection = http.client.HTTPSConnection
-except AttributeError:
-    HTTPSHandler = HTTPSConnection = object
-
-is_available = ssl is not None and object not in (
-    HTTPSHandler, HTTPSConnection)
-
-
-try:
-    from ssl import CertificateError, match_hostname
-except ImportError:
-    try:
-        from backports.ssl_match_hostname import CertificateError
-        from backports.ssl_match_hostname import match_hostname
-    except ImportError:
-        CertificateError = None
-        match_hostname = None
-
-if not CertificateError:
-
-    class CertificateError(ValueError):
-        pass
-
-
-if not match_hostname:  # noqa: C901  # 'If 59' is too complex (21)  # FIXME
-
-    def _dnsname_match(dn, hostname, max_wildcards=1):
-        """Matching according to RFC 6125, section 6.4.3
-
-        https://tools.ietf.org/html/rfc6125#section-6.4.3
-        """
-        pats = []
-        if not dn:
-            return False
-
-        # Ported from python3-syntax:
-        # leftmost, *remainder = dn.split(r'.')
-        parts = dn.split(r'.')
-        leftmost = parts[0]
-        remainder = parts[1:]
-
-        wildcards = leftmost.count('*')
-        if wildcards > max_wildcards:
-            # Issue #17980: avoid denials of service by refusing more
-            # than one wildcard per fragment.  A survey of established
-            # policy among SSL implementations showed it to be a
-            # reasonable choice.
-            raise CertificateError(
-                "too many wildcards in certificate DNS name: " + repr(dn))
-
-        # speed up common case w/o wildcards
-        if not wildcards:
-            return dn.lower() == hostname.lower()
-
-        # RFC 6125, section 6.4.3, subitem 1.
-        # The client SHOULD NOT attempt to match a
-        # presented identifier in which the wildcard
-        # character comprises a label other than the
-        # left-most label.
-        if leftmost == '*':
-            # When '*' is a fragment by itself, it matches a non-empty dotless
-            # fragment.
-            pats.append('[^.]+')
-        elif leftmost.startswith('xn--') or hostname.startswith('xn--'):
-            # RFC 6125, section 6.4.3, subitem 3.
-            # The client SHOULD NOT attempt to match a presented identifier
-            # where the wildcard character is embedded within an A-label or
-            # U-label of an internationalized domain name.
-            pats.append(re.escape(leftmost))
-        else:
-            # Otherwise, '*' matches any dotless string, e.g. www*
-            pats.append(re.escape(leftmost).replace(r'\*', '[^.]*'))
-
-        # add the remaining fragments, ignore any wildcards
-        for frag in remainder:
-            pats.append(re.escape(frag))
-
-        pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE)
-        return pat.match(hostname)
-
-    def match_hostname(cert, hostname):
-        """Verify that *cert* (in decoded format as returned by
-        SSLSocket.getpeercert()) matches the *hostname*.  RFC 2818 and RFC 6125
-        rules are followed, but IP addresses are not accepted for *hostname*.
-
-        CertificateError is raised on failure. On success, the function
-        returns nothing.
-        """
-        if not cert:
-            raise ValueError("empty or no certificate")
-        dnsnames = []
-        san = cert.get('subjectAltName', ())
-        for key, value in san:
-            if key == 'DNS':
-                if _dnsname_match(value, hostname):
-                    return
-                dnsnames.append(value)
-        if not dnsnames:
-            # The subject is only checked when there is no dNSName entry
-            # in subjectAltName
-            for sub in cert.get('subject', ()):
-                for key, value in sub:
-                    # XXX according to RFC 2818, the most specific Common Name
-                    # must be used.
-                    if key == 'commonName':
-                        if _dnsname_match(value, hostname):
-                            return
-                        dnsnames.append(value)
-        if len(dnsnames) > 1:
-            raise CertificateError(
-                "hostname %r doesn't match either of %s"
-                % (hostname, ', '.join(map(repr, dnsnames))))
-        elif len(dnsnames) == 1:
-            raise CertificateError(
-                "hostname %r doesn't match %r"
-                % (hostname, dnsnames[0]))
-        else:
-            raise CertificateError(
-                "no appropriate commonName or "
-                "subjectAltName fields were found")
-
-
-class VerifyingHTTPSHandler(HTTPSHandler):
-    """Simple verifying handler: no auth, subclasses, timeouts, etc."""
-
-    def __init__(self, ca_bundle):
-        self.ca_bundle = ca_bundle
-        HTTPSHandler.__init__(self)
-
-    def https_open(self, req):
-        return self.do_open(
-            lambda host, **kw: VerifyingHTTPSConn(host, self.ca_bundle, **kw),
-            req
-        )
-
-
-class VerifyingHTTPSConn(HTTPSConnection):
-    """Simple verifying connection: no auth, subclasses, timeouts, etc."""
-
-    def __init__(self, host, ca_bundle, **kw):
-        HTTPSConnection.__init__(self, host, **kw)
-        self.ca_bundle = ca_bundle
-
-    def connect(self):
-        sock = socket.create_connection(
-            (self.host, self.port), getattr(self, 'source_address', None)
-        )
-
-        # Handle the socket if a (proxy) tunnel is present
-        if hasattr(self, '_tunnel') and getattr(self, '_tunnel_host', None):
-            self.sock = sock
-            self._tunnel()
-            # http://bugs.python.org/issue7776: Python>=3.4.1 and >=2.7.7
-            # change self.host to mean the proxy server host when tunneling is
-            # being used. Adapt, since we are interested in the destination
-            # host for the match_hostname() comparison.
-            actual_host = self._tunnel_host
-        else:
-            actual_host = self.host
-
-        if hasattr(ssl, 'create_default_context'):
-            ctx = ssl.create_default_context(cafile=self.ca_bundle)
-            self.sock = ctx.wrap_socket(sock, server_hostname=actual_host)
-        else:
-            # This is for python < 2.7.9 and < 3.4?
-            self.sock = ssl.wrap_socket(
-                sock, cert_reqs=ssl.CERT_REQUIRED, ca_certs=self.ca_bundle
-            )
-        try:
-            match_hostname(self.sock.getpeercert(), actual_host)
-        except CertificateError:
-            self.sock.shutdown(socket.SHUT_RDWR)
-            self.sock.close()
-            raise
-
-
-def opener_for(ca_bundle=None):
-    """Get a urlopen() replacement that uses ca_bundle for verification"""
-    return urllib.request.build_opener(
-        VerifyingHTTPSHandler(ca_bundle or find_ca_bundle())
-    ).open
-
-
-# from jaraco.functools
-def once(func):
-    @functools.wraps(func)
-    def wrapper(*args, **kwargs):
-        if not hasattr(func, 'always_returns'):
-            func.always_returns = func(*args, **kwargs)
-        return func.always_returns
-    return wrapper
-
-
-@once
-def get_win_certfile():
-    try:
-        import wincertstore
-    except ImportError:
-        return None
-
-    class CertFile(wincertstore.CertFile):
-        def __init__(self):
-            super(CertFile, self).__init__()
-            atexit.register(self.close)
-
-        def close(self):
-            try:
-                super(CertFile, self).close()
-            except OSError:
-                pass
-
-    _wincerts = CertFile()
-    _wincerts.addstore('CA')
-    _wincerts.addstore('ROOT')
-    return _wincerts.name
-
-
-def find_ca_bundle():
-    """Return an existing CA bundle path, or None"""
-    extant_cert_paths = filter(os.path.isfile, cert_paths)
-    return (
-        get_win_certfile()
-        or next(extant_cert_paths, None)
-        or _certifi_where()
-    )
-
-
-def _certifi_where():
-    try:
-        return __import__('certifi').where()
-    except (ImportError, ResolutionError, ExtractionError):
-        pass
diff --git a/tox.ini b/tox.ini
index 4afe732..a1b3c16 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -54,7 +54,6 @@ skip_install = True
 deps =
        build
        twine>=3
-       path
        jaraco.develop>=7.1
 passenv =
        TWINE_PASSWORD
@@ -63,7 +62,7 @@ setenv =
        TWINE_USERNAME = {env:TWINE_USERNAME:__token__}
 commands =
        python -m bootstrap
-       python -c "import path; path.Path('dist').rmtree_p()"
+       python -c "import shutil; shutil.rmtree('dist', ignore_errors=True)"
        # unset tag_build and tag_date pypa/setuptools#2500
        python setup.py egg_info -Db "" saveopts
        python -m build