+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
-------
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
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
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
* = *.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
* ``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
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
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
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.
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
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
sphinx-inline-tabs
sphinxcontrib-towncrier
ssl =
- wincertstore==0.2; sys_platform=='win32'
certs =
- certifi==2016.9.26
[options.entry_points]
distutils.commands =
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'])
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__':
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
setuptools/sandbox.py
setuptools/script (dev).tmpl
setuptools/script.tmpl
-setuptools/ssl_support.py
setuptools/unicode_utils.py
setuptools/version.py
setuptools/wheel.py
-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
+
[certs]
-certifi==2016.9.26
[docs]
sphinx
[ssl]
-[ssl:sys_platform == "win32"]
-wincertstore==0.2
-
[testing]
pytest>=4.6
pytest-checkdocs>=2.4
# 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
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:
# 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
# (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
"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"]
# 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))
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:
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')
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.
if DEBUG:
print(msg)
- # -- List-like methods ---------------------------------------------
+ # Collection methods
def append(self, item):
self.files.append(item)
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!
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()
(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" %
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
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.
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.
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):])
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.
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)
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
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([
"""Tests for distutils.unixccompiler."""
+import os
import sys
import unittest
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):
@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
# 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":
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
@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)
)
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
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
+++ /dev/null
-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
deps =
build
twine>=3
- path
jaraco.develop>=7.1
passenv =
TWINE_PASSWORD
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