[bumpversion]
-current_version = 65.5.1
+current_version = 65.6.0
commit = True
tag = True
${{ 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:
steps:
- uses: actions/checkout@v3
- name: Setup Python
+ id: python-install
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}${{ matrix.dev }}
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
- 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:
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
release:
needs:
- check
- - test_cygwin
- - integration-test
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
timeout-minutes: 75
--- /dev/null
+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"
+++ /dev/null
-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"
+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
-------
-.. 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
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
--------
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
.. [#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).
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
[mypy]
ignore_missing_imports = True
+# required to support namespace packages
+# https://github.com/python/mypy/issues/14057
+explicit_package_bases = True
[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
+++ /dev/null
-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$
-"""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:
import collections
+import functools
import itertools
+import operator
# from jaraco.collections 3.5.1
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)
--- /dev/null
+import logging
+
+
+log = logging.getLogger()
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
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
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(
)
if runtime_library_dirs:
- log.warn(
+ log.warning(
"I don't know what to do with 'runtime_library_dirs': %s",
str(runtime_library_dirs),
)
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:
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:
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
"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):
# -- 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)
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:
$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):
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):
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()
# 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()
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
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,
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
def show_compilers():
- from distutils.ccompiler import show_compilers
+ from ..ccompiler import show_compilers
show_compilers()
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
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",
# 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'.
# 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
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')
# 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"
# 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'):
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):
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
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:
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].*)?$')
"""
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
# 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):
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
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"}
"""
# 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(
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
"""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:
'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:
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:
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
"""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:
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."
)
# 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):
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):
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
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.
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
# 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
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):
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:
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:
'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':
'''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'
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
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():
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)
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)
body = body.getvalue()
msg = "Submitting {} to {}".format(filename, self.repository)
- self.announce(msg, log.INFO)
+ self.announce(msg, logging.INFO)
# build the Request
headers = {
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)
import os
from configparser import RawConfigParser
-from distutils.cmd import Command
+from .cmd import Command
DEFAULT_PYPIRC = """\
[distutils]
import sys
import tokenize
-from distutils.debug import DEBUG
-from distutils.errors import (
+from .debug import DEBUG
+from .errors import (
DistutilsSetupError,
DistutilsError,
CCompilerError,
)
# 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']
"""
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 = (
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)
timestamp dependency analysis."""
import os
-from distutils.errors import DistutilsFileError
+from .errors import DistutilsFileError
def newer(source, target):
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
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):
import re
import pathlib
import contextlib
+import logging
from email import message_from_file
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
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
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):
# -- 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):
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
"""
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'}
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:
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'"
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"
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"
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'"
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,
)
-"""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)
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
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:
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
_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):
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:
"""
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.
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
@pytest.mark.usefixtures('save_cwd')
class TestBuildDumb(
support.TempdirManager,
- support.LoggingSilencer,
):
@pytest.mark.usefixtures('needs_zlib')
def test_simple_built(self):
@pytest.mark.usefixtures('save_cwd')
class TestBuildRpm(
support.TempdirManager,
- support.LoggingSilencer,
):
@mac_woes
@requires_zlib()
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)
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)
from distutils import sysconfig
from distutils.tests.support import (
TempdirManager,
- LoggingSilencer,
copy_xxmodule_c,
fixup_build_ext,
)
@pytest.mark.usefixtures('user_site_dir')
-class TestBuildExt(TempdirManager, LoggingSilencer):
+class TestBuildExt(TempdirManager):
def build_ext(self, *args, **kwargs):
return build_ext(*args, **kwargs)
import os
import sys
-import unittest.mock as mock
import pytest
@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")
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)
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
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",
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
+ )
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
@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 = {}
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)
"""Tests for distutils.cmd."""
import os
-from test.support import captured_stdout
from distutils.cmd import Command
from distutils.dist import Distribution
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'
@support.combine_markers
-@pytest.mark.usefixtures('threshold_warn')
@pytest.mark.usefixtures('pypirc')
-class BasePyPIRCCommandTestCase(
- support.TempdirManager,
- support.LoggingSilencer,
-):
+class BasePyPIRCCommandTestCase(support.TempdirManager):
pass
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)
@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)
import distutils.core
import os
import sys
-from test.support import captured_stdout
import pytest
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)
ensure_relative,
)
-from distutils import log
from distutils.tests import support
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):
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)
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)
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'
@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
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.
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'])
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'])
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'])
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):
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
@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')
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
"""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
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
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()
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()
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()
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()
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()
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()
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()
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()
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()
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:
import sys
import site
import pathlib
-
-from test.support import captured_stdout
+import logging
import pytest
@pytest.mark.usefixtures('save_env')
class TestInstall(
support.TempdirManager,
- support.LoggingSilencer,
):
@pytest.mark.xfail(
'platform.system() == "Windows" and sys.version_info > (3, 11)',
]
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)
@pytest.mark.usefixtures('save_env')
class TestInstallData(
support.TempdirManager,
- support.LoggingSilencer,
):
def test_simple_run(self):
pkg_dir, dist = self.create_dist()
@pytest.mark.usefixtures('save_env')
class TestInstallHeaders(
support.TempdirManager,
- support.LoggingSilencer,
):
def test_simple_run(self):
# we have two headers
@pytest.mark.usefixtures('save_env')
class TestInstallLib(
support.TempdirManager,
- support.LoggingSilencer,
):
def test_finalize_options(self):
dist = self.create_dist()[1]
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)
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]
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")
"""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']
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
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')
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 * '-'
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
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
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={})
# 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
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
# 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):
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()
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
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)
)
# 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
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',
),
],
)
- 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)
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()
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:
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():
"""Module for parsing and testing package version predicate strings.
"""
import re
-import distutils.version
+from . import version
import operator
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)
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
{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",
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
"""
-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 ---")
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):
'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
)
+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')
# 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
@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))
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__')