From caf0a2fdd2b7f329033e59183ca34d779d037bcc Mon Sep 17 00:00:00 2001 From: JinWang An Date: Mon, 27 Mar 2023 17:02:57 +0900 Subject: [PATCH] Imported Upstream version 65.6.0 --- .bumpversion.cfg | 2 +- .github/workflows/main.yml | 69 ++++++++- .readthedocs.yml => .readthedocs.yaml | 0 CHANGES.rst | 9 ++ README.rst | 10 +- docs/userguide/declarative_config.rst | 6 + docs/userguide/development_mode.rst | 4 +- docs/userguide/pyproject_config.rst | 7 + mypy.ini | 3 + setup.cfg | 2 +- setuptools/_distutils/README | 11 -- setuptools/_distutils/__init__.py | 12 +- setuptools/_distutils/_collections.py | 138 ++++++++++++++++++ setuptools/_distutils/_log.py | 4 + setuptools/_distutils/_msvccompiler.py | 8 +- setuptools/_distutils/archive_util.py | 8 +- setuptools/_distutils/bcppcompiler.py | 12 +- setuptools/_distutils/ccompiler.py | 14 +- setuptools/_distutils/cmd.py | 19 ++- setuptools/_distutils/command/bdist.py | 8 +- setuptools/_distutils/command/bdist_dumb.py | 12 +- setuptools/_distutils/command/bdist_rpm.py | 12 +- setuptools/_distutils/command/build.py | 8 +- setuptools/_distutils/command/build_clib.py | 12 +- setuptools/_distutils/command/build_ext.py | 34 +++-- setuptools/_distutils/command/build_py.py | 12 +- .../_distutils/command/build_scripts.py | 8 +- setuptools/_distutils/command/check.py | 4 +- setuptools/_distutils/command/clean.py | 8 +- setuptools/_distutils/command/config.py | 18 +-- setuptools/_distutils/command/install.py | 20 +-- setuptools/_distutils/command/install_data.py | 4 +- .../_distutils/command/install_egg_info.py | 5 +- .../_distutils/command/install_headers.py | 2 +- setuptools/_distutils/command/install_lib.py | 6 +- .../_distutils/command/install_scripts.py | 4 +- setuptools/_distutils/command/register.py | 18 ++- setuptools/_distutils/command/sdist.py | 20 +-- setuptools/_distutils/command/upload.py | 20 +-- setuptools/_distutils/config.py | 2 +- setuptools/_distutils/core.py | 12 +- setuptools/_distutils/cygwinccompiler.py | 82 +++++------ setuptools/_distutils/dep_util.py | 2 +- setuptools/_distutils/dir_util.py | 6 +- setuptools/_distutils/dist.py | 17 ++- setuptools/_distutils/fancy_getopt.py | 2 +- setuptools/_distutils/file_util.py | 4 +- setuptools/_distutils/filelist.py | 22 +-- setuptools/_distutils/log.py | 78 +++------- setuptools/_distutils/msvc9compiler.py | 8 +- setuptools/_distutils/msvccompiler.py | 6 +- setuptools/_distutils/spawn.py | 6 +- setuptools/_distutils/sysconfig.py | 8 +- setuptools/_distutils/tests/support.py | 28 +--- .../_distutils/tests/test_archive_util.py | 2 +- .../_distutils/tests/test_bdist_dumb.py | 1 - setuptools/_distutils/tests/test_bdist_rpm.py | 1 - setuptools/_distutils/tests/test_build.py | 2 +- .../_distutils/tests/test_build_clib.py | 2 +- setuptools/_distutils/tests/test_build_ext.py | 3 +- setuptools/_distutils/tests/test_build_py.py | 22 +-- .../_distutils/tests/test_build_scripts.py | 2 +- setuptools/_distutils/tests/test_check.py | 2 +- setuptools/_distutils/tests/test_clean.py | 2 +- setuptools/_distutils/tests/test_cmd.py | 21 +-- setuptools/_distutils/tests/test_config.py | 6 +- .../_distutils/tests/test_config_cmd.py | 4 +- setuptools/_distutils/tests/test_core.py | 21 +-- setuptools/_distutils/tests/test_dir_util.py | 33 ++--- setuptools/_distutils/tests/test_dist.py | 45 +++--- setuptools/_distutils/tests/test_file_util.py | 20 +-- setuptools/_distutils/tests/test_filelist.py | 75 +++++----- setuptools/_distutils/tests/test_install.py | 18 +-- .../_distutils/tests/test_install_data.py | 1 - .../_distutils/tests/test_install_headers.py | 1 - .../_distutils/tests/test_install_lib.py | 5 +- .../_distutils/tests/test_install_scripts.py | 2 +- setuptools/_distutils/tests/test_log.py | 53 +------ setuptools/_distutils/tests/test_register.py | 11 +- setuptools/_distutils/tests/test_sdist.py | 44 +++--- setuptools/_distutils/tests/test_spawn.py | 2 +- setuptools/_distutils/tests/test_upload.py | 15 +- setuptools/_distutils/unixccompiler.py | 10 +- setuptools/_distutils/util.py | 8 +- setuptools/_distutils/versionpredicate.py | 10 +- .../tests/config/test_apply_pyprojecttoml.py | 66 ++++++++- setuptools/tests/test_sdist.py | 24 ++- 87 files changed, 732 insertions(+), 658 deletions(-) rename .readthedocs.yml => .readthedocs.yaml (100%) delete mode 100644 setuptools/_distutils/README create mode 100644 setuptools/_distutils/_log.py diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 316cd4c..6a4b339 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 65.5.1 +current_version = 65.6.0 commit = True tag = True diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8892a6c..a877b76 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,6 +9,36 @@ concurrency: ${{ github.event.pull_request.number || github.sha }} cancel-in-progress: true +env: + # Environment variables to support color support (jaraco/skeleton#66): + # Request colored output from CLI tools supporting it. Different tools + # interpret the value differently. For some, just being set is sufficient. + # For others, it must be a non-zero integer. For yet others, being set + # to a non-empty value is sufficient. + FORCE_COLOR: -106 + # MyPy's color enforcement (must be a non-zero number) + MYPY_FORCE_COLOR: -42 + # Recognized by the `py` package, dependency of `pytest` (must be "1") + PY_COLORS: 1 + # Make tox-wrapped tools see color requests + TOX_TESTENV_PASSENV: >- + FORCE_COLOR + MYPY_FORCE_COLOR + NO_COLOR + PY_COLORS + PYTEST_THEME + PYTEST_THEME_MODE + + # Suppress noisy pip warnings + PIP_DISABLE_PIP_VERSION_CHECK: 'true' + PIP_NO_PYTHON_VERSION_WARNING: 'true' + PIP_NO_WARN_SCRIPT_LOCATION: 'true' + + # Disable the spinner, noise in GHA; TODO(webknjaz): Fix this upstream + # Must be "1". + TOX_PARALLEL_NO_SPINNER: 1 + + jobs: test: strategy: @@ -40,6 +70,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: Setup Python + id: python-install uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }}${{ matrix.dev }} @@ -67,14 +98,19 @@ jobs: uses: codecov/codecov-action@v3 with: flags: >- # Mark which lines are covered by which envs - ${{ runner.os }}, - ${{ matrix.python }} + CI-GHA, + ${{ github.job }}, + OS-${{ runner.os }}, + VM-${{ matrix.platform }}, + Py-${{ steps.python-install.outputs.python-version }} check: # This job does nothing and is only used for the branch protection if: always() needs: + - integration-test - test + - test_cygwin runs-on: ubuntu-latest @@ -82,6 +118,7 @@ jobs: - name: Decide whether the needed jobs succeeded or failed uses: re-actors/alls-green@release/v1 with: + allowed-skips: integration-test jobs: ${{ toJSON(needs) }} test_cygwin: @@ -105,11 +142,37 @@ jobs: python${{ matrix.python }}-tox, gcc-core, git, + - name: Record the currently selected Python version + id: python-install + # NOTE: This roughly emulates what `actions/setup-python@v4` provides + # NOTE: except the action gets the version from the installation path + # NOTE: on disk and we get it from runtime. + run: | + python -c 'import platform; print("python-version=" + platform.python_version())' >> ${GITHUB_OUTPUT} + shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0} - name: Run tests shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0} run: | git config --global --add safe.directory "$(cygpath -u "$GITHUB_WORKSPACE")" # workaround for #3408 tox + - name: Create coverage report + if: hashFiles('.coverage') != '' # Rudimentary `file.exists()` + run: | + python -m pip install coverage + python -m coverage xml --ignore-errors + shell: C:\cygwin\bin\env.exe CYGWIN_NOWINPATH=1 CHERE_INVOKING=1 C:\cygwin\bin\bash.exe -leo pipefail -o igncr {0} + - name: Publish coverage + if: hashFiles('coverage.xml') != '' # Rudimentary `file.exists()` + uses: codecov/codecov-action@v3 + with: + files: >- + ${{ github.workspace }}\coverage.xml + flags: >- # Mark which lines are covered by which envs + CI-GHA, + ${{ github.job }}, + OS-${{ runner.os }}, + VM-${{ matrix.platform }}, + Py-${{ steps.python-install.outputs.python-version }} integration-test: needs: test @@ -143,8 +206,6 @@ jobs: release: needs: - check - - test_cygwin - - integration-test if: github.event_name == 'push' && contains(github.ref, 'refs/tags/') runs-on: ubuntu-latest timeout-minutes: 75 diff --git a/.readthedocs.yml b/.readthedocs.yaml similarity index 100% rename from .readthedocs.yml rename to .readthedocs.yaml diff --git a/CHANGES.rst b/CHANGES.rst index e2b8dbd..5a24ff7 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,12 @@ +v65.6.0 +------- + + +Changes +^^^^^^^ +* #3674: Sync with pypa/distutils@e0787fa, including pypa/distutils#183 updating distutils to use the Python logging framework. + + v65.5.1 ------- diff --git a/README.rst b/README.rst index e421de5..86197e7 100644 --- a/README.rst +++ b/README.rst @@ -1,15 +1,7 @@ -.. image:: https://raw.githubusercontent.com/pypa/setuptools/main/docs/images/banner-640x320.svg - :align: center - -| - .. image:: https://img.shields.io/pypi/v/setuptools.svg - :target: `PyPI link`_ + :target: https://pypi.org/project/setuptools .. image:: https://img.shields.io/pypi/pyversions/setuptools.svg - :target: `PyPI link`_ - -.. _PyPI link: https://pypi.org/project/setuptools .. image:: https://github.com/pypa/setuptools/workflows/tests/badge.svg :target: https://github.com/pypa/setuptools/actions?query=workflow%3A%22tests%22 diff --git a/docs/userguide/declarative_config.rst b/docs/userguide/declarative_config.rst index c12f87b..3ef7b07 100644 --- a/docs/userguide/declarative_config.rst +++ b/docs/userguide/declarative_config.rst @@ -166,6 +166,12 @@ Special directives: The ``file:`` directive is sandboxed and won't reach anything outside the project directory (i.e. the directory containing ``setup.cfg``/``pyproject.toml``). + .. attention:: + When using the ``file:`` directive, please make sure that all necessary + files are included in the ``sdist``. You can do that via ``MANIFEST.in`` + or using plugins such as ``setuptools-scm``. + Please have a look on :doc:`/userguide/miscellaneous` for more information. + Metadata -------- diff --git a/docs/userguide/development_mode.rst b/docs/userguide/development_mode.rst index 2dc6dfb..1716e0a 100644 --- a/docs/userguide/development_mode.rst +++ b/docs/userguide/development_mode.rst @@ -5,7 +5,7 @@ When creating a Python project, developers usually want to implement and test changes iteratively, before cutting a release and preparing a distribution archive. In normal circumstances this can be quite cumbersome and require the developers -to manipulate the ``PYTHONPATH`` environment variable or to continuous re-build +to manipulate the ``PYTHONPATH`` environment variable or to continuously re-build and re-install the project. To facilitate iterative exploration and experimentation, setuptools allows @@ -247,7 +247,7 @@ More information is available on the text of :pep:`PEP 660 <660#what-to-put-in-t .. [#cwd] Techniques like the :ref:`src-layout` or tooling-specific options like `tox's changedir `_ - can be used to prevent such kinds of situations (chekout `this blog post + can be used to prevent such kinds of situations (checkout `this blog post `_ for more insights). diff --git a/docs/userguide/pyproject_config.rst b/docs/userguide/pyproject_config.rst index 88a6119..cc811ed 100644 --- a/docs/userguide/pyproject_config.rst +++ b/docs/userguide/pyproject_config.rst @@ -210,6 +210,13 @@ Also note that the file format for specifying dependencies resembles a ``require however please keep in mind that all non-comment lines must conform with :pep:`508` (``pip``-specify syntaxes, e.g. ``-c/-r/-e`` flags, are not supported). + +.. attention:: + When using the ``file`` directive, please make sure that all necessary + files are included in the ``sdist``. You can do that via ``MANIFEST.in`` + or using plugins such as ``setuptools-scm``. + Please have a look on :doc:`/userguide/miscellaneous` for more information. + ---- .. rubric:: Notes diff --git a/mypy.ini b/mypy.ini index 976ba02..b6f9727 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,2 +1,5 @@ [mypy] ignore_missing_imports = True +# required to support namespace packages +# https://github.com/python/mypy/issues/14057 +explicit_package_bases = True diff --git a/setup.cfg b/setup.cfg index 054fd04..a34af39 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = setuptools -version = 65.5.1 +version = 65.6.0 author = Python Packaging Authority author_email = distutils-sig@python.org description = Easily download, build, install, upgrade, and uninstall Python packages diff --git a/setuptools/_distutils/README b/setuptools/_distutils/README deleted file mode 100644 index 23f4885..0000000 --- a/setuptools/_distutils/README +++ /dev/null @@ -1,11 +0,0 @@ -This directory contains the Distutils package. - -There's a full documentation available at: - - http://docs.python.org/distutils/ - -The Distutils-SIG web page is also a good starting point: - - http://www.python.org/sigs/distutils-sig/ - -$Id$ diff --git a/setuptools/_distutils/__init__.py b/setuptools/_distutils/__init__.py index b3ac014..1a188c3 100644 --- a/setuptools/_distutils/__init__.py +++ b/setuptools/_distutils/__init__.py @@ -1,17 +1,7 @@ -"""distutils - -The main package for the Python Module Distribution Utilities. Normally -used from a setup script as - - from distutils.core import setup - - setup (...) -""" - import sys import importlib -__version__ = sys.version[: sys.version.index(' ')] +__version__, _, _ = sys.version.partition(' ') try: diff --git a/setuptools/_distutils/_collections.py b/setuptools/_distutils/_collections.py index 98fce80..0255661 100644 --- a/setuptools/_distutils/_collections.py +++ b/setuptools/_distutils/_collections.py @@ -1,5 +1,7 @@ import collections +import functools import itertools +import operator # from jaraco.collections 3.5.1 @@ -54,3 +56,139 @@ class DictStack(list, collections.abc.Mapping): def __len__(self): return len(list(iter(self))) + + +# from jaraco.collections 3.7 +class RangeMap(dict): + """ + A dictionary-like object that uses the keys as bounds for a range. + Inclusion of the value for that range is determined by the + key_match_comparator, which defaults to less-than-or-equal. + A value is returned for a key if it is the first key that matches in + the sorted list of keys. + + One may supply keyword parameters to be passed to the sort function used + to sort keys (i.e. key, reverse) as sort_params. + + Let's create a map that maps 1-3 -> 'a', 4-6 -> 'b' + + >>> r = RangeMap({3: 'a', 6: 'b'}) # boy, that was easy + >>> r[1], r[2], r[3], r[4], r[5], r[6] + ('a', 'a', 'a', 'b', 'b', 'b') + + Even float values should work so long as the comparison operator + supports it. + + >>> r[4.5] + 'b' + + But you'll notice that the way rangemap is defined, it must be open-ended + on one side. + + >>> r[0] + 'a' + >>> r[-1] + 'a' + + One can close the open-end of the RangeMap by using undefined_value + + >>> r = RangeMap({0: RangeMap.undefined_value, 3: 'a', 6: 'b'}) + >>> r[0] + Traceback (most recent call last): + ... + KeyError: 0 + + One can get the first or last elements in the range by using RangeMap.Item + + >>> last_item = RangeMap.Item(-1) + >>> r[last_item] + 'b' + + .last_item is a shortcut for Item(-1) + + >>> r[RangeMap.last_item] + 'b' + + Sometimes it's useful to find the bounds for a RangeMap + + >>> r.bounds() + (0, 6) + + RangeMap supports .get(key, default) + + >>> r.get(0, 'not found') + 'not found' + + >>> r.get(7, 'not found') + 'not found' + + One often wishes to define the ranges by their left-most values, + which requires use of sort params and a key_match_comparator. + + >>> r = RangeMap({1: 'a', 4: 'b'}, + ... sort_params=dict(reverse=True), + ... key_match_comparator=operator.ge) + >>> r[1], r[2], r[3], r[4], r[5], r[6] + ('a', 'a', 'a', 'b', 'b', 'b') + + That wasn't nearly as easy as before, so an alternate constructor + is provided: + + >>> r = RangeMap.left({1: 'a', 4: 'b', 7: RangeMap.undefined_value}) + >>> r[1], r[2], r[3], r[4], r[5], r[6] + ('a', 'a', 'a', 'b', 'b', 'b') + + """ + + def __init__(self, source, sort_params={}, key_match_comparator=operator.le): + dict.__init__(self, source) + self.sort_params = sort_params + self.match = key_match_comparator + + @classmethod + def left(cls, source): + return cls( + source, sort_params=dict(reverse=True), key_match_comparator=operator.ge + ) + + def __getitem__(self, item): + sorted_keys = sorted(self.keys(), **self.sort_params) + if isinstance(item, RangeMap.Item): + result = self.__getitem__(sorted_keys[item]) + else: + key = self._find_first_match_(sorted_keys, item) + result = dict.__getitem__(self, key) + if result is RangeMap.undefined_value: + raise KeyError(key) + return result + + def get(self, key, default=None): + """ + Return the value for key if key is in the dictionary, else default. + If default is not given, it defaults to None, so that this method + never raises a KeyError. + """ + try: + return self[key] + except KeyError: + return default + + def _find_first_match_(self, keys, item): + is_match = functools.partial(self.match, item) + matches = list(filter(is_match, keys)) + if matches: + return matches[0] + raise KeyError(item) + + def bounds(self): + sorted_keys = sorted(self.keys(), **self.sort_params) + return (sorted_keys[RangeMap.first_item], sorted_keys[RangeMap.last_item]) + + # some special values for the RangeMap + undefined_value = type(str('RangeValueUndefined'), (), {})() + + class Item(int): + "RangeMap Item" + + first_item = Item(0) + last_item = Item(-1) diff --git a/setuptools/_distutils/_log.py b/setuptools/_distutils/_log.py new file mode 100644 index 0000000..4a2ae0a --- /dev/null +++ b/setuptools/_distutils/_log.py @@ -0,0 +1,4 @@ +import logging + + +log = logging.getLogger() diff --git a/setuptools/_distutils/_msvccompiler.py b/setuptools/_distutils/_msvccompiler.py index 729c2dd..8b4023c 100644 --- a/setuptools/_distutils/_msvccompiler.py +++ b/setuptools/_distutils/_msvccompiler.py @@ -22,16 +22,16 @@ import unittest.mock as mock with contextlib.suppress(ImportError): import winreg -from distutils.errors import ( +from .errors import ( DistutilsExecError, DistutilsPlatformError, CompileError, LibError, LinkError, ) -from distutils.ccompiler import CCompiler, gen_lib_options -from distutils import log -from distutils.util import get_platform +from .ccompiler import CCompiler, gen_lib_options +from ._log import log +from .util import get_platform from itertools import count diff --git a/setuptools/_distutils/archive_util.py b/setuptools/_distutils/archive_util.py index 5dfe2a1..7f9e1e0 100644 --- a/setuptools/_distutils/archive_util.py +++ b/setuptools/_distutils/archive_util.py @@ -13,10 +13,10 @@ except ImportError: zipfile = None -from distutils.errors import DistutilsExecError -from distutils.spawn import spawn -from distutils.dir_util import mkpath -from distutils import log +from .errors import DistutilsExecError +from .spawn import spawn +from .dir_util import mkpath +from ._log import log try: from pwd import getpwnam diff --git a/setuptools/_distutils/bcppcompiler.py b/setuptools/_distutils/bcppcompiler.py index 80b6bd8..5d6b865 100644 --- a/setuptools/_distutils/bcppcompiler.py +++ b/setuptools/_distutils/bcppcompiler.py @@ -15,17 +15,17 @@ for the Borland C++ compiler. import os import warnings -from distutils.errors import ( +from .errors import ( DistutilsExecError, CompileError, LibError, LinkError, UnknownFileError, ) -from distutils.ccompiler import CCompiler, gen_preprocess_options -from distutils.file_util import write_file -from distutils.dep_util import newer -from distutils import log +from .ccompiler import CCompiler, gen_preprocess_options +from .file_util import write_file +from .dep_util import newer +from ._log import log warnings.warn( @@ -210,7 +210,7 @@ class BCPPCompiler(CCompiler): ) if runtime_library_dirs: - log.warn( + log.warning( "I don't know what to do with 'runtime_library_dirs': %s", str(runtime_library_dirs), ) diff --git a/setuptools/_distutils/ccompiler.py b/setuptools/_distutils/ccompiler.py index 97551c9..6463531 100644 --- a/setuptools/_distutils/ccompiler.py +++ b/setuptools/_distutils/ccompiler.py @@ -7,19 +7,19 @@ import sys import os import re -from distutils.errors import ( +from .errors import ( CompileError, LinkError, UnknownFileError, DistutilsPlatformError, DistutilsModuleError, ) -from distutils.spawn import spawn -from distutils.file_util import move_file -from distutils.dir_util import mkpath -from distutils.dep_util import newer_group -from distutils.util import split_quoted, execute -from distutils import log +from .spawn import spawn +from .file_util import move_file +from .dir_util import mkpath +from .dep_util import newer_group +from .util import split_quoted, execute +from ._log import log class CCompiler: diff --git a/setuptools/_distutils/cmd.py b/setuptools/_distutils/cmd.py index 68a9267..918db85 100644 --- a/setuptools/_distutils/cmd.py +++ b/setuptools/_distutils/cmd.py @@ -7,9 +7,11 @@ in the distutils.command package. import sys import os import re -from distutils.errors import DistutilsOptionError -from distutils import util, dir_util, file_util, archive_util, dep_util -from distutils import log +import logging + +from .errors import DistutilsOptionError +from . import util, dir_util, file_util, archive_util, dep_util +from ._log import log class Command: @@ -156,14 +158,14 @@ class Command: if header is None: header = "command options for '%s':" % self.get_command_name() - self.announce(indent + header, level=log.INFO) + self.announce(indent + header, level=logging.INFO) indent = indent + " " for (option, _, _) in self.user_options: option = option.translate(longopt_xlate) if option[-1] == "=": option = option[:-1] value = getattr(self, option) - self.announce(indent + "{} = {}".format(option, value), level=log.INFO) + self.announce(indent + "{} = {}".format(option, value), level=logging.INFO) def run(self): """A command's raison d'etre: carry out the action it exists to @@ -179,10 +181,7 @@ class Command: "abstract method -- subclass %s must override" % self.__class__ ) - def announce(self, msg, level=1): - """If the current verbosity level is of greater than or equal to - 'level' print 'msg' to stdout. - """ + def announce(self, msg, level=logging.DEBUG): log.log(level, msg) def debug_print(self, msg): @@ -334,7 +333,7 @@ class Command: # -- External world manipulation ----------------------------------- def warn(self, msg): - log.warn("warning: %s: %s\n", self.get_command_name(), msg) + log.warning("warning: %s: %s\n", self.get_command_name(), msg) def execute(self, func, args, msg=None, level=1): util.execute(func, args, msg, dry_run=self.dry_run) diff --git a/setuptools/_distutils/command/bdist.py b/setuptools/_distutils/command/bdist.py index de37dae..bf0baab 100644 --- a/setuptools/_distutils/command/bdist.py +++ b/setuptools/_distutils/command/bdist.py @@ -6,14 +6,14 @@ distribution).""" import os import warnings -from distutils.core import Command -from distutils.errors import DistutilsPlatformError, DistutilsOptionError -from distutils.util import get_platform +from ..core import Command +from ..errors import DistutilsPlatformError, DistutilsOptionError +from ..util import get_platform def show_formats(): """Print list of available formats (arguments to "--format" option).""" - from distutils.fancy_getopt import FancyGetopt + from ..fancy_getopt import FancyGetopt formats = [] for format in bdist.format_commands: diff --git a/setuptools/_distutils/command/bdist_dumb.py b/setuptools/_distutils/command/bdist_dumb.py index 0f52330..071da77 100644 --- a/setuptools/_distutils/command/bdist_dumb.py +++ b/setuptools/_distutils/command/bdist_dumb.py @@ -5,12 +5,12 @@ distribution -- i.e., just an archive to be unpacked under $prefix or $exec_prefix).""" import os -from distutils.core import Command -from distutils.util import get_platform -from distutils.dir_util import remove_tree, ensure_relative -from distutils.errors import DistutilsPlatformError -from distutils.sysconfig import get_python_version -from distutils import log +from ..core import Command +from ..util import get_platform +from ..dir_util import remove_tree, ensure_relative +from ..errors import DistutilsPlatformError +from ..sysconfig import get_python_version +from distutils._log import log class bdist_dumb(Command): diff --git a/setuptools/_distutils/command/bdist_rpm.py b/setuptools/_distutils/command/bdist_rpm.py index 6a50ef3..340527b 100644 --- a/setuptools/_distutils/command/bdist_rpm.py +++ b/setuptools/_distutils/command/bdist_rpm.py @@ -7,17 +7,17 @@ import subprocess import sys import os -from distutils.core import Command -from distutils.debug import DEBUG -from distutils.file_util import write_file -from distutils.errors import ( +from ..core import Command +from ..debug import DEBUG +from ..file_util import write_file +from ..errors import ( DistutilsOptionError, DistutilsPlatformError, DistutilsFileError, DistutilsExecError, ) -from distutils.sysconfig import get_python_version -from distutils import log +from ..sysconfig import get_python_version +from distutils._log import log class bdist_rpm(Command): diff --git a/setuptools/_distutils/command/build.py b/setuptools/_distutils/command/build.py index 6d45341..c3ab410 100644 --- a/setuptools/_distutils/command/build.py +++ b/setuptools/_distutils/command/build.py @@ -4,13 +4,13 @@ Implements the Distutils 'build' command.""" import sys import os -from distutils.core import Command -from distutils.errors import DistutilsOptionError -from distutils.util import get_platform +from ..core import Command +from ..errors import DistutilsOptionError +from ..util import get_platform def show_compilers(): - from distutils.ccompiler import show_compilers + from ..ccompiler import show_compilers show_compilers() diff --git a/setuptools/_distutils/command/build_clib.py b/setuptools/_distutils/command/build_clib.py index 50bb9bb..f90c566 100644 --- a/setuptools/_distutils/command/build_clib.py +++ b/setuptools/_distutils/command/build_clib.py @@ -15,14 +15,14 @@ module.""" # cut 'n paste. Sigh. import os -from distutils.core import Command -from distutils.errors import DistutilsSetupError -from distutils.sysconfig import customize_compiler -from distutils import log +from ..core import Command +from ..errors import DistutilsSetupError +from ..sysconfig import customize_compiler +from distutils._log import log def show_compilers(): - from distutils.ccompiler import show_compilers + from ..ccompiler import show_compilers show_compilers() @@ -92,7 +92,7 @@ class build_clib(Command): return # Yech -- this is cut 'n pasted from build_ext.py! - from distutils.ccompiler import new_compiler + from ..ccompiler import new_compiler self.compiler = new_compiler( compiler=self.compiler, dry_run=self.dry_run, force=self.force diff --git a/setuptools/_distutils/command/build_ext.py b/setuptools/_distutils/command/build_ext.py index 3c6cee7..f4c0ecc 100644 --- a/setuptools/_distutils/command/build_ext.py +++ b/setuptools/_distutils/command/build_ext.py @@ -8,8 +8,8 @@ import contextlib import os import re import sys -from distutils.core import Command -from distutils.errors import ( +from ..core import Command +from ..errors import ( DistutilsOptionError, DistutilsSetupError, CCompilerError, @@ -17,12 +17,12 @@ from distutils.errors import ( CompileError, DistutilsPlatformError, ) -from distutils.sysconfig import customize_compiler, get_python_version -from distutils.sysconfig import get_config_h_filename -from distutils.dep_util import newer_group -from distutils.extension import Extension -from distutils.util import get_platform -from distutils import log +from ..sysconfig import customize_compiler, get_python_version +from ..sysconfig import get_config_h_filename +from ..dep_util import newer_group +from ..extension import Extension +from ..util import get_platform +from distutils._log import log from . import py37compat from site import USER_BASE @@ -33,7 +33,7 @@ extension_name_re = re.compile(r'^[a-zA-Z_][a-zA-Z_0-9]*(\.[a-zA-Z_][a-zA-Z_0-9] def show_compilers(): - from distutils.ccompiler import show_compilers + from ..ccompiler import show_compilers show_compilers() @@ -280,7 +280,7 @@ class build_ext(Command): raise DistutilsOptionError("parallel should be an integer") def run(self): # noqa: C901 - from distutils.ccompiler import new_compiler + from ..ccompiler import new_compiler # 'self.extensions', as supplied by setup.py, is a list of # Extension instances. See the documentation for Extension (in @@ -373,7 +373,7 @@ class build_ext(Command): ext_name, build_info = ext - log.warn( + log.warning( "old-style (ext_name, build_info) tuple found in " "ext_modules for extension '%s' " "-- please convert to Extension instance", @@ -413,7 +413,9 @@ class build_ext(Command): # Medium-easy stuff: same syntax/semantics, different names. ext.runtime_library_dirs = build_info.get('rpath') if 'def_file' in build_info: - log.warn("'def_file' element of build info dict " "no longer supported") + log.warning( + "'def_file' element of build info dict " "no longer supported" + ) # Non-trivial stuff: 'macros' split into 'define_macros' # and 'undef_macros'. @@ -597,7 +599,7 @@ class build_ext(Command): # the temp dir. if self.swig_cpp: - log.warn("--swig-cpp is deprecated - use --swig-opts=-c++") + log.warning("--swig-cpp is deprecated - use --swig-opts=-c++") if ( self.swig_cpp @@ -704,7 +706,7 @@ class build_ext(Command): of the file from which it will be loaded (eg. "foo/bar.so", or "foo\bar.pyd"). """ - from distutils.sysconfig import get_config_var + from ..sysconfig import get_config_var ext_path = ext_name.split('.') ext_suffix = get_config_var('EXT_SUFFIX') @@ -742,7 +744,7 @@ class build_ext(Command): # to need it mentioned explicitly, though, so that's what we do. # Append '_d' to the python import library on debug builds. if sys.platform == "win32": - from distutils._msvccompiler import MSVCCompiler + from .._msvccompiler import MSVCCompiler if not isinstance(self.compiler, MSVCCompiler): template = "python%d%d" @@ -764,7 +766,7 @@ class build_ext(Command): # On Cygwin (and if required, other POSIX-like platforms based on # Windows like MinGW) it is simply necessary that all symbols in # shared libraries are resolved at link time. - from distutils.sysconfig import get_config_var + from ..sysconfig import get_config_var link_libpython = False if get_config_var('Py_ENABLE_SHARED'): diff --git a/setuptools/_distutils/command/build_py.py b/setuptools/_distutils/command/build_py.py index 47c6158..9f78324 100644 --- a/setuptools/_distutils/command/build_py.py +++ b/setuptools/_distutils/command/build_py.py @@ -7,10 +7,10 @@ import importlib.util import sys import glob -from distutils.core import Command -from distutils.errors import DistutilsOptionError, DistutilsFileError -from distutils.util import convert_path -from distutils import log +from ..core import Command +from ..errors import DistutilsOptionError, DistutilsFileError +from ..util import convert_path +from distutils._log import log class build_py(Command): @@ -212,7 +212,7 @@ class build_py(Command): def check_module(self, module, module_file): if not os.path.isfile(module_file): - log.warn("file %s (for module %s) not found", module_file, module) + log.warning("file %s (for module %s) not found", module_file, module) return False else: return True @@ -384,7 +384,7 @@ class build_py(Command): self.warn('byte-compiling is disabled, skipping.') return - from distutils.util import byte_compile + from ..util import byte_compile prefix = self.build_lib if prefix[-1] != os.sep: diff --git a/setuptools/_distutils/command/build_scripts.py b/setuptools/_distutils/command/build_scripts.py index 2cc5d1e..87174f6 100644 --- a/setuptools/_distutils/command/build_scripts.py +++ b/setuptools/_distutils/command/build_scripts.py @@ -6,10 +6,10 @@ import os import re from stat import ST_MODE from distutils import sysconfig -from distutils.core import Command -from distutils.dep_util import newer -from distutils.util import convert_path -from distutils import log +from ..core import Command +from ..dep_util import newer +from ..util import convert_path +from distutils._log import log import tokenize shebang_pattern = re.compile('^#!.*python[0-9.]*([ \t].*)?$') diff --git a/setuptools/_distutils/command/check.py b/setuptools/_distutils/command/check.py index 539481c..575e49f 100644 --- a/setuptools/_distutils/command/check.py +++ b/setuptools/_distutils/command/check.py @@ -4,8 +4,8 @@ Implements the Distutils 'check' command. """ import contextlib -from distutils.core import Command -from distutils.errors import DistutilsSetupError +from ..core import Command +from ..errors import DistutilsSetupError with contextlib.suppress(ImportError): import docutils.utils diff --git a/setuptools/_distutils/command/clean.py b/setuptools/_distutils/command/clean.py index b731b60..d6eb3eb 100644 --- a/setuptools/_distutils/command/clean.py +++ b/setuptools/_distutils/command/clean.py @@ -5,9 +5,9 @@ Implements the Distutils 'clean' command.""" # contributed by Bastian Kleineidam , added 2000-03-18 import os -from distutils.core import Command -from distutils.dir_util import remove_tree -from distutils import log +from ..core import Command +from ..dir_util import remove_tree +from distutils._log import log class clean(Command): @@ -64,7 +64,7 @@ class clean(Command): if os.path.exists(directory): remove_tree(directory, dry_run=self.dry_run) else: - log.warn("'%s' does not exist -- can't clean it", directory) + log.warning("'%s' does not exist -- can't clean it", directory) # just for the heck of it, try to remove the base build directory: # we might have emptied it right now, but if not we don't care diff --git a/setuptools/_distutils/command/config.py b/setuptools/_distutils/command/config.py index 4492c89..8bf0e48 100644 --- a/setuptools/_distutils/command/config.py +++ b/setuptools/_distutils/command/config.py @@ -12,10 +12,10 @@ this header file lives". import os import re -from distutils.core import Command -from distutils.errors import DistutilsExecError -from distutils.sysconfig import customize_compiler -from distutils import log +from ..core import Command +from ..errors import DistutilsExecError +from ..sysconfig import customize_compiler +from distutils._log import log LANG_EXT = {"c": ".c", "c++": ".cxx"} @@ -87,7 +87,7 @@ class config(Command): """ # We do this late, and only on-demand, because this is an expensive # import. - from distutils.ccompiler import CCompiler, new_compiler + from ..ccompiler import CCompiler, new_compiler if not isinstance(self.compiler, CCompiler): self.compiler = new_compiler( @@ -174,7 +174,7 @@ class config(Command): preprocessor succeeded, false if there were any errors. ('body' probably isn't of much use, but what the heck.) """ - from distutils.ccompiler import CompileError + from ..ccompiler import CompileError self._check_compiler() ok = True @@ -217,7 +217,7 @@ class config(Command): """Try to compile a source file built from 'body' and 'headers'. Return true on success, false otherwise. """ - from distutils.ccompiler import CompileError + from ..ccompiler import CompileError self._check_compiler() try: @@ -243,7 +243,7 @@ class config(Command): 'headers', to executable form. Return true on success, false otherwise. """ - from distutils.ccompiler import CompileError, LinkError + from ..ccompiler import CompileError, LinkError self._check_compiler() try: @@ -269,7 +269,7 @@ class config(Command): built from 'body' and 'headers'. Return true on success, false otherwise. """ - from distutils.ccompiler import CompileError, LinkError + from ..ccompiler import CompileError, LinkError self._check_compiler() try: diff --git a/setuptools/_distutils/command/install.py b/setuptools/_distutils/command/install.py index a38cddc..08d2f88 100644 --- a/setuptools/_distutils/command/install.py +++ b/setuptools/_distutils/command/install.py @@ -8,14 +8,14 @@ import contextlib import sysconfig import itertools -from distutils import log -from distutils.core import Command -from distutils.debug import DEBUG -from distutils.sysconfig import get_config_vars -from distutils.file_util import write_file -from distutils.util import convert_path, subst_vars, change_root -from distutils.util import get_platform -from distutils.errors import DistutilsOptionError, DistutilsPlatformError +from distutils._log import log +from ..core import Command +from ..debug import DEBUG +from ..sysconfig import get_config_vars +from ..file_util import write_file +from ..util import convert_path, subst_vars, change_root +from ..util import get_platform +from ..errors import DistutilsOptionError, DistutilsPlatformError from . import _framework_compat as fw from .. import _collections @@ -515,7 +515,7 @@ class install(Command): """Dumps the list of user options.""" if not DEBUG: return - from distutils.fancy_getopt import longopt_xlate + from ..fancy_getopt import longopt_xlate log.debug(msg + ":") for opt in self.user_options: @@ -644,7 +644,7 @@ class install(Command): self.extra_path = self.distribution.extra_path if self.extra_path is not None: - log.warn( + log.warning( "Distribution option extra_path is deprecated. " "See issue27919 for details." ) diff --git a/setuptools/_distutils/command/install_data.py b/setuptools/_distutils/command/install_data.py index 23d91ad..d92ed87 100644 --- a/setuptools/_distutils/command/install_data.py +++ b/setuptools/_distutils/command/install_data.py @@ -6,8 +6,8 @@ platform-independent data files.""" # contributed by Bastian Kleineidam import os -from distutils.core import Command -from distutils.util import change_root, convert_path +from ..core import Command +from ..util import change_root, convert_path class install_data(Command): diff --git a/setuptools/_distutils/command/install_egg_info.py b/setuptools/_distutils/command/install_egg_info.py index d5e68a6..f3e8f34 100644 --- a/setuptools/_distutils/command/install_egg_info.py +++ b/setuptools/_distutils/command/install_egg_info.py @@ -9,8 +9,9 @@ import os import sys import re -from distutils.cmd import Command -from distutils import log, dir_util +from ..cmd import Command +from .. import dir_util +from .._log import log class install_egg_info(Command): diff --git a/setuptools/_distutils/command/install_headers.py b/setuptools/_distutils/command/install_headers.py index 87046ab..1cdee82 100644 --- a/setuptools/_distutils/command/install_headers.py +++ b/setuptools/_distutils/command/install_headers.py @@ -3,7 +3,7 @@ Implements the Distutils 'install_headers' command, to install C/C++ header files to the Python include directory.""" -from distutils.core import Command +from ..core import Command # XXX force is never used diff --git a/setuptools/_distutils/command/install_lib.py b/setuptools/_distutils/command/install_lib.py index ad3089c..840d340 100644 --- a/setuptools/_distutils/command/install_lib.py +++ b/setuptools/_distutils/command/install_lib.py @@ -7,8 +7,8 @@ import os import importlib.util import sys -from distutils.core import Command -from distutils.errors import DistutilsOptionError +from ..core import Command +from ..errors import DistutilsOptionError # Extension for Python source files. @@ -126,7 +126,7 @@ class install_lib(Command): self.warn('byte-compiling is disabled, skipping.') return - from distutils.util import byte_compile + from ..util import byte_compile # Get the "--root" directory supplied to the "install" command, # and use it as a prefix to strip off the purported filename diff --git a/setuptools/_distutils/command/install_scripts.py b/setuptools/_distutils/command/install_scripts.py index f09bd64..ec6ec5a 100644 --- a/setuptools/_distutils/command/install_scripts.py +++ b/setuptools/_distutils/command/install_scripts.py @@ -6,8 +6,8 @@ Python scripts.""" # contributed by Bastian Kleineidam import os -from distutils.core import Command -from distutils import log +from ..core import Command +from distutils._log import log from stat import ST_MODE diff --git a/setuptools/_distutils/command/register.py b/setuptools/_distutils/command/register.py index c140265..55c1045 100644 --- a/setuptools/_distutils/command/register.py +++ b/setuptools/_distutils/command/register.py @@ -7,12 +7,13 @@ Implements the Distutils 'register' command (register with the repository). import getpass import io +import logging import urllib.parse import urllib.request from warnings import warn -from distutils.core import PyPIRCCommand -from distutils import log +from ..core import PyPIRCCommand +from distutils._log import log class register(PyPIRCCommand): @@ -153,7 +154,7 @@ We need to know who you are, so please choose either: 3. have the server generate a new password for you (and email it to you), or 4. quit Your selection [default 1]: ''', - log.INFO, + logging.INFO, ) choice = input() if not choice: @@ -174,7 +175,7 @@ Your selection [default 1]: ''', auth.add_password(self.realm, host, username, password) # send the info to the server and report the result code, result = self.post_to_server(self.build_post_data('submit'), auth) - self.announce('Server response ({}): {}'.format(code, result), log.INFO) + self.announce('Server response ({}): {}'.format(code, result), logging.INFO) # possibly save the login if code == 200: @@ -188,11 +189,11 @@ Your selection [default 1]: ''', 'I can store your PyPI login so future ' 'submissions will be faster.' ), - log.INFO, + logging.INFO, ) self.announce( '(the login will be stored in %s)' % self._get_rc_file(), - log.INFO, + logging.INFO, ) choice = 'X' while choice.lower() not in 'yn': @@ -265,7 +266,8 @@ Your selection [default 1]: ''', '''Post a query to the server, and return a string response.''' if 'name' in data: self.announce( - 'Registering {} to {}'.format(data['name'], self.repository), log.INFO + 'Registering {} to {}'.format(data['name'], self.repository), + logging.INFO, ) # Build up the MIME payload for the urllib2 POST data boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' @@ -315,5 +317,5 @@ Your selection [default 1]: ''', result = 200, 'OK' if self.show_response: msg = '\n'.join(('-' * 75, data, '-' * 75)) - self.announce(msg, log.INFO) + self.announce(msg, logging.INFO) return result diff --git a/setuptools/_distutils/command/sdist.py b/setuptools/_distutils/command/sdist.py index d6e9489..5cfd4c1 100644 --- a/setuptools/_distutils/command/sdist.py +++ b/setuptools/_distutils/command/sdist.py @@ -7,23 +7,23 @@ import sys from glob import glob from warnings import warn -from distutils.core import Command +from ..core import Command from distutils import dir_util from distutils import file_util from distutils import archive_util -from distutils.text_file import TextFile -from distutils.filelist import FileList -from distutils import log -from distutils.util import convert_path -from distutils.errors import DistutilsOptionError, DistutilsTemplateError +from ..text_file import TextFile +from ..filelist import FileList +from distutils._log import log +from ..util import convert_path +from ..errors import DistutilsOptionError, DistutilsTemplateError def show_formats(): """Print all possible values for the 'formats' option (used by the "--help-formats" command-line option). """ - from distutils.fancy_getopt import FancyGetopt - from distutils.archive_util import ARCHIVE_FORMATS + from ..fancy_getopt import FancyGetopt + from ..archive_util import ARCHIVE_FORMATS formats = [] for format in ARCHIVE_FORMATS.keys(): @@ -481,12 +481,12 @@ class sdist(Command): msg = "copying files to %s..." % base_dir if not files: - log.warn("no files to distribute -- empty manifest?") + log.warning("no files to distribute -- empty manifest?") else: log.info(msg) for file in files: if not os.path.isfile(file): - log.warn("'%s' not a regular file -- skipping", file) + log.warning("'%s' not a regular file -- skipping", file) else: dest = os.path.join(base_dir, file) self.copy_file(file, dest, link=link) diff --git a/setuptools/_distutils/command/upload.py b/setuptools/_distutils/command/upload.py index 6af5394..16e15d8 100644 --- a/setuptools/_distutils/command/upload.py +++ b/setuptools/_distutils/command/upload.py @@ -8,13 +8,13 @@ index). import os import io import hashlib +import logging from base64 import standard_b64encode from urllib.request import urlopen, Request, HTTPError from urllib.parse import urlparse -from distutils.errors import DistutilsError, DistutilsOptionError -from distutils.core import PyPIRCCommand -from distutils.spawn import spawn -from distutils import log +from ..errors import DistutilsError, DistutilsOptionError +from ..core import PyPIRCCommand +from ..spawn import spawn # PyPI Warehouse supports MD5, SHA256, and Blake2 (blake2-256) @@ -171,7 +171,7 @@ class upload(PyPIRCCommand): body = body.getvalue() msg = "Submitting {} to {}".format(filename, self.repository) - self.announce(msg, log.INFO) + self.announce(msg, logging.INFO) # build the Request headers = { @@ -190,16 +190,18 @@ class upload(PyPIRCCommand): status = e.code reason = e.msg except OSError as e: - self.announce(str(e), log.ERROR) + self.announce(str(e), logging.ERROR) raise if status == 200: - self.announce('Server response ({}): {}'.format(status, reason), log.INFO) + self.announce( + 'Server response ({}): {}'.format(status, reason), logging.INFO + ) if self.show_response: text = self._read_pypi_response(result) msg = '\n'.join(('-' * 75, text, '-' * 75)) - self.announce(msg, log.INFO) + self.announce(msg, logging.INFO) else: msg = 'Upload failed ({}): {}'.format(status, reason) - self.announce(msg, log.ERROR) + self.announce(msg, logging.ERROR) raise DistutilsError(msg) diff --git a/setuptools/_distutils/config.py b/setuptools/_distutils/config.py index 6e0c3a7..9a4044a 100644 --- a/setuptools/_distutils/config.py +++ b/setuptools/_distutils/config.py @@ -6,7 +6,7 @@ that uses .pypirc in the distutils.command package. import os from configparser import RawConfigParser -from distutils.cmd import Command +from .cmd import Command DEFAULT_PYPIRC = """\ [distutils] diff --git a/setuptools/_distutils/core.py b/setuptools/_distutils/core.py index de13978..34cafbc 100644 --- a/setuptools/_distutils/core.py +++ b/setuptools/_distutils/core.py @@ -10,8 +10,8 @@ import os import sys import tokenize -from distutils.debug import DEBUG -from distutils.errors import ( +from .debug import DEBUG +from .errors import ( DistutilsSetupError, DistutilsError, CCompilerError, @@ -19,10 +19,10 @@ from distutils.errors import ( ) # Mainly import these so setup scripts can "from distutils.core import" them. -from distutils.dist import Distribution -from distutils.cmd import Command -from distutils.config import PyPIRCCommand -from distutils.extension import Extension +from .dist import Distribution +from .cmd import Command +from .config import PyPIRCCommand +from .extension import Extension __all__ = ['Distribution', 'Command', 'PyPIRCCommand', 'Extension', 'setup'] diff --git a/setuptools/_distutils/cygwinccompiler.py b/setuptools/_distutils/cygwinccompiler.py index 2c4da5b..f15b8ee 100644 --- a/setuptools/_distutils/cygwinccompiler.py +++ b/setuptools/_distutils/cygwinccompiler.py @@ -7,56 +7,61 @@ cygwin in no-cygwin mode). """ import os +import re import sys import copy import shlex import warnings from subprocess import check_output -from distutils.unixccompiler import UnixCCompiler -from distutils.file_util import write_file -from distutils.errors import ( +from .unixccompiler import UnixCCompiler +from .file_util import write_file +from .errors import ( DistutilsExecError, DistutilsPlatformError, CCompilerError, CompileError, ) -from distutils.version import LooseVersion, suppress_known_deprecation +from .version import LooseVersion, suppress_known_deprecation +from ._collections import RangeMap + + +_msvcr_lookup = RangeMap.left( + { + # MSVC 7.0 + 1300: ['msvcr70'], + # MSVC 7.1 + 1310: ['msvcr71'], + # VS2005 / MSVC 8.0 + 1400: ['msvcr80'], + # VS2008 / MSVC 9.0 + 1500: ['msvcr90'], + # VS2010 / MSVC 10.0 + 1600: ['msvcr100'], + # VS2012 / MSVC 11.0 + 1700: ['msvcr110'], + # VS2013 / MSVC 12.0 + 1800: ['msvcr120'], + # VS2015 / MSVC 14.0 + 1900: ['ucrt', 'vcruntime140'], + 2000: RangeMap.undefined_value, + }, +) def get_msvcr(): """Include the appropriate MSVC runtime library if Python was built with MSVC 7.0 or later. """ - msc_pos = sys.version.find('MSC v.') - if msc_pos != -1: - msc_ver = sys.version[msc_pos + 6 : msc_pos + 10] - if msc_ver == '1300': - # MSVC 7.0 - return ['msvcr70'] - elif msc_ver == '1310': - # MSVC 7.1 - return ['msvcr71'] - elif msc_ver == '1400': - # VS2005 / MSVC 8.0 - return ['msvcr80'] - elif msc_ver == '1500': - # VS2008 / MSVC 9.0 - return ['msvcr90'] - elif msc_ver == '1600': - # VS2010 / MSVC 10.0 - return ['msvcr100'] - elif msc_ver == '1700': - # VS2012 / MSVC 11.0 - return ['msvcr110'] - elif msc_ver == '1800': - # VS2013 / MSVC 12.0 - return ['msvcr120'] - elif 1900 <= int(msc_ver) < 2000: - # VS2015 / MSVC 14.0 - return ['ucrt', 'vcruntime140'] - else: - raise ValueError("Unknown MS Compiler version %s " % msc_ver) + match = re.search(r'MSC v\.(\d{4})', sys.version) + try: + msc_ver = int(match.group(1)) + except AttributeError: + return + try: + return _msvcr_lookup[msc_ver] + except KeyError: + raise ValueError("Unknown MS Compiler version %s " % msc_ver) _runtime_library_dirs_msg = ( @@ -280,17 +285,6 @@ class Mingw32CCompiler(CygwinCCompiler): linker_so='{} {}'.format(self.linker_dll, shared_option), ) - # Maybe we should also append -mthreads, but then the finished - # dlls need another dll (mingwm10.dll see Mingw32 docs) - # (-mthreads: Support thread-safe exception handling on `Mingw32') - - # no additional libraries needed - self.dll_libraries = [] - - # Include the appropriate MSVC runtime library if Python was built - # with MSVC 7.0 or later. - self.dll_libraries = get_msvcr() - def runtime_library_dir_option(self, dir): raise DistutilsPlatformError(_runtime_library_dirs_msg) diff --git a/setuptools/_distutils/dep_util.py b/setuptools/_distutils/dep_util.py index db1fa01..48da864 100644 --- a/setuptools/_distutils/dep_util.py +++ b/setuptools/_distutils/dep_util.py @@ -5,7 +5,7 @@ and groups of files; also, function based entirely on such timestamp dependency analysis.""" import os -from distutils.errors import DistutilsFileError +from .errors import DistutilsFileError def newer(source, target): diff --git a/setuptools/_distutils/dir_util.py b/setuptools/_distutils/dir_util.py index 6f0bb8a..80f7764 100644 --- a/setuptools/_distutils/dir_util.py +++ b/setuptools/_distutils/dir_util.py @@ -4,8 +4,8 @@ Utility functions for manipulating directories and directory trees.""" import os import errno -from distutils.errors import DistutilsInternalError, DistutilsFileError -from distutils import log +from .errors import DistutilsInternalError, DistutilsFileError +from ._log import log # cache for by mkpath() -- in addition to cheapening redundant calls, # eliminates redundant "creating /foo/bar/baz" messages in dry-run mode @@ -229,7 +229,7 @@ def remove_tree(directory, verbose=1, dry_run=0): if abspath in _path_created: del _path_created[abspath] except OSError as exc: - log.warn("error removing %s: %s", directory, exc) + log.warning("error removing %s: %s", directory, exc) def ensure_relative(path): diff --git a/setuptools/_distutils/dist.py b/setuptools/_distutils/dist.py index 917cd94..d7458a0 100644 --- a/setuptools/_distutils/dist.py +++ b/setuptools/_distutils/dist.py @@ -9,6 +9,7 @@ import os import re import pathlib import contextlib +import logging from email import message_from_file try: @@ -16,16 +17,16 @@ try: except ImportError: warnings = None -from distutils.errors import ( +from .errors import ( DistutilsOptionError, DistutilsModuleError, DistutilsArgError, DistutilsClassError, ) -from distutils.fancy_getopt import FancyGetopt, translate_longopt -from distutils.util import check_environ, strtobool, rfc822_escape -from distutils import log -from distutils.debug import DEBUG +from .fancy_getopt import FancyGetopt, translate_longopt +from .util import check_environ, strtobool, rfc822_escape +from ._log import log +from .debug import DEBUG # Regex to define acceptable Distutils command names. This is not *quite* # the same as a Python NAME -- I don't allow leading underscores. The fact @@ -44,7 +45,7 @@ def _ensure_list(value, fieldname): typename = type(value).__name__ msg = "Warning: '{fieldname}' should be a list, got type '{typename}'" msg = msg.format(**locals()) - log.log(log.WARN, msg) + log.warning(msg) value = list(value) return value @@ -465,7 +466,7 @@ Common commands: (see '--help-commands' for more) parser.set_aliases({'licence': 'license'}) args = parser.getopt(args=self.script_args, object=self) option_order = parser.get_option_order() - log.set_verbosity(self.verbose) + logging.getLogger().setLevel(logging.WARN - 10 * self.verbose) # for display options we return immediately if self.handle_display_options(option_order): @@ -956,7 +957,7 @@ Common commands: (see '--help-commands' for more) # -- Methods that operate on the Distribution ---------------------- - def announce(self, msg, level=log.INFO): + def announce(self, msg, level=logging.INFO): log.log(level, msg) def run_commands(self): diff --git a/setuptools/_distutils/fancy_getopt.py b/setuptools/_distutils/fancy_getopt.py index 830f047..6abb884 100644 --- a/setuptools/_distutils/fancy_getopt.py +++ b/setuptools/_distutils/fancy_getopt.py @@ -12,7 +12,7 @@ import sys import string import re import getopt -from distutils.errors import DistutilsGetoptError, DistutilsArgError +from .errors import DistutilsGetoptError, DistutilsArgError # Much like command_re in distutils.core, this is close to but not quite # the same as a Python NAME -- except, in the spirit of most GNU diff --git a/setuptools/_distutils/file_util.py b/setuptools/_distutils/file_util.py index 1f1e444..1b7cd53 100644 --- a/setuptools/_distutils/file_util.py +++ b/setuptools/_distutils/file_util.py @@ -4,8 +4,8 @@ Utility functions for operating on single files. """ import os -from distutils.errors import DistutilsFileError -from distutils import log +from .errors import DistutilsFileError +from ._log import log # for generating verbose output in 'copy_file()' _copy_action = {None: 'copying', 'hard': 'hard linking', 'sym': 'symbolically linking'} diff --git a/setuptools/_distutils/filelist.py b/setuptools/_distutils/filelist.py index 987931a..6dadf92 100644 --- a/setuptools/_distutils/filelist.py +++ b/setuptools/_distutils/filelist.py @@ -9,9 +9,9 @@ import re import fnmatch import functools -from distutils.util import convert_path -from distutils.errors import DistutilsTemplateError, DistutilsInternalError -from distutils import log +from .util import convert_path +from .errors import DistutilsTemplateError, DistutilsInternalError +from ._log import log class FileList: @@ -120,13 +120,13 @@ class FileList: self.debug_print("include " + ' '.join(patterns)) for pattern in patterns: if not self.include_pattern(pattern, anchor=1): - log.warn("warning: no files found matching '%s'", pattern) + log.warning("warning: no files found matching '%s'", pattern) elif action == 'exclude': self.debug_print("exclude " + ' '.join(patterns)) for pattern in patterns: if not self.exclude_pattern(pattern, anchor=1): - log.warn( + log.warning( ( "warning: no previously-included files " "found matching '%s'" @@ -138,7 +138,7 @@ class FileList: self.debug_print("global-include " + ' '.join(patterns)) for pattern in patterns: if not self.include_pattern(pattern, anchor=0): - log.warn( + log.warning( ( "warning: no files found matching '%s' " "anywhere in distribution" @@ -150,7 +150,7 @@ class FileList: self.debug_print("global-exclude " + ' '.join(patterns)) for pattern in patterns: if not self.exclude_pattern(pattern, anchor=0): - log.warn( + log.warning( ( "warning: no previously-included files matching " "'%s' found anywhere in distribution" @@ -165,13 +165,13 @@ class FileList: msg = ( "warning: no files found matching '%s' " "under directory '%s'" ) - log.warn(msg, pattern, dir) + log.warning(msg, pattern, dir) elif action == 'recursive-exclude': self.debug_print("recursive-exclude {} {}".format(dir, ' '.join(patterns))) for pattern in patterns: if not self.exclude_pattern(pattern, prefix=dir): - log.warn( + log.warning( ( "warning: no previously-included files matching " "'%s' found under directory '%s'" @@ -183,12 +183,12 @@ class FileList: elif action == 'graft': self.debug_print("graft " + dir_pattern) if not self.include_pattern(None, prefix=dir_pattern): - log.warn("warning: no directories found matching '%s'", dir_pattern) + log.warning("warning: no directories found matching '%s'", dir_pattern) elif action == 'prune': self.debug_print("prune " + dir_pattern) if not self.exclude_pattern(None, prefix=dir_pattern): - log.warn( + log.warning( ("no previously-included directories found " "matching '%s'"), dir_pattern, ) diff --git a/setuptools/_distutils/log.py b/setuptools/_distutils/log.py index be25f6c..bb789c3 100644 --- a/setuptools/_distutils/log.py +++ b/setuptools/_distutils/log.py @@ -1,80 +1,38 @@ -"""A simple log mechanism styled after PEP 282.""" +""" +A simple log mechanism styled after PEP 282. -# The class here is styled after PEP 282 so that it could later be -# replaced with a standard Python logging implementation. +Retained for compatibility and should not be used. +""" -import sys +import logging -DEBUG = 1 -INFO = 2 -WARN = 3 -ERROR = 4 -FATAL = 5 +from ._log import log as _global_log -class Log: - def __init__(self, threshold=WARN): - self.threshold = threshold +DEBUG = logging.DEBUG +INFO = logging.INFO +WARN = logging.WARN +ERROR = logging.ERROR +FATAL = logging.FATAL - def _log(self, level, msg, args): - if level not in (DEBUG, INFO, WARN, ERROR, FATAL): - raise ValueError('%s wrong log level' % str(level)) - - if level >= self.threshold: - if args: - msg = msg % args - if level in (WARN, ERROR, FATAL): - stream = sys.stderr - else: - stream = sys.stdout - try: - stream.write('%s\n' % msg) - except UnicodeEncodeError: - # emulate backslashreplace error handler - encoding = stream.encoding - msg = msg.encode(encoding, "backslashreplace").decode(encoding) - stream.write('%s\n' % msg) - stream.flush() - - def log(self, level, msg, *args): - self._log(level, msg, args) - - def debug(self, msg, *args): - self._log(DEBUG, msg, args) - - def info(self, msg, *args): - self._log(INFO, msg, args) - - def warn(self, msg, *args): - self._log(WARN, msg, args) - - def error(self, msg, *args): - self._log(ERROR, msg, args) - - def fatal(self, msg, *args): - self._log(FATAL, msg, args) - - -_global_log = Log() log = _global_log.log debug = _global_log.debug info = _global_log.info -warn = _global_log.warn +warn = _global_log.warning error = _global_log.error fatal = _global_log.fatal def set_threshold(level): - # return the old threshold for use from tests - old = _global_log.threshold - _global_log.threshold = level - return old + orig = _global_log.level + _global_log.setLevel(level) + return orig def set_verbosity(v): if v <= 0: - set_threshold(WARN) + set_threshold(logging.WARN) elif v == 1: - set_threshold(INFO) + set_threshold(logging.INFO) elif v >= 2: - set_threshold(DEBUG) + set_threshold(logging.DEBUG) diff --git a/setuptools/_distutils/msvc9compiler.py b/setuptools/_distutils/msvc9compiler.py index 2202183..a4714a5 100644 --- a/setuptools/_distutils/msvc9compiler.py +++ b/setuptools/_distutils/msvc9compiler.py @@ -18,16 +18,16 @@ import sys import re import warnings -from distutils.errors import ( +from .errors import ( DistutilsExecError, DistutilsPlatformError, CompileError, LibError, LinkError, ) -from distutils.ccompiler import CCompiler, gen_lib_options -from distutils import log -from distutils.util import get_platform +from .ccompiler import CCompiler, gen_lib_options +from ._log import log +from .util import get_platform import winreg diff --git a/setuptools/_distutils/msvccompiler.py b/setuptools/_distutils/msvccompiler.py index 1069e99..59ebe99 100644 --- a/setuptools/_distutils/msvccompiler.py +++ b/setuptools/_distutils/msvccompiler.py @@ -11,15 +11,15 @@ for the Microsoft Visual Studio. import sys import os import warnings -from distutils.errors import ( +from .errors import ( DistutilsExecError, DistutilsPlatformError, CompileError, LibError, LinkError, ) -from distutils.ccompiler import CCompiler, gen_lib_options -from distutils import log +from .ccompiler import CCompiler, gen_lib_options +from ._log import log _can_read_reg = False try: diff --git a/setuptools/_distutils/spawn.py b/setuptools/_distutils/spawn.py index b18ba9d..afefe52 100644 --- a/setuptools/_distutils/spawn.py +++ b/setuptools/_distutils/spawn.py @@ -10,9 +10,9 @@ import sys import os import subprocess -from distutils.errors import DistutilsExecError -from distutils.debug import DEBUG -from distutils import log +from .errors import DistutilsExecError +from .debug import DEBUG +from ._log import log def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None): # noqa: C901 diff --git a/setuptools/_distutils/sysconfig.py b/setuptools/_distutils/sysconfig.py index 6a979f8..0ec6936 100644 --- a/setuptools/_distutils/sysconfig.py +++ b/setuptools/_distutils/sysconfig.py @@ -537,13 +537,7 @@ def get_config_vars(*args): _config_vars = sysconfig.get_config_vars().copy() py39compat.add_ext_suffix(_config_vars) - if args: - vals = [] - for name in args: - vals.append(_config_vars.get(name)) - return vals - else: - return _config_vars + return [_config_vars.get(name) for name in args] if args else _config_vars def get_config_var(name): diff --git a/setuptools/_distutils/tests/support.py b/setuptools/_distutils/tests/support.py index 5203ed1..fd4b11b 100644 --- a/setuptools/_distutils/tests/support.py +++ b/setuptools/_distutils/tests/support.py @@ -5,29 +5,14 @@ import shutil import tempfile import sysconfig import itertools +import pathlib import pytest +from more_itertools import always_iterable -from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL from distutils.core import Distribution -@pytest.mark.usefixtures('distutils_logging_silencer') -class LoggingSilencer: - def _log(self, level, msg, args): - if level not in (DEBUG, INFO, WARN, ERROR, FATAL): - raise ValueError('%s wrong log level' % str(level)) - if not isinstance(msg, str): - raise TypeError("msg should be str, not '%.200s'" % (type(msg).__name__)) - self.logs.append((level, msg, args)) - - def get_logs(self, *levels): - return [msg % args for level, msg, args in self.logs if level in levels] - - def clear_logs(self): - self.logs = [] - - @pytest.mark.usefixtures('distutils_managed_tempdir') class TempdirManager: """ @@ -46,16 +31,9 @@ class TempdirManager: def write_file(self, path, content='xxx'): """Writes a file in the given path. - path can be a string or a sequence. """ - if isinstance(path, (list, tuple)): - path = os.path.join(*path) - f = open(path, 'w') - try: - f.write(content) - finally: - f.close() + pathlib.Path(*always_iterable(path)).write_text(content) def create_dist(self, pkg_name='foo', **kw): """Will generate a test environment. diff --git a/setuptools/_distutils/tests/test_archive_util.py b/setuptools/_distutils/tests/test_archive_util.py index d0f5b73..7778c3a 100644 --- a/setuptools/_distutils/tests/test_archive_util.py +++ b/setuptools/_distutils/tests/test_archive_util.py @@ -48,7 +48,7 @@ def same_drive(*paths): return all_equal(pathlib.Path(path).drive for path in paths) -class ArchiveUtilTestCase(support.TempdirManager, support.LoggingSilencer): +class ArchiveUtilTestCase(support.TempdirManager): @pytest.mark.usefixtures('needs_zlib') def test_make_tarball(self, name='archive'): # creating something to tar diff --git a/setuptools/_distutils/tests/test_bdist_dumb.py b/setuptools/_distutils/tests/test_bdist_dumb.py index 8624a42..b9bec05 100644 --- a/setuptools/_distutils/tests/test_bdist_dumb.py +++ b/setuptools/_distutils/tests/test_bdist_dumb.py @@ -26,7 +26,6 @@ setup(name='foo', version='0.1', py_modules=['foo'], @pytest.mark.usefixtures('save_cwd') class TestBuildDumb( support.TempdirManager, - support.LoggingSilencer, ): @pytest.mark.usefixtures('needs_zlib') def test_simple_built(self): diff --git a/setuptools/_distutils/tests/test_bdist_rpm.py b/setuptools/_distutils/tests/test_bdist_rpm.py index 2d14baf..4a702fb 100644 --- a/setuptools/_distutils/tests/test_bdist_rpm.py +++ b/setuptools/_distutils/tests/test_bdist_rpm.py @@ -42,7 +42,6 @@ mac_woes = pytest.mark.skipif( @pytest.mark.usefixtures('save_cwd') class TestBuildRpm( support.TempdirManager, - support.LoggingSilencer, ): @mac_woes @requires_zlib() diff --git a/setuptools/_distutils/tests/test_build.py b/setuptools/_distutils/tests/test_build.py index 8036760..66d8af5 100644 --- a/setuptools/_distutils/tests/test_build.py +++ b/setuptools/_distutils/tests/test_build.py @@ -7,7 +7,7 @@ from distutils.tests import support from sysconfig import get_platform -class TestBuild(support.TempdirManager, support.LoggingSilencer): +class TestBuild(support.TempdirManager): def test_finalize_options(self): pkg_dir, dist = self.create_dist() cmd = build(dist) diff --git a/setuptools/_distutils/tests/test_build_clib.py b/setuptools/_distutils/tests/test_build_clib.py index c931c06..709d0b7 100644 --- a/setuptools/_distutils/tests/test_build_clib.py +++ b/setuptools/_distutils/tests/test_build_clib.py @@ -10,7 +10,7 @@ from distutils.errors import DistutilsSetupError from distutils.tests import support -class TestBuildCLib(support.TempdirManager, support.LoggingSilencer): +class TestBuildCLib(support.TempdirManager): def test_check_library_dist(self): pkg_dir, dist = self.create_dist() cmd = build_clib(dist) diff --git a/setuptools/_distutils/tests/test_build_ext.py b/setuptools/_distutils/tests/test_build_ext.py index cf6e989..f505848 100644 --- a/setuptools/_distutils/tests/test_build_ext.py +++ b/setuptools/_distutils/tests/test_build_ext.py @@ -18,7 +18,6 @@ from distutils.command.build_ext import build_ext from distutils import sysconfig from distutils.tests.support import ( TempdirManager, - LoggingSilencer, copy_xxmodule_c, fixup_build_ext, ) @@ -85,7 +84,7 @@ def extension_redirect(mod, path): @pytest.mark.usefixtures('user_site_dir') -class TestBuildExt(TempdirManager, LoggingSilencer): +class TestBuildExt(TempdirManager): def build_ext(self, *args, **kwargs): return build_ext(*args, **kwargs) diff --git a/setuptools/_distutils/tests/test_build_py.py b/setuptools/_distutils/tests/test_build_py.py index 63543dc..3bef9d7 100644 --- a/setuptools/_distutils/tests/test_build_py.py +++ b/setuptools/_distutils/tests/test_build_py.py @@ -2,7 +2,6 @@ import os import sys -import unittest.mock as mock import pytest @@ -14,7 +13,7 @@ from distutils.tests import support @support.combine_markers -class TestBuildPy(support.TempdirManager, support.LoggingSilencer): +class TestBuildPy(support.TempdirManager): def test_package_data(self): sources = self.mkdtemp() f = open(os.path.join(sources, "__init__.py"), "w") @@ -151,7 +150,7 @@ class TestBuildPy(support.TempdirManager, support.LoggingSilencer): except DistutilsFileError: self.fail("failed package_data when data dir includes a dir") - def test_dont_write_bytecode(self): + def test_dont_write_bytecode(self, caplog): # makes sure byte_compile is not used dist = self.create_dist()[1] cmd = build_py(dist) @@ -165,10 +164,9 @@ class TestBuildPy(support.TempdirManager, support.LoggingSilencer): finally: sys.dont_write_bytecode = old_dont_write_bytecode - assert 'byte-compiling is disabled' in self.logs[0][1] % self.logs[0][2] + assert 'byte-compiling is disabled' in caplog.records[0].message - @mock.patch("distutils.command.build_py.log.warn") - def test_namespace_package_does_not_warn(self, log_warn): + def test_namespace_package_does_not_warn(self, caplog): """ Originally distutils implementation did not account for PEP 420 and included warns for package directories that did not contain @@ -182,13 +180,6 @@ class TestBuildPy(support.TempdirManager, support.LoggingSilencer): os.makedirs("ns/pkg") open("ns/pkg/module.py", "w").close() - # Set up a trap if the undesirable effect is observed: - def _trap(msg, *args): - if "package init file" in msg and "not found" in msg: - raise AssertionError(f"Undesired warning: {msg!r} {args!r}") - - log_warn.side_effect = _trap - # Configure the package: attrs = { "name": "ns.pkg", @@ -206,4 +197,7 @@ class TestBuildPy(support.TempdirManager, support.LoggingSilencer): assert module_path.replace(os.sep, "/") == "ns/pkg/module.py" cmd.run() - # Test should complete successfully with no exception + + assert not any( + "package init file" in msg and "not found" in msg for msg in caplog.messages + ) diff --git a/setuptools/_distutils/tests/test_build_scripts.py b/setuptools/_distutils/tests/test_build_scripts.py index 00d7fc5..1a5753c 100644 --- a/setuptools/_distutils/tests/test_build_scripts.py +++ b/setuptools/_distutils/tests/test_build_scripts.py @@ -9,7 +9,7 @@ from distutils import sysconfig from distutils.tests import support -class TestBuildScripts(support.TempdirManager, support.LoggingSilencer): +class TestBuildScripts(support.TempdirManager): def test_default_settings(self): cmd = self.get_build_scripts_cmd("/foo/bar", []) assert not cmd.force diff --git a/setuptools/_distutils/tests/test_check.py b/setuptools/_distutils/tests/test_check.py index 3e5f603..5465406 100644 --- a/setuptools/_distutils/tests/test_check.py +++ b/setuptools/_distutils/tests/test_check.py @@ -18,7 +18,7 @@ HERE = os.path.dirname(__file__) @support.combine_markers -class TestCheck(support.LoggingSilencer, support.TempdirManager): +class TestCheck(support.TempdirManager): def _run(self, metadata=None, cwd=None, **options): if metadata is None: metadata = {} diff --git a/setuptools/_distutils/tests/test_clean.py b/setuptools/_distutils/tests/test_clean.py index 4166bb7..157b60a 100644 --- a/setuptools/_distutils/tests/test_clean.py +++ b/setuptools/_distutils/tests/test_clean.py @@ -5,7 +5,7 @@ from distutils.command.clean import clean from distutils.tests import support -class TestClean(support.TempdirManager, support.LoggingSilencer): +class TestClean(support.TempdirManager): def test_simple_run(self): pkg_dir, dist = self.create_dist() cmd = clean(dist) diff --git a/setuptools/_distutils/tests/test_cmd.py b/setuptools/_distutils/tests/test_cmd.py index e4d5bf3..3aac448 100644 --- a/setuptools/_distutils/tests/test_cmd.py +++ b/setuptools/_distutils/tests/test_cmd.py @@ -1,6 +1,5 @@ """Tests for distutils.cmd.""" import os -from test.support import captured_stdout from distutils.cmd import Command from distutils.dist import Distribution @@ -100,17 +99,9 @@ class TestCommand: with pytest.raises(DistutilsOptionError): cmd.ensure_dirname('option2') - def test_debug_print(self, cmd): - with captured_stdout() as stdout: - cmd.debug_print('xxx') - stdout.seek(0) - assert stdout.read() == '' - - debug.DEBUG = True - try: - with captured_stdout() as stdout: - cmd.debug_print('xxx') - stdout.seek(0) - assert stdout.read() == 'xxx\n' - finally: - debug.DEBUG = False + def test_debug_print(self, cmd, capsys, monkeypatch): + cmd.debug_print('xxx') + assert capsys.readouterr().out == '' + monkeypatch.setattr(debug, 'DEBUG', True) + cmd.debug_print('xxx') + assert capsys.readouterr().out == 'xxx\n' diff --git a/setuptools/_distutils/tests/test_config.py b/setuptools/_distutils/tests/test_config.py index 43ba676..1ae615d 100644 --- a/setuptools/_distutils/tests/test_config.py +++ b/setuptools/_distutils/tests/test_config.py @@ -46,12 +46,8 @@ password:xxx @support.combine_markers -@pytest.mark.usefixtures('threshold_warn') @pytest.mark.usefixtures('pypirc') -class BasePyPIRCCommandTestCase( - support.TempdirManager, - support.LoggingSilencer, -): +class BasePyPIRCCommandTestCase(support.TempdirManager): pass diff --git a/setuptools/_distutils/tests/test_config_cmd.py b/setuptools/_distutils/tests/test_config_cmd.py index 65c60f6..e72a7c5 100644 --- a/setuptools/_distutils/tests/test_config_cmd.py +++ b/setuptools/_distutils/tests/test_config_cmd.py @@ -7,7 +7,7 @@ import pytest from distutils.command.config import dump_file, config from distutils.tests import support -from distutils import log +from distutils._log import log @pytest.fixture(autouse=True) @@ -18,7 +18,7 @@ def info_log(request, monkeypatch): @support.combine_markers -class TestConfig(support.LoggingSilencer, support.TempdirManager): +class TestConfig(support.TempdirManager): def _info(self, msg, *args): for line in msg.splitlines(): self._logs.append(line) diff --git a/setuptools/_distutils/tests/test_core.py b/setuptools/_distutils/tests/test_core.py index 5fe7e95..2c11ff7 100644 --- a/setuptools/_distutils/tests/test_core.py +++ b/setuptools/_distutils/tests/test_core.py @@ -4,7 +4,6 @@ import io import distutils.core import os import sys -from test.support import captured_stdout import pytest @@ -121,20 +120,12 @@ class TestCore: distutils.core.run_commands(dist) assert 'build' in dist.have_run - def test_debug_mode(self): + def test_debug_mode(self, capsys, monkeypatch): # this covers the code called when DEBUG is set sys.argv = ['setup.py', '--name'] - with captured_stdout() as stdout: - distutils.core.setup(name='bar') - stdout.seek(0) - assert stdout.read() == 'bar\n' - - distutils.core.DEBUG = True - try: - with captured_stdout() as stdout: - distutils.core.setup(name='bar') - finally: - distutils.core.DEBUG = False - stdout.seek(0) + distutils.core.setup(name='bar') + capsys.readouterr().out == 'bar\n' + monkeypatch.setattr(distutils.core, 'DEBUG', True) + distutils.core.setup(name='bar') wanted = "options (after parsing config files):\n" - assert stdout.readlines()[0] == wanted + assert capsys.readouterr().out.startswith(wanted) diff --git a/setuptools/_distutils/tests/test_dir_util.py b/setuptools/_distutils/tests/test_dir_util.py index cd7e018..0c6db4a 100644 --- a/setuptools/_distutils/tests/test_dir_util.py +++ b/setuptools/_distutils/tests/test_dir_util.py @@ -12,7 +12,6 @@ from distutils.dir_util import ( ensure_relative, ) -from distutils import log from distutils.tests import support import pytest @@ -20,36 +19,26 @@ import pytest @pytest.fixture(autouse=True) def stuff(request, monkeypatch, distutils_managed_tempdir): self = request.instance - self._logs = [] tmp_dir = self.mkdtemp() self.root_target = os.path.join(tmp_dir, 'deep') self.target = os.path.join(self.root_target, 'here') self.target2 = os.path.join(tmp_dir, 'deep2') - monkeypatch.setattr(log, 'info', self._log) class TestDirUtil(support.TempdirManager): - def _log(self, msg, *args): - if len(args) > 0: - self._logs.append(msg % args) - else: - self._logs.append(msg) - - def test_mkpath_remove_tree_verbosity(self): - + def test_mkpath_remove_tree_verbosity(self, caplog): mkpath(self.target, verbose=0) - wanted = [] - assert self._logs == wanted + assert not caplog.records remove_tree(self.root_target, verbose=0) mkpath(self.target, verbose=1) wanted = ['creating %s' % self.root_target, 'creating %s' % self.target] - assert self._logs == wanted - self._logs = [] + assert caplog.messages == wanted + caplog.clear() remove_tree(self.root_target, verbose=1) wanted = ["removing '%s' (and everything under it)" % self.root_target] - assert self._logs == wanted + assert caplog.messages == wanted @pytest.mark.skipif("platform.system() == 'Windows'") def test_mkpath_with_custom_mode(self): @@ -61,24 +50,24 @@ class TestDirUtil(support.TempdirManager): mkpath(self.target2, 0o555) assert stat.S_IMODE(os.stat(self.target2).st_mode) == 0o555 & ~umask - def test_create_tree_verbosity(self): + def test_create_tree_verbosity(self, caplog): create_tree(self.root_target, ['one', 'two', 'three'], verbose=0) - assert self._logs == [] + assert caplog.messages == [] remove_tree(self.root_target, verbose=0) wanted = ['creating %s' % self.root_target] create_tree(self.root_target, ['one', 'two', 'three'], verbose=1) - assert self._logs == wanted + assert caplog.messages == wanted remove_tree(self.root_target, verbose=0) - def test_copy_tree_verbosity(self): + def test_copy_tree_verbosity(self, caplog): mkpath(self.target, verbose=0) copy_tree(self.target, self.target2, verbose=0) - assert self._logs == [] + assert caplog.messages == [] remove_tree(self.root_target, verbose=0) @@ -89,7 +78,7 @@ class TestDirUtil(support.TempdirManager): wanted = ['copying {} -> {}'.format(a_file, self.target2)] copy_tree(self.target, self.target2, verbose=1) - assert self._logs == wanted + assert caplog.messages == wanted remove_tree(self.root_target, verbose=0) remove_tree(self.target2, verbose=0) diff --git a/setuptools/_distutils/tests/test_dist.py b/setuptools/_distutils/tests/test_dist.py index 52e0b3c..b5e81d0 100644 --- a/setuptools/_distutils/tests/test_dist.py +++ b/setuptools/_distutils/tests/test_dist.py @@ -13,9 +13,7 @@ import jaraco.path from distutils.dist import Distribution, fix_help_options from distutils.cmd import Command -from test.support import captured_stdout, captured_stderr from distutils.tests import support -from distutils import log pydistutils_cfg = '.' * (os.name == 'posix') + 'pydistutils.cfg' @@ -52,10 +50,7 @@ def clear_argv(): @support.combine_markers @pytest.mark.usefixtures('save_env') @pytest.mark.usefixtures('save_argv') -class TestDistributionBehavior( - support.LoggingSilencer, - support.TempdirManager, -): +class TestDistributionBehavior(support.TempdirManager): def create_distribution(self, configfiles=()): d = TestDistribution() d._config_files = configfiles @@ -240,10 +235,8 @@ class TestDistributionBehavior( def test_announce(self): # make sure the level is known dist = Distribution() - args = ('ok',) - kwargs = {'level': 'ok2'} - with pytest.raises(ValueError): - dist.announce(args, kwargs) + with pytest.raises(TypeError): + dist.announce('ok', level='ok2') def test_find_config_files_disable(self, temp_home): # Ticket #1180: Allow user to disable their home config file. @@ -373,16 +366,15 @@ class TestMetadata(support.TempdirManager): meta = self.format_metadata(dist) assert 'Metadata-Version: 1.1' in meta - def test_classifier_invalid_type(self): + def test_classifier_invalid_type(self, caplog): attrs = { 'name': 'Boa', 'version': '3.0', 'classifiers': ('Programming Language :: Python :: 3',), } - with captured_stderr() as error: - d = Distribution(attrs) + d = Distribution(attrs) # should have warning about passing a non-list - assert 'should be a list' in error.getvalue() + assert 'should be a list' in caplog.messages[0] # should be converted to a list assert isinstance(d.metadata.classifiers, list) assert d.metadata.classifiers == list(attrs['classifiers']) @@ -396,16 +388,15 @@ class TestMetadata(support.TempdirManager): dist = Distribution(attrs) assert dist.get_keywords() == ['spam', 'eggs', 'life of brian'] - def test_keywords_invalid_type(self): + def test_keywords_invalid_type(self, caplog): attrs = { 'name': 'Monty', 'version': '1.0', 'keywords': ('spam', 'eggs', 'life of brian'), } - with captured_stderr() as error: - d = Distribution(attrs) + d = Distribution(attrs) # should have warning about passing a non-list - assert 'should be a list' in error.getvalue() + assert 'should be a list' in caplog.messages[0] # should be converted to a list assert isinstance(d.metadata.keywords, list) assert d.metadata.keywords == list(attrs['keywords']) @@ -419,16 +410,15 @@ class TestMetadata(support.TempdirManager): dist = Distribution(attrs) assert dist.get_platforms() == ['GNU/Linux', 'Some Evil Platform'] - def test_platforms_invalid_types(self): + def test_platforms_invalid_types(self, caplog): attrs = { 'name': 'Monty', 'version': '1.0', 'platforms': ('GNU/Linux', 'Some Evil Platform'), } - with captured_stderr() as error: - d = Distribution(attrs) + d = Distribution(attrs) # should have warning about passing a non-list - assert 'should be a list' in error.getvalue() + assert 'should be a list' in caplog.messages[0] # should be converted to a list assert isinstance(d.metadata.platforms, list) assert d.metadata.platforms == list(attrs['platforms']) @@ -479,18 +469,17 @@ class TestMetadata(support.TempdirManager): assert fancy_options[0] == ('a', 'b', 'c') assert fancy_options[1] == (1, 2, 3) - def test_show_help(self, request): + def test_show_help(self, request, capsys): # smoke test, just makes sure some help is displayed - reset_log = functools.partial(log.set_threshold, log._global_log.threshold) - request.addfinalizer(reset_log) dist = Distribution() sys.argv = [] dist.help = 1 dist.script_name = 'setup.py' - with captured_stdout() as s: - dist.parse_command_line() + dist.parse_command_line() - output = [line for line in s.getvalue().split('\n') if line.strip() != ''] + output = [ + line for line in capsys.readouterr().out.split('\n') if line.strip() != '' + ] assert output def test_read_metadata(self): diff --git a/setuptools/_distutils/tests/test_file_util.py b/setuptools/_distutils/tests/test_file_util.py index b2e83c5..9f44f91 100644 --- a/setuptools/_distutils/tests/test_file_util.py +++ b/setuptools/_distutils/tests/test_file_util.py @@ -4,7 +4,6 @@ import errno import unittest.mock as mock from distutils.file_util import move_file, copy_file -from distutils import log from distutils.tests import support from distutils.errors import DistutilsFileError from .py38compat import unlink @@ -14,22 +13,14 @@ import pytest @pytest.fixture(autouse=True) def stuff(request, monkeypatch, distutils_managed_tempdir): self = request.instance - self._logs = [] tmp_dir = self.mkdtemp() self.source = os.path.join(tmp_dir, 'f1') self.target = os.path.join(tmp_dir, 'f2') self.target_dir = os.path.join(tmp_dir, 'd1') - monkeypatch.setattr(log, 'info', self._log) class TestFileUtil(support.TempdirManager): - def _log(self, msg, *args): - if len(args) > 0: - self._logs.append(msg % args) - else: - self._logs.append(msg) - - def test_move_file_verbosity(self): + def test_move_file_verbosity(self, caplog): f = open(self.source, 'w') try: f.write('some content') @@ -37,25 +28,24 @@ class TestFileUtil(support.TempdirManager): f.close() move_file(self.source, self.target, verbose=0) - wanted = [] - assert self._logs == wanted + assert not caplog.messages # back to original state move_file(self.target, self.source, verbose=0) move_file(self.source, self.target, verbose=1) wanted = ['moving {} -> {}'.format(self.source, self.target)] - assert self._logs == wanted + assert caplog.messages == wanted # back to original state move_file(self.target, self.source, verbose=0) - self._logs = [] + caplog.clear() # now the target is a dir os.mkdir(self.target_dir) move_file(self.source, self.target_dir, verbose=1) wanted = ['moving {} -> {}'.format(self.source, self.target_dir)] - assert self._logs == wanted + assert caplog.messages == wanted def test_move_file_exception_unpacking_rename(self): # see issue 22182 diff --git a/setuptools/_distutils/tests/test_filelist.py b/setuptools/_distutils/tests/test_filelist.py index 7ff9d3e..2cee42c 100644 --- a/setuptools/_distutils/tests/test_filelist.py +++ b/setuptools/_distutils/tests/test_filelist.py @@ -1,18 +1,16 @@ """Tests for distutils.filelist.""" import os import re +import logging + from distutils import debug -from distutils.log import WARN from distutils.errors import DistutilsTemplateError from distutils.filelist import glob_to_re, translate_pattern, FileList from distutils import filelist -from test.support import captured_stdout - import pytest import jaraco.path -from distutils.tests import support from . import py38compat as os_helper @@ -37,14 +35,16 @@ def make_local_path(s): return s.replace('/', os.sep) -class TestFileList(support.LoggingSilencer): - def assertNoWarnings(self): - assert self.get_logs(WARN) == [] - self.clear_logs() +class TestFileList: + def assertNoWarnings(self, caplog): + warnings = [rec for rec in caplog.records if rec.levelno == logging.WARNING] + assert not warnings + caplog.clear() - def assertWarnings(self): - assert len(self.get_logs(WARN)) > 0 - self.clear_logs() + def assertWarnings(self, caplog): + warnings = [rec for rec in caplog.records if rec.levelno == logging.WARNING] + assert warnings + caplog.clear() def test_glob_to_re(self): sep = os.sep @@ -110,19 +110,14 @@ class TestFileList(support.LoggingSilencer): assert file_list.files == wanted - def test_debug_print(self): + def test_debug_print(self, capsys, monkeypatch): file_list = FileList() - with captured_stdout() as stdout: - file_list.debug_print('xxx') - assert stdout.getvalue() == '' - - debug.DEBUG = True - try: - with captured_stdout() as stdout: - file_list.debug_print('xxx') - assert stdout.getvalue() == 'xxx\n' - finally: - debug.DEBUG = False + file_list.debug_print('xxx') + assert capsys.readouterr().out == '' + + monkeypatch.setattr(debug, 'DEBUG', True) + file_list.debug_print('xxx') + assert capsys.readouterr().out == 'xxx\n' def test_set_allfiles(self): file_list = FileList() @@ -188,7 +183,7 @@ class TestFileList(support.LoggingSilencer): file_list.include_pattern('*') assert file_list.allfiles == ['a.py', 'b.txt'] - def test_process_template(self): + def test_process_template(self, caplog): mlp = make_local_path # invalid lines file_list = FileList() @@ -212,11 +207,11 @@ class TestFileList(support.LoggingSilencer): file_list.process_template_line('include *.py') assert file_list.files == ['a.py'] - self.assertNoWarnings() + self.assertNoWarnings(caplog) file_list.process_template_line('include *.rb') assert file_list.files == ['a.py'] - self.assertWarnings() + self.assertWarnings(caplog) # exclude file_list = FileList() @@ -224,11 +219,11 @@ class TestFileList(support.LoggingSilencer): file_list.process_template_line('exclude *.py') assert file_list.files == ['b.txt', mlp('d/c.py')] - self.assertNoWarnings() + self.assertNoWarnings(caplog) file_list.process_template_line('exclude *.rb') assert file_list.files == ['b.txt', mlp('d/c.py')] - self.assertWarnings() + self.assertWarnings(caplog) # global-include file_list = FileList() @@ -236,11 +231,11 @@ class TestFileList(support.LoggingSilencer): file_list.process_template_line('global-include *.py') assert file_list.files == ['a.py', mlp('d/c.py')] - self.assertNoWarnings() + self.assertNoWarnings(caplog) file_list.process_template_line('global-include *.rb') assert file_list.files == ['a.py', mlp('d/c.py')] - self.assertWarnings() + self.assertWarnings(caplog) # global-exclude file_list = FileList() @@ -248,11 +243,11 @@ class TestFileList(support.LoggingSilencer): file_list.process_template_line('global-exclude *.py') assert file_list.files == ['b.txt'] - self.assertNoWarnings() + self.assertNoWarnings(caplog) file_list.process_template_line('global-exclude *.rb') assert file_list.files == ['b.txt'] - self.assertWarnings() + self.assertWarnings(caplog) # recursive-include file_list = FileList() @@ -260,11 +255,11 @@ class TestFileList(support.LoggingSilencer): file_list.process_template_line('recursive-include d *.py') assert file_list.files == [mlp('d/b.py'), mlp('d/d/e.py')] - self.assertNoWarnings() + self.assertNoWarnings(caplog) file_list.process_template_line('recursive-include e *.py') assert file_list.files == [mlp('d/b.py'), mlp('d/d/e.py')] - self.assertWarnings() + self.assertWarnings(caplog) # recursive-exclude file_list = FileList() @@ -272,11 +267,11 @@ class TestFileList(support.LoggingSilencer): file_list.process_template_line('recursive-exclude d *.py') assert file_list.files == ['a.py', mlp('d/c.txt')] - self.assertNoWarnings() + self.assertNoWarnings(caplog) file_list.process_template_line('recursive-exclude e *.py') assert file_list.files == ['a.py', mlp('d/c.txt')] - self.assertWarnings() + self.assertWarnings(caplog) # graft file_list = FileList() @@ -284,11 +279,11 @@ class TestFileList(support.LoggingSilencer): file_list.process_template_line('graft d') assert file_list.files == [mlp('d/b.py'), mlp('d/d/e.py')] - self.assertNoWarnings() + self.assertNoWarnings(caplog) file_list.process_template_line('graft e') assert file_list.files == [mlp('d/b.py'), mlp('d/d/e.py')] - self.assertWarnings() + self.assertWarnings(caplog) # prune file_list = FileList() @@ -296,11 +291,11 @@ class TestFileList(support.LoggingSilencer): file_list.process_template_line('prune d') assert file_list.files == ['a.py', mlp('f/f.py')] - self.assertNoWarnings() + self.assertNoWarnings(caplog) file_list.process_template_line('prune e') assert file_list.files == ['a.py', mlp('f/f.py')] - self.assertWarnings() + self.assertWarnings(caplog) class TestFindAll: diff --git a/setuptools/_distutils/tests/test_install.py b/setuptools/_distutils/tests/test_install.py index 32a18b2..102218b 100644 --- a/setuptools/_distutils/tests/test_install.py +++ b/setuptools/_distutils/tests/test_install.py @@ -4,8 +4,7 @@ import os import sys import site import pathlib - -from test.support import captured_stdout +import logging import pytest @@ -30,7 +29,6 @@ def _make_ext_name(modname): @pytest.mark.usefixtures('save_env') class TestInstall( support.TempdirManager, - support.LoggingSilencer, ): @pytest.mark.xfail( 'platform.system() == "Windows" and sys.version_info > (3, 11)', @@ -246,13 +244,9 @@ class TestInstall( ] assert found == expected - def test_debug_mode(self): + def test_debug_mode(self, caplog, monkeypatch): # this covers the code called when DEBUG is set - old_logs_len = len(self.logs) - install_module.DEBUG = True - try: - with captured_stdout(): - self.test_record() - finally: - install_module.DEBUG = False - assert len(self.logs) > old_logs_len + monkeypatch.setattr(install_module, 'DEBUG', True) + caplog.set_level(logging.DEBUG) + self.test_record() + assert any(rec for rec in caplog.records if rec.levelno == logging.DEBUG) diff --git a/setuptools/_distutils/tests/test_install_data.py b/setuptools/_distutils/tests/test_install_data.py index f77c790..9badbc2 100644 --- a/setuptools/_distutils/tests/test_install_data.py +++ b/setuptools/_distutils/tests/test_install_data.py @@ -10,7 +10,6 @@ from distutils.tests import support @pytest.mark.usefixtures('save_env') class TestInstallData( support.TempdirManager, - support.LoggingSilencer, ): def test_simple_run(self): pkg_dir, dist = self.create_dist() diff --git a/setuptools/_distutils/tests/test_install_headers.py b/setuptools/_distutils/tests/test_install_headers.py index 7594f5a..1e8ccf7 100644 --- a/setuptools/_distutils/tests/test_install_headers.py +++ b/setuptools/_distutils/tests/test_install_headers.py @@ -10,7 +10,6 @@ from distutils.tests import support @pytest.mark.usefixtures('save_env') class TestInstallHeaders( support.TempdirManager, - support.LoggingSilencer, ): def test_simple_run(self): # we have two headers diff --git a/setuptools/_distutils/tests/test_install_lib.py b/setuptools/_distutils/tests/test_install_lib.py index a654a66..0bd67cd 100644 --- a/setuptools/_distutils/tests/test_install_lib.py +++ b/setuptools/_distutils/tests/test_install_lib.py @@ -15,7 +15,6 @@ from distutils.errors import DistutilsOptionError @pytest.mark.usefixtures('save_env') class TestInstallLib( support.TempdirManager, - support.LoggingSilencer, ): def test_finalize_options(self): dist = self.create_dist()[1] @@ -94,7 +93,7 @@ class TestInstallLib( inputs = cmd.get_inputs() assert len(inputs) == 2, inputs - def test_dont_write_bytecode(self): + def test_dont_write_bytecode(self, caplog): # makes sure byte_compile is not used dist = self.create_dist()[1] cmd = install_lib(dist) @@ -108,4 +107,4 @@ class TestInstallLib( finally: sys.dont_write_bytecode = old_dont_write_bytecode - assert 'byte-compiling is disabled' in self.logs[0][1] % self.logs[0][2] + assert 'byte-compiling is disabled' in caplog.messages[0] diff --git a/setuptools/_distutils/tests/test_install_scripts.py b/setuptools/_distutils/tests/test_install_scripts.py index 0d17f11..58313f2 100644 --- a/setuptools/_distutils/tests/test_install_scripts.py +++ b/setuptools/_distutils/tests/test_install_scripts.py @@ -8,7 +8,7 @@ from distutils.core import Distribution from distutils.tests import support -class TestInstallScripts(support.TempdirManager, support.LoggingSilencer): +class TestInstallScripts(support.TempdirManager): def test_default_settings(self): dist = Distribution() dist.command_obj["build"] = support.DummyCommand(build_scripts="/foo/bar") diff --git a/setuptools/_distutils/tests/test_log.py b/setuptools/_distutils/tests/test_log.py index 7aeee40..ec6a0c8 100644 --- a/setuptools/_distutils/tests/test_log.py +++ b/setuptools/_distutils/tests/test_log.py @@ -1,52 +1,13 @@ """Tests for distutils.log""" -import io -import sys -from test.support import swap_attr +import logging -import pytest - -from distutils import log +from distutils._log import log class TestLog: - @pytest.mark.parametrize( - 'errors', - ( - 'strict', - 'backslashreplace', - 'surrogateescape', - 'replace', - 'ignore', - ), - ) - def test_non_ascii(self, errors): - # Issues #8663, #34421: test that non-encodable text is escaped with - # backslashreplace error handler and encodable non-ASCII text is - # output as is. - stdout = io.TextIOWrapper(io.BytesIO(), encoding='cp437', errors=errors) - stderr = io.TextIOWrapper(io.BytesIO(), encoding='cp437', errors=errors) - old_threshold = log.set_threshold(log.DEBUG) - try: - with swap_attr(sys, 'stdout', stdout), swap_attr(sys, 'stderr', stderr): - log.debug('Dεbug\tMėssãge') - log.fatal('Fαtal\tÈrrōr') - finally: - log.set_threshold(old_threshold) - - stdout.seek(0) - assert stdout.read().rstrip() == ( - 'Dεbug\tM?ss?ge' - if errors == 'replace' - else 'Dεbug\tMssge' - if errors == 'ignore' - else 'Dεbug\tM\\u0117ss\\xe3ge' - ) - stderr.seek(0) - assert stderr.read().rstrip() == ( - 'Fαtal\t?rr?r' - if errors == 'replace' - else 'Fαtal\trrr' - if errors == 'ignore' - else 'Fαtal\t\\xc8rr\\u014dr' - ) + def test_non_ascii(self, caplog): + caplog.set_level(logging.DEBUG) + log.debug('Dεbug\tMėssãge') + log.fatal('Fαtal\tÈrrōr') + assert caplog.messages == ['Dεbug\tMėssãge', 'Fαtal\tÈrrōr'] diff --git a/setuptools/_distutils/tests/test_register.py b/setuptools/_distutils/tests/test_register.py index 0a5765f..a10393b 100644 --- a/setuptools/_distutils/tests/test_register.py +++ b/setuptools/_distutils/tests/test_register.py @@ -6,7 +6,6 @@ import urllib from distutils.command import register as register_module from distutils.command.register import register from distutils.errors import DistutilsSetupError -from distutils.log import INFO from distutils.tests.test_config import BasePyPIRCCommandTestCase import pytest @@ -303,14 +302,13 @@ class TestRegister(BasePyPIRCCommandTestCase): with pytest.raises(DistutilsSetupError): cmd.run() - def test_list_classifiers(self): + def test_list_classifiers(self, caplog): cmd = self._get_cmd() cmd.list_classifiers = 1 cmd.run() - results = self.get_logs(INFO) - assert results == ['running check', 'xxx'] + assert caplog.messages == ['running check', 'xxx'] - def test_show_response(self): + def test_show_response(self, caplog): # test that the --show-response option return a well formatted response cmd = self._get_cmd() inputs = Inputs('1', 'tarek', 'y') @@ -321,5 +319,4 @@ class TestRegister(BasePyPIRCCommandTestCase): finally: del register_module.input - results = self.get_logs(INFO) - assert results[3] == 75 * '-' + '\nxxx\n' + 75 * '-' + assert caplog.messages[3] == 75 * '-' + '\nxxx\n' + 75 * '-' diff --git a/setuptools/_distutils/tests/test_sdist.py b/setuptools/_distutils/tests/test_sdist.py index b11fe7c..9750472 100644 --- a/setuptools/_distutils/tests/test_sdist.py +++ b/setuptools/_distutils/tests/test_sdist.py @@ -5,7 +5,6 @@ import warnings import zipfile from os.path import join from textwrap import dedent -from test.support import captured_stdout from .unix_compat import require_unix_id, require_uid_0, pwd, grp import pytest @@ -19,7 +18,6 @@ from distutils.core import Distribution from distutils.tests.test_config import BasePyPIRCCommandTestCase from distutils.errors import DistutilsOptionError from distutils.spawn import find_executable # noqa: F401 -from distutils.log import WARN from distutils.filelist import FileList from distutils.archive_util import ARCHIVE_FORMATS @@ -252,8 +250,12 @@ class TestSDist(BasePyPIRCCommandTestCase): f.close() assert manifest == MANIFEST % {'sep': os.sep} + @staticmethod + def warnings(messages, prefix='warning: '): + return [msg for msg in messages if msg.startswith(prefix)] + @pytest.mark.usefixtures('needs_zlib') - def test_metadata_check_option(self): + def test_metadata_check_option(self, caplog): # testing the `medata-check` option dist, cmd = self.get_cmd(metadata={}) @@ -261,21 +263,15 @@ class TestSDist(BasePyPIRCCommandTestCase): # with the `check` subcommand cmd.ensure_finalized() cmd.run() - warnings = [ - msg for msg in self.get_logs(WARN) if msg.startswith('warning: check:') - ] - assert len(warnings) == 1 + assert len(self.warnings(caplog.messages, 'warning: check: ')) == 1 # trying with a complete set of metadata - self.clear_logs() + caplog.clear() dist, cmd = self.get_cmd() cmd.ensure_finalized() cmd.metadata_check = 0 cmd.run() - warnings = [ - msg for msg in self.get_logs(WARN) if msg.startswith('warning: check:') - ] - assert len(warnings) == 0 + assert len(self.warnings(caplog.messages, 'warning: check: ')) == 0 def test_check_metadata_deprecated(self): # makes sure make_metadata is deprecated @@ -285,15 +281,14 @@ class TestSDist(BasePyPIRCCommandTestCase): cmd.check_metadata() assert len(w.warnings) == 1 - def test_show_formats(self): - with captured_stdout() as stdout: - show_formats() + def test_show_formats(self, capsys): + show_formats() # the output should be a header line + one line per format num_formats = len(ARCHIVE_FORMATS.keys()) output = [ line - for line in stdout.getvalue().split('\n') + for line in capsys.readouterr().out.split('\n') if line.strip().startswith('--formats=') ] assert len(output) == num_formats @@ -323,28 +318,27 @@ class TestSDist(BasePyPIRCCommandTestCase): # the following tests make sure there is a nice error message instead # of a traceback when parsing an invalid manifest template - def _check_template(self, content): + def _check_template(self, content, caplog): dist, cmd = self.get_cmd() os.chdir(self.tmp_dir) self.write_file('MANIFEST.in', content) cmd.ensure_finalized() cmd.filelist = FileList() cmd.read_template() - warnings = self.get_logs(WARN) - assert len(warnings) == 1 + assert len(self.warnings(caplog.messages)) == 1 - def test_invalid_template_unknown_command(self): - self._check_template('taunt knights *') + def test_invalid_template_unknown_command(self, caplog): + self._check_template('taunt knights *', caplog) - def test_invalid_template_wrong_arguments(self): + def test_invalid_template_wrong_arguments(self, caplog): # this manifest command takes one argument - self._check_template('prune') + self._check_template('prune', caplog) @pytest.mark.skipif("platform.system() != 'Windows'") - def test_invalid_template_wrong_path(self): + def test_invalid_template_wrong_path(self, caplog): # on Windows, trailing slashes are not allowed # this used to crash instead of raising a warning: #8286 - self._check_template('include examples/') + self._check_template('include examples/', caplog) @pytest.mark.usefixtures('needs_zlib') def test_get_file_list(self): diff --git a/setuptools/_distutils/tests/test_spawn.py b/setuptools/_distutils/tests/test_spawn.py index 5da4997..08a34ee 100644 --- a/setuptools/_distutils/tests/test_spawn.py +++ b/setuptools/_distutils/tests/test_spawn.py @@ -17,7 +17,7 @@ from distutils.tests import support import pytest -class TestSpawn(support.TempdirManager, support.LoggingSilencer): +class TestSpawn(support.TempdirManager): @pytest.mark.skipif("os.name not in ('nt', 'posix')") def test_spawn(self): tmpdir = self.mkdtemp() diff --git a/setuptools/_distutils/tests/test_upload.py b/setuptools/_distutils/tests/test_upload.py index fb905b6..9685c06 100644 --- a/setuptools/_distutils/tests/test_upload.py +++ b/setuptools/_distutils/tests/test_upload.py @@ -8,7 +8,6 @@ from distutils.command import upload as upload_mod from distutils.command.upload import upload from distutils.core import Distribution from distutils.errors import DistutilsError -from distutils.log import ERROR, INFO from distutils.tests.test_config import PYPIRC, BasePyPIRCCommandTestCase import pytest @@ -109,7 +108,7 @@ class TestUpload(BasePyPIRCCommandTestCase): cmd.finalize_options() assert cmd.password == 'xxx' - def test_upload(self): + def test_upload(self, caplog): tmp = self.mkdtemp() path = os.path.join(tmp, 'xxx') self.write_file(path) @@ -150,7 +149,7 @@ class TestUpload(BasePyPIRCCommandTestCase): ) # The PyPI response body was echoed - results = self.get_logs(INFO) + results = caplog.messages assert results[-1] == 75 * '-' + '\nxyzzy\n' + 75 * '-' # bpo-32304: archives whose last byte was b'\r' were corrupted due to @@ -178,11 +177,11 @@ class TestUpload(BasePyPIRCCommandTestCase): assert int(headers['Content-length']) >= 2172 assert b'long description\r' in self.last_open.req.data - def test_upload_fails(self): + def test_upload_fails(self, caplog): self.next_msg = "Not Found" self.next_code = 404 with pytest.raises(DistutilsError): - self.test_upload() + self.test_upload(caplog) @pytest.mark.parametrize( 'exception,expected,raised_exception', @@ -196,7 +195,7 @@ class TestUpload(BasePyPIRCCommandTestCase): ), ], ) - def test_wrong_exception_order(self, exception, expected, raised_exception): + def test_wrong_exception_order(self, exception, expected, raised_exception, caplog): tmp = self.mkdtemp() path = os.path.join(tmp, 'xxx') self.write_file(path) @@ -213,6 +212,6 @@ class TestUpload(BasePyPIRCCommandTestCase): cmd = upload(dist) cmd.ensure_finalized() cmd.run() - results = self.get_logs(ERROR) + results = caplog.messages assert expected in results[-1] - self.clear_logs() + caplog.clear() diff --git a/setuptools/_distutils/unixccompiler.py b/setuptools/_distutils/unixccompiler.py index 4ab771a..4bf2e6a 100644 --- a/setuptools/_distutils/unixccompiler.py +++ b/setuptools/_distutils/unixccompiler.py @@ -19,11 +19,11 @@ import re import shlex import itertools -from distutils import sysconfig -from distutils.dep_util import newer -from distutils.ccompiler import CCompiler, gen_preprocess_options, gen_lib_options -from distutils.errors import DistutilsExecError, CompileError, LibError, LinkError -from distutils import log +from . import sysconfig +from .dep_util import newer +from .ccompiler import CCompiler, gen_preprocess_options, gen_lib_options +from .errors import DistutilsExecError, CompileError, LibError, LinkError +from ._log import log from ._macos_compat import compiler_fixup # XXX Things not currently handled: diff --git a/setuptools/_distutils/util.py b/setuptools/_distutils/util.py index 4763202..8668b43 100644 --- a/setuptools/_distutils/util.py +++ b/setuptools/_distutils/util.py @@ -13,10 +13,10 @@ import sys import sysconfig import functools -from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError -from distutils.dep_util import newer -from distutils.spawn import spawn -from distutils import log +from .errors import DistutilsPlatformError, DistutilsByteCompileError +from .dep_util import newer +from .spawn import spawn +from ._log import log def get_host_platform(): diff --git a/setuptools/_distutils/versionpredicate.py b/setuptools/_distutils/versionpredicate.py index 6ea1192..d6c0c00 100644 --- a/setuptools/_distutils/versionpredicate.py +++ b/setuptools/_distutils/versionpredicate.py @@ -1,7 +1,7 @@ """Module for parsing and testing package version predicate strings. """ import re -import distutils.version +from . import version import operator @@ -22,8 +22,8 @@ def splitUp(pred): if not res: raise ValueError("bad package restriction syntax: %r" % pred) comp, verStr = res.groups() - with distutils.version.suppress_known_deprecation(): - other = distutils.version.StrictVersion(verStr) + with version.suppress_known_deprecation(): + other = version.StrictVersion(verStr) return (comp, other) @@ -170,6 +170,6 @@ def split_provision(value): raise ValueError("illegal provides specification: %r" % value) ver = m.group(2) or None if ver: - with distutils.version.suppress_known_deprecation(): - ver = distutils.version.StrictVersion(ver) + with version.suppress_known_deprecation(): + ver = version.StrictVersion(ver) return m.group(1), ver diff --git a/setuptools/tests/config/test_apply_pyprojecttoml.py b/setuptools/tests/config/test_apply_pyprojecttoml.py index 2194197..3a66d49 100644 --- a/setuptools/tests/config/test_apply_pyprojecttoml.py +++ b/setuptools/tests/config/test_apply_pyprojecttoml.py @@ -98,7 +98,9 @@ authors = [ {name = "Tzu-Ping Chung"} ] maintainers = [ - {name = "Brett Cannon", email = "brett@python.org"} + {name = "Brett Cannon", email = "brett@python.org"}, + {name = "John X. Ãørçeč", email = "john@utf8.org"}, + {name = "Γαμα קּ 東", email = "gama@utf8.org"}, ] classifiers = [ "Development Status :: 4 - Beta", @@ -134,6 +136,19 @@ spam-gui = "spam:main_gui" tomatoes = "spam:main_tomatoes" """ +PEP621_INTERNATIONAL_EMAIL_EXAMPLE = """\ +[project] +name = "spam" +version = "2020.0.0" +authors = [ + {email = "hi@pradyunsg.me"}, + {name = "Tzu-Ping Chung"} +] +maintainers = [ + {name = "Степан Бандера", email = "криївка@оун-упа.укр"}, +] +""" + PEP621_EXAMPLE_SCRIPT = """ def main_cli(): pass def main_gui(): pass @@ -141,13 +156,17 @@ def main_tomatoes(): pass """ -def _pep621_example_project(tmp_path, readme="README.rst"): +def _pep621_example_project( + tmp_path, + readme="README.rst", + pyproject_text=PEP621_EXAMPLE, +): pyproject = tmp_path / "pyproject.toml" - text = PEP621_EXAMPLE + text = pyproject_text replacements = {'readme = "README.rst"': f'readme = "{readme}"'} for orig, subst in replacements.items(): text = text.replace(orig, subst) - pyproject.write_text(text) + pyproject.write_text(text, encoding="utf-8") (tmp_path / readme).write_text("hello world") (tmp_path / "LICENSE.txt").write_text("--- LICENSE stub ---") @@ -189,6 +208,45 @@ def test_no_explicit_content_type_for_missing_extension(tmp_path): assert dist.metadata.long_description_content_type is None +@pytest.mark.parametrize( + ('pyproject_text', 'expected_maintainers_meta_value'), + ( + pytest.param( + PEP621_EXAMPLE, + ( + 'Brett Cannon , "John X. Ãørçeč" , ' + 'Γαμα קּ 東 ' + ), + id='non-international-emails', + ), + pytest.param( + PEP621_INTERNATIONAL_EMAIL_EXAMPLE, + 'Степан Бандера <криївка@оун-упа.укр>', + marks=pytest.mark.xfail( + reason="CPython's `email.headerregistry.Address` only supports " + 'RFC 5322, as of Nov 10, 2022 and latest Python 3.11.0', + strict=True, + ), + id='international-email', + ), + ), +) +def test_utf8_maintainer_in_metadata( # issue-3663 + expected_maintainers_meta_value, + pyproject_text, tmp_path, +): + pyproject = _pep621_example_project( + tmp_path, "README", pyproject_text=pyproject_text, + ) + dist = pyprojecttoml.apply_configuration(makedist(tmp_path), pyproject) + assert dist.metadata.maintainer_email == expected_maintainers_meta_value + pkg_file = tmp_path / "PKG-FILE" + with open(pkg_file, "w", encoding="utf-8") as fh: + dist.metadata.write_pkg_file(fh) + content = pkg_file.read_text(encoding="utf-8") + assert f"Maintainer-email: {expected_maintainers_meta_value}" in content + + # TODO: After PEP 639 is accepted, we have to move the license-files # to the `project` table instead of `tool.setuptools` def test_license_and_license_files(tmp_path): diff --git a/setuptools/tests/test_sdist.py b/setuptools/tests/test_sdist.py index 4b0d2e1..30631c2 100644 --- a/setuptools/tests/test_sdist.py +++ b/setuptools/tests/test_sdist.py @@ -28,11 +28,14 @@ SETUP_ATTRS = { 'data_files': [("data", [os.path.join("d", "e.dat")])], } -SETUP_PY = """\ +SETUP_PY = ( + """\ from setuptools import setup setup(**%r) -""" % SETUP_ATTRS +""" + % SETUP_ATTRS +) @contextlib.contextmanager @@ -86,6 +89,12 @@ fail_on_latin1_encoded_filenames = pytest.mark.xfail( ) +skip_under_xdist = pytest.mark.skipif( + "os.environ.get('PYTEST_XDIST_WORKER')", + reason="pytest-dev/pytest-xdist#843", +) + + def touch(path): path.write_text('', encoding='utf-8') @@ -319,6 +328,7 @@ class TestSdistTest: # The filelist should have been updated as well assert u_filename in mm.filelist.files + @skip_under_xdist def test_write_manifest_skips_non_utf8_filenames(self): """ Files that cannot be encoded to UTF-8 (specifically, those that @@ -451,13 +461,13 @@ class TestSdistTest: @classmethod def make_strings(cls, item): if isinstance(item, dict): - return { - key: cls.make_strings(value) for key, value in item.items()} + return {key: cls.make_strings(value) for key, value in item.items()} if isinstance(item, list): return list(map(cls.make_strings, item)) return str(item) @fail_on_latin1_encoded_filenames + @skip_under_xdist def test_sdist_with_latin1_encoded_filename(self): # Test for #303. dist = Distribution(self.make_strings(SETUP_ATTRS)) @@ -570,9 +580,11 @@ def test_default_revctrl(): This interface must be maintained until Ubuntu 12.04 is no longer supported (by Setuptools). """ - ep, = metadata.EntryPoints._from_text(""" + (ep,) = metadata.EntryPoints._from_text( + """ [setuptools.file_finders] svn_cvs = setuptools.command.sdist:_default_revctrl - """) + """ + ) res = ep.load() assert hasattr(res, '__iter__') -- 2.34.1