Imported Upstream version 65.6.0 upstream/65.6.0
authorJinWang An <jinwang.an@samsung.com>
Mon, 27 Mar 2023 08:02:57 +0000 (17:02 +0900)
committerJinWang An <jinwang.an@samsung.com>
Mon, 27 Mar 2023 08:02:57 +0000 (17:02 +0900)
88 files changed:
.bumpversion.cfg
.github/workflows/main.yml
.readthedocs.yaml [new file with mode: 0644]
.readthedocs.yml [deleted file]
CHANGES.rst
README.rst
docs/userguide/declarative_config.rst
docs/userguide/development_mode.rst
docs/userguide/pyproject_config.rst
mypy.ini
setup.cfg
setuptools/_distutils/README [deleted file]
setuptools/_distutils/__init__.py
setuptools/_distutils/_collections.py
setuptools/_distutils/_log.py [new file with mode: 0644]
setuptools/_distutils/_msvccompiler.py
setuptools/_distutils/archive_util.py
setuptools/_distutils/bcppcompiler.py
setuptools/_distutils/ccompiler.py
setuptools/_distutils/cmd.py
setuptools/_distutils/command/bdist.py
setuptools/_distutils/command/bdist_dumb.py
setuptools/_distutils/command/bdist_rpm.py
setuptools/_distutils/command/build.py
setuptools/_distutils/command/build_clib.py
setuptools/_distutils/command/build_ext.py
setuptools/_distutils/command/build_py.py
setuptools/_distutils/command/build_scripts.py
setuptools/_distutils/command/check.py
setuptools/_distutils/command/clean.py
setuptools/_distutils/command/config.py
setuptools/_distutils/command/install.py
setuptools/_distutils/command/install_data.py
setuptools/_distutils/command/install_egg_info.py
setuptools/_distutils/command/install_headers.py
setuptools/_distutils/command/install_lib.py
setuptools/_distutils/command/install_scripts.py
setuptools/_distutils/command/register.py
setuptools/_distutils/command/sdist.py
setuptools/_distutils/command/upload.py
setuptools/_distutils/config.py
setuptools/_distutils/core.py
setuptools/_distutils/cygwinccompiler.py
setuptools/_distutils/dep_util.py
setuptools/_distutils/dir_util.py
setuptools/_distutils/dist.py
setuptools/_distutils/fancy_getopt.py
setuptools/_distutils/file_util.py
setuptools/_distutils/filelist.py
setuptools/_distutils/log.py
setuptools/_distutils/msvc9compiler.py
setuptools/_distutils/msvccompiler.py
setuptools/_distutils/spawn.py
setuptools/_distutils/sysconfig.py
setuptools/_distutils/tests/support.py
setuptools/_distutils/tests/test_archive_util.py
setuptools/_distutils/tests/test_bdist_dumb.py
setuptools/_distutils/tests/test_bdist_rpm.py
setuptools/_distutils/tests/test_build.py
setuptools/_distutils/tests/test_build_clib.py
setuptools/_distutils/tests/test_build_ext.py
setuptools/_distutils/tests/test_build_py.py
setuptools/_distutils/tests/test_build_scripts.py
setuptools/_distutils/tests/test_check.py
setuptools/_distutils/tests/test_clean.py
setuptools/_distutils/tests/test_cmd.py
setuptools/_distutils/tests/test_config.py
setuptools/_distutils/tests/test_config_cmd.py
setuptools/_distutils/tests/test_core.py
setuptools/_distutils/tests/test_dir_util.py
setuptools/_distutils/tests/test_dist.py
setuptools/_distutils/tests/test_file_util.py
setuptools/_distutils/tests/test_filelist.py
setuptools/_distutils/tests/test_install.py
setuptools/_distutils/tests/test_install_data.py
setuptools/_distutils/tests/test_install_headers.py
setuptools/_distutils/tests/test_install_lib.py
setuptools/_distutils/tests/test_install_scripts.py
setuptools/_distutils/tests/test_log.py
setuptools/_distutils/tests/test_register.py
setuptools/_distutils/tests/test_sdist.py
setuptools/_distutils/tests/test_spawn.py
setuptools/_distutils/tests/test_upload.py
setuptools/_distutils/unixccompiler.py
setuptools/_distutils/util.py
setuptools/_distutils/versionpredicate.py
setuptools/tests/config/test_apply_pyprojecttoml.py
setuptools/tests/test_sdist.py

index 316cd4c71ed3bbf9a76770851260276205c03ae3..6a4b339c05f5fde977ef4126872fedac0952cd85 100644 (file)
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 65.5.1
+current_version = 65.6.0
 commit = True
 tag = True
 
index 8892a6cde0bcd48196060008cb36595c7aeedde0..a877b765857a9e6b90caca63e594d3afebe5b2c2 100644 (file)
@@ -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.yaml b/.readthedocs.yaml
new file mode 100644 (file)
index 0000000..6bef349
--- /dev/null
@@ -0,0 +1,13 @@
+version: 2
+python:
+  install:
+  - path: .
+    extra_requirements:
+      - docs
+
+# workaround for readthedocs/readthedocs.org#9623
+build:
+  # workaround for readthedocs/readthedocs.org#9635
+  os: ubuntu-22.04
+  tools:
+    python: "3"
diff --git a/.readthedocs.yml b/.readthedocs.yml
deleted file mode 100644 (file)
index 6bef349..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-version: 2
-python:
-  install:
-  - path: .
-    extra_requirements:
-      - docs
-
-# workaround for readthedocs/readthedocs.org#9623
-build:
-  # workaround for readthedocs/readthedocs.org#9635
-  os: ubuntu-22.04
-  tools:
-    python: "3"
index e2b8dbdc4fbbeec5c59ff8259602ff941a9e75f6..5a24ff7a03d4c5efd5d2614c84246ab1acd05507 100644 (file)
@@ -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
 -------
 
index e421de51b811f6336da9faacbe13032bfd7560f0..86197e765d77580c98c3c4f7c809aa8205bee784 100644 (file)
@@ -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
index c12f87bf18da20cd0fc799b81c1de926966b7abd..3ef7b074629c7cc415a503da42e0842c27a809e2 100644 (file)
@@ -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
 --------
index 2dc6dfbd0d22692c10f36a684465cede0226f741..1716e0afb444450598f5f48a9d8e62949563eddd 100644 (file)
@@ -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 <https://tox.wiki/en/stable/config.html#conf-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
    <https://blog.ganssle.io/articles/2019/08/test-as-installed.html>`_ for more
    insights).
 
index 88a6119af8a89d3b69357981037a646406543488..cc811ed737574ddb6779d395d0c467d31a446b88 100644 (file)
@@ -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
index 976ba0294638950e865be3934cbeee3b6305ffd6..b6f972769e61a5e65f9f45015b363cc362487e55 100644 (file)
--- 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
index 054fd047fc818a8e627ce819e7c5f6ad7dc757a2..a34af39c1d6a7bf9aba1f44d6fd62650ee5cb4ad 100644 (file)
--- 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 (file)
index 23f4885..0000000
+++ /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$
index b3ac0146cb3f4cb1894f55fc09775875bc4e1177..1a188c35cb6a82cfb7dfb6d8a813fed35bed0cc4 100644 (file)
@@ -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:
index 98fce8008dc25cb97d026426b47f898fccc0c34a..02556614a5049d230a9bc9bf6a0ae8670bb338fc 100644 (file)
@@ -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 (file)
index 0000000..4a2ae0a
--- /dev/null
@@ -0,0 +1,4 @@
+import logging
+
+
+log = logging.getLogger()
index 729c2dd5217528d7b3f9220cc2c7981f95c6f6e1..8b4023c4203d6ff5ab46638ca63d884b74e4d49e 100644 (file)
@@ -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
 
index 5dfe2a16ffbf5dc907aa3ce315757f4f9a055a82..7f9e1e00ccdb0e67a5601db4707a1cfa46cbc96f 100644 (file)
@@ -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
index 80b6bd852269afc075e38a4280c728f0777c923f..5d6b86536e0e04208e22298ee671b052189f8e72 100644 (file)
@@ -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),
             )
index 97551c99fec8ebdaa523bdc22dba80e3447c981a..646353111f6c22860edb2ceec8e5551dc2678dc7 100644 (file)
@@ -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:
index 68a9267c65babd799cec04213c20ad4f3289e109..918db85325c70c35dedef01549d1bbb54e60edf1 100644 (file)
@@ -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)
index de37dae0ffcd5ea3b05c2203981f23163707cdd6..bf0baab0d2697a07d76decf7a922f848d0316858 100644 (file)
@@ -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:
index 0f52330f67728e5f02d1673dc9683e95f6f9d294..071da77e18ce510879ecfc36739984e58f1ebd74 100644 (file)
@@ -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):
index 6a50ef34eab60cf005ea604f83eaf6170437032e..340527b08a24a4376a828cebc9dadaf02fb76a04 100644 (file)
@@ -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):
index 6d453419d073677e33ab60a5e627ba412be1fd6a..c3ab410f29a196d9292965e732947653415362a4 100644 (file)
@@ -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()
 
index 50bb9bbabb7ab00cd4763b524ab536e711e468a8..f90c56643236bebc49ae3004fff650d58ecd95d2 100644 (file)
@@ -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
index 3c6cee7e3644fdbdeeb4b5bcb0124044eb0f50ed..f4c0eccd4ff57000cd93c1263b605d5837caf3fb 100644 (file)
@@ -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'):
index 47c6158e0f74033bfcfeb7424df227a3815651de..9f78324452c6726e01d2958d23b802be94a44120 100644 (file)
@@ -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:
index 2cc5d1e09c09b6c674d47a26c5ebc6163705ecce..87174f6bb15b6b8119c019682da312df9389c131 100644 (file)
@@ -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].*)?$')
index 539481c946043c53aa61bd62cfd4b4146934697d..575e49fb4b14f9876f4e8fec076b1f5c9b6c72c3 100644 (file)
@@ -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
index b731b60609621ad822aa989ffa1f711ec2932278..d6eb3ebad6bea5034b48517a92f04cab1bc65ecf 100644 (file)
@@ -5,9 +5,9 @@ Implements the Distutils 'clean' command."""
 # contributed by Bastian Kleineidam <calvin@cs.uni-sb.de>, 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
index 4492c89660c202acf882375258dffafff00a99ba..8bf0e4893be9463b23cc13b38edd6344951e4e8c 100644 (file)
@@ -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:
index a38cddcda5380aac99bade87e2cdf95d4c99348a..08d2f8812fe3fb3ed43d4ad31acc236041dd9f97 100644 (file)
@@ -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."
             )
index 23d91aded26df8fdc5600ac2dda19787ec5ce916..d92ed87a64bb02dc194dfc4b66c6ec4339081b40 100644 (file)
@@ -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):
index d5e68a6e47199372c79ec094e0385f49a6600f22..f3e8f3447dc206799a8e124000a81c443adc870f 100644 (file)
@@ -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):
index 87046ab391b9f5e577e6ef0181c50de7e9c7f01b..1cdee823dc27e67e8a4dc2c75c9235276b5d1d40 100644 (file)
@@ -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
index ad3089c8b144f292e9560c8cefcbab4012d09a45..840d3403c4158f9c7298ca31d298f0322ec0f73e 100644 (file)
@@ -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
index f09bd644207e5c5a891d3605cb6aff4f00d70c8a..ec6ec5acaa40ceea9ac34db647bdcc5bb2f28c4e 100644 (file)
@@ -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
 
 
index c1402650d7f7defdde15741aabafa9f42843dcdf..55c1045ec6d2fb931da66b72b973c574e955b05a 100644 (file)
@@ -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
index d6e9489d1b1913f7090b225db69c42fc0454c17a..5cfd4c1456ee099c85e0186603a32afd2b52a182 100644 (file)
@@ -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)
index 6af5394339437497134fa74a97463e3e5309ff29..16e15d8b6050dc89cfab8583a74c8ce2d222825f 100644 (file)
@@ -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)
index 6e0c3a71f10cf216aaa19053564159353e47e66a..9a4044adaf876f57befa8cf37c5c23f8840a99f4 100644 (file)
@@ -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]
index de13978f02aa85ac70aa49a0d39178cbba913199..34cafbceec026b75ef9e76f1b152984abe8e7f24 100644 (file)
@@ -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']
index 2c4da5b57e5fda8b1510a61c2f14a61fac1c0916..f15b8eee26b51dddbd8ede357c8fcc337ee690ff 100644 (file)
@@ -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)
 
index db1fa01996ce0d47cd7f070c53b085926440d377..48da8641c636edede90d6200076eaa0a41c88fbc 100644 (file)
@@ -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):
index 6f0bb8ad76a064dad843db670c91e493d0e19a0c..80f7764902d194f54f1f07bac1178c263771d85b 100644 (file)
@@ -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):
index 917cd94a0c29985085f9332c5a73549c51bb8fb1..d7458a052fe1e83e47b77a2ba4ec9123071f26ea 100644 (file)
@@ -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):
index 830f047e28aa3b25295174d44d735448a1a43098..6abb884d36c9b9e4cfcd2186c4b5cc6795a05232 100644 (file)
@@ -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
index 1f1e444b1c30d93ca28ac15115ef73e63b9f6169..1b7cd53bd99a1b3f199a7309da961306fb58ca0a 100644 (file)
@@ -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'}
index 987931a9883ff36862dbd0831bd0a16903977879..6dadf923d71b29a07c93331c7913baccd8c01947 100644 (file)
@@ -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,
                 )
index be25f6cabd839af772dd74399c57991c222d3da8..bb789c300d64357971724ec6ba34a025b05f4ca4 100644 (file)
@@ -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)
index 22021831086ea8b2db134f7a2310a803abf5f982..a4714a559dcb695c996cadbac8be71ec9ccb40da 100644 (file)
@@ -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
 
index 1069e9951abd003cf347e3d151a6f6ca7340eb00..59ebe99caaa5fbee52b4780238a8749448c69686 100644 (file)
@@ -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:
index b18ba9db7d2e5919c853e7dcf8d5b7c180607c3f..afefe525ef13ac82b24b356ccce10e6f5141e4cf 100644 (file)
@@ -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
index 6a979f8c91fce3c8239b36ddb8764dc85dea41f2..0ec69366fd4d8db7ab9f76a958d035cbb891bf6b 100644 (file)
@@ -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):
index 5203ed19d4ab3b54517ac6c5a7a536ca42a6925e..fd4b11bf750beac11b347b6f696b4277c2f7f9e8 100644 (file)
@@ -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.
index d0f5b73481ae5ae92f1e546b1e4134b17ebcc8f9..7778c3ad363fd61657cbb63f328a29e76a046876 100644 (file)
@@ -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
index 8624a4290dd105fe768ea22518b20970a80a4900..b9bec05137d5a55761788c93b87d176598d725da 100644 (file)
@@ -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):
index 2d14bafc9823c24cbf1c9acf7a7a846be3ff107f..4a702fb913e89dafefb2d10a21ff94156617bf20 100644 (file)
@@ -42,7 +42,6 @@ mac_woes = pytest.mark.skipif(
 @pytest.mark.usefixtures('save_cwd')
 class TestBuildRpm(
     support.TempdirManager,
-    support.LoggingSilencer,
 ):
     @mac_woes
     @requires_zlib()
index 80367607f5401384a800a52663707fb5b2643ea1..66d8af50acaed2b436ad7aebf4d0529fd58b47b9 100644 (file)
@@ -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)
index c931c06ec5880fb1a39b4ac6eb0edc520ff8d1d3..709d0b7d66ad1612a749e0637307ef916a3952c7 100644 (file)
@@ -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)
index cf6e98985c97c3e0f561be2f74b15fccb97e3950..f5058487a5f1e7fe9a6680bbf979c243161caf2c 100644 (file)
@@ -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)
 
index 63543dcaa1a6e236417f61a765c7a09afc8c354d..3bef9d79eceb53f4b82fbd79b4dfd6726dfc6e2b 100644 (file)
@@ -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
+        )
index 00d7fc59292047bc8d2be84a0e4b71bbcacbe28e..1a5753c7729a37f8d536526d9c989d57b5016c9e 100644 (file)
@@ -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
index 3e5f6034bfb1c31edaa0768d48566f561867fa2a..546540679a1d45eae8856625141cbc453b7a0f13 100644 (file)
@@ -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 = {}
index 4166bb7e9bbbd1312822e369ec5cf394a84e27b4..157b60a1e98fddd8c3719e96a9b01aa563fcb0cb 100644 (file)
@@ -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)
index e4d5bf3c0167b0e1d93c08a38107ac8bc332b8f2..3aac448d5ecc0ee886c80adf1cb1cbcfb91ed781 100644 (file)
@@ -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'
index 43ba6766aef4e13d4a8a195c65ff59cb28be2a61..1ae615db955de8d7a9981df99161f75b9d1094e4 100644 (file)
@@ -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
 
 
index 65c60f64dd4f7613e990e70c82e7dbe16a782f0a..e72a7c5ff8d6b068b544d442a9fe21057da41087 100644 (file)
@@ -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)
index 5fe7e958f516cfe412362802e9ee16b7e786b304..2c11ff769eebec2e6199a19efa436a3305037bdf 100644 (file)
@@ -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)
index cd7e018f5e5219d98e6454f0fd1f209205e532e3..0c6db4afaebaac27bb3fd3a811f16b8c74cc7713 100644 (file)
@@ -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)
index 52e0b3ce26e7a0ad97559d64b2a2f07b27d46440..b5e81d0356317c11cca0deba97f14571bbf5c69d 100644 (file)
@@ -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):
index b2e83c52f2981beef8d720066c46855bbe9f99dc..9f44f91dfa87a541965290d59e08d0a99b009d88 100644 (file)
@@ -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
index 7ff9d3e8660a0dd5c7b6a01cbb201fd1616af21c..2cee42cddd15d0877d04b7caddd077f013805eb3 100644 (file)
@@ -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:
index 32a18b2f2f26b8ac543674c4cc48e7f41328d0ee..102218bc0070d84164dc4765124035c34675c413 100644 (file)
@@ -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)
index f77c790fcad59ee0784321efa4cbf506839975f6..9badbc264f81e397545bd1394b29e7326f594cf8 100644 (file)
@@ -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()
index 7594f5af3c6da13720f35fb10d489f4e91fee6da..1e8ccf7991cbb3b0c1a672ba47d56430b74c3ed2 100644 (file)
@@ -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
index a654a66a79b6a8a2bb2cdddd7422a6d756690bf3..0bd67cd04d8f633ec6924b5c9e2b592dda6f6303 100644 (file)
@@ -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]
index 0d17f11b5b8130f726dc70a81fd47df0447e7747..58313f28649d6e89ee44e89362af35ae0ff4c2ce 100644 (file)
@@ -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")
index 7aeee4057fdea53e11e98c32e4c282a4ca406928..ec6a0c805110eb2751377eac46d12c8bbc37b5c3 100644 (file)
@@ -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']
index 0a5765f1fdc231eecf03e08f97dbb2cb255abede..a10393b5e6065e5ce4db59d2965c9fbf59abc8f8 100644 (file)
@@ -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 * '-'
index b11fe7c41ea440929723dd64b16b2ca8dab58075..97504722acc79320f271e1efa2e7a6d0c9bc25a8 100644 (file)
@@ -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):
index 5da499777a52b40c77181c711e59e6cdd5e3d1b0..08a34ee2b8e35af6e9f9dbe6d983449a88a4a32c 100644 (file)
@@ -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()
index fb905b641ab819fc58eaac3ebd8540a1709aa154..9685c065f5e8f74a30028e7a0733355674113ca8 100644 (file)
@@ -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()
index 4ab771a475df8f53f4054d7869366a2457397a09..4bf2e6a681d74b618b86c12af3f436eca80aec36 100644 (file)
@@ -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:
index 4763202b67cf3b7dc849fcca401be5df6adbf083..8668b43699168b6358cc1d8d022f96ba3b63144b 100644 (file)
@@ -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():
index 6ea1192d4c22480c378cdf2279368bde203ea09d..d6c0c007aad871b9348fea57c9188d0ffd5f10d2 100644 (file)
@@ -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
index 2194197f82fa2534e5853b07b0f4aa152d5d73c8..3a66d494b315027b63b17ae658f0a0b470ffffcc 100644 (file)
@@ -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 <brett@python.org>, "John X. Ãørçeč" <john@utf8.org>, '
+                'Γαμα קּ 東 <gama@utf8.org>'
+            ),
+            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):
index 4b0d2e17c26caf031eeba2342acfc81acf8d1610..30631c242967e7569270e55ced238fe26e9f9121 100644 (file)
@@ -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__')