summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason R. Coombs <jaraco@jaraco.com>2022-08-13 13:16:37 -0400
committerJason R. Coombs <jaraco@jaraco.com>2022-08-13 13:16:37 -0400
commitc5cba301167d6351ef37a3b9982a6e9e5360b3d7 (patch)
treea074b603e06d3d9914f95cc854bfc655a443b6c9
parent1c6d72b5f0680ba3708fecd3d86c2b38b1d80bc0 (diff)
parente6062a16d91983d49ad3e54c6eabb7e4b32d1bc0 (diff)
downloadpython-setuptools-git-c5cba301167d6351ef37a3b9982a6e9e5360b3d7.tar.gz
Merge branch 'main' into debt/remove-bdist_msi
-rw-r--r--.github/workflows/main.yml4
-rw-r--r--conftest.py125
-rw-r--r--distutils/_msvccompiler.py37
-rw-r--r--distutils/archive_util.py2
-rw-r--r--distutils/bcppcompiler.py4
-rw-r--r--distutils/ccompiler.py61
-rw-r--r--distutils/cmd.py8
-rw-r--r--distutils/command/bdist.py54
-rw-r--r--distutils/command/bdist_dumb.py4
-rw-r--r--distutils/command/bdist_rpm.py8
-rw-r--r--distutils/command/bdist_wininst.py418
-rw-r--r--distutils/command/build.py2
-rw-r--r--distutils/command/build_ext.py2
-rw-r--r--distutils/command/check.py40
-rw-r--r--distutils/command/register.py12
-rw-r--r--distutils/command/sdist.py2
-rw-r--r--distutils/command/upload.py6
-rw-r--r--distutils/core.py8
-rw-r--r--distutils/cygwinccompiler.py70
-rw-r--r--distutils/dir_util.py6
-rw-r--r--distutils/dist.py10
-rw-r--r--distutils/extension.py2
-rw-r--r--distutils/fancy_getopt.py6
-rw-r--r--distutils/file_util.py20
-rw-r--r--distutils/filelist.py8
-rw-r--r--distutils/msvc9compiler.py820
-rw-r--r--distutils/msvccompiler.py684
-rw-r--r--distutils/py38compat.py2
-rw-r--r--distutils/spawn.py6
-rw-r--r--distutils/tests/py38compat.py12
-rw-r--r--distutils/tests/support.py85
-rw-r--r--distutils/tests/test_archive_util.py158
-rw-r--r--distutils/tests/test_bdist.py15
-rw-r--r--distutils/tests/test_bdist_dumb.py24
-rw-r--r--distutils/tests/test_bdist_rpm.py78
-rw-r--r--distutils/tests/test_bdist_wininst.py37
-rw-r--r--distutils/tests/test_build.py21
-rw-r--r--distutils/tests/test_build_clib.py63
-rw-r--r--distutils/tests/test_build_ext.py276
-rw-r--r--distutils/tests/test_build_py.py40
-rw-r--r--distutils/tests/test_build_scripts.py17
-rw-r--r--distutils/tests/test_ccompiler.py55
-rw-r--r--distutils/tests/test_check.py63
-rw-r--r--distutils/tests/test_clean.py5
-rw-r--r--distutils/tests/test_cmd.py72
-rw-r--r--distutils/tests/test_config.py49
-rw-r--r--distutils/tests/test_config_cmd.py43
-rw-r--r--distutils/tests/test_core.py51
-rw-r--r--distutils/tests/test_cygwinccompiler.py65
-rw-r--r--distutils/tests/test_dep_util.py26
-rw-r--r--distutils/tests/test_dir_util.py69
-rw-r--r--distutils/tests/test_dist.py252
-rw-r--r--distutils/tests/test_extension.py31
-rw-r--r--distutils/tests/test_file_util.py69
-rw-r--r--distutils/tests/test_filelist.py93
-rw-r--r--distutils/tests/test_install.py110
-rw-r--r--distutils/tests/test_install_data.py24
-rw-r--r--distutils/tests/test_install_headers.py8
-rw-r--r--distutils/tests/test_install_lib.py29
-rw-r--r--distutils/tests/test_install_scripts.py23
-rw-r--r--distutils/tests/test_log.py75
-rw-r--r--distutils/tests/test_msvc9compiler.py183
-rw-r--r--distutils/tests/test_msvccompiler.py52
-rw-r--r--distutils/tests/test_register.py98
-rw-r--r--distutils/tests/test_sdist.py118
-rw-r--r--distutils/tests/test_spawn.py51
-rw-r--r--distutils/tests/test_sysconfig.py172
-rw-r--r--distutils/tests/test_text_file.py5
-rw-r--r--distutils/tests/test_unixccompiler.py78
-rw-r--r--distutils/tests/test_upload.py109
-rw-r--r--distutils/tests/test_util.py153
-rw-r--r--distutils/tests/test_version.py54
-rw-r--r--distutils/tests/unix_compat.py9
-rw-r--r--distutils/tests/xxmodule-3.8.c411
-rw-r--r--distutils/tests/xxmodule.c412
-rw-r--r--distutils/text_file.py3
-rw-r--r--distutils/unixccompiler.py160
-rw-r--r--distutils/util.py4
-rw-r--r--distutils/version.py2
-rw-r--r--pyproject.toml9
-rw-r--r--pytest.ini23
-rw-r--r--tox.ini14
82 files changed, 2612 insertions, 3977 deletions
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index e244014d..62f6fcef 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -20,6 +20,10 @@ jobs:
- ubuntu-latest
- macos-latest
- windows-latest
+ exclude:
+ # macOS is failing to build pyobjc (#165)
+ - platform: macos-latest
+ python: ~3.11.0-0
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
diff --git a/conftest.py b/conftest.py
index 70999c9e..feac1b60 100644
--- a/conftest.py
+++ b/conftest.py
@@ -1,5 +1,7 @@
import os
+import sys
import platform
+import shutil
import pytest
@@ -31,3 +33,126 @@ def save_env():
@pytest.fixture
def needs_zlib():
pytest.importorskip('zlib')
+
+
+@pytest.fixture
+def distutils_logging_silencer(request):
+ from distutils import log
+
+ self = request.instance
+ self.threshold = log.set_threshold(log.FATAL)
+ # catching warnings
+ # when log will be replaced by logging
+ # we won't need such monkey-patch anymore
+ self._old_log = log.Log._log
+ log.Log._log = self._log
+ self.logs = []
+
+ try:
+ yield
+ finally:
+ log.set_threshold(self.threshold)
+ log.Log._log = self._old_log
+
+
+@pytest.fixture
+def distutils_managed_tempdir(request):
+ from distutils.tests import py38compat as os_helper
+
+ self = request.instance
+ self.old_cwd = os.getcwd()
+ self.tempdirs = []
+ try:
+ yield
+ finally:
+ # Restore working dir, for Solaris and derivatives, where rmdir()
+ # on the current directory fails.
+ os.chdir(self.old_cwd)
+ while self.tempdirs:
+ tmpdir = self.tempdirs.pop()
+ os_helper.rmtree(tmpdir)
+
+
+@pytest.fixture
+def save_argv():
+ orig = sys.argv[:]
+ try:
+ yield
+ finally:
+ sys.argv[:] = orig
+
+
+@pytest.fixture
+def save_cwd():
+ orig = os.getcwd()
+ try:
+ yield
+ finally:
+ os.chdir(orig)
+
+
+@pytest.fixture
+def threshold_warn():
+ from distutils.log import set_threshold, WARN
+
+ orig = set_threshold(WARN)
+ yield
+ set_threshold(orig)
+
+
+@pytest.fixture
+def pypirc(request, save_env, distutils_managed_tempdir):
+ from distutils.core import PyPIRCCommand
+ from distutils.core import Distribution
+
+ self = request.instance
+ self.tmp_dir = self.mkdtemp()
+ os.environ['HOME'] = self.tmp_dir
+ os.environ['USERPROFILE'] = self.tmp_dir
+ self.rc = os.path.join(self.tmp_dir, '.pypirc')
+ self.dist = Distribution()
+
+ class command(PyPIRCCommand):
+ def __init__(self, dist):
+ super().__init__(dist)
+
+ def initialize_options(self):
+ pass
+
+ finalize_options = initialize_options
+
+ self._cmd = command
+
+
+@pytest.fixture
+def cleanup_testfn():
+ from distutils.tests import py38compat as os_helper
+
+ yield
+ path = os_helper.TESTFN
+ if os.path.isfile(path):
+ os.remove(path)
+ elif os.path.isdir(path):
+ shutil.rmtree(path)
+
+
+# from pytest-dev/pytest#363
+@pytest.fixture(scope="session")
+def monkeysession(request):
+ from _pytest.monkeypatch import MonkeyPatch
+
+ mpatch = MonkeyPatch()
+ yield mpatch
+ mpatch.undo()
+
+
+@pytest.fixture(autouse=True, scope="session")
+def suppress_path_mangle(monkeysession):
+ """
+ Disable the path mangling in CCompiler. Workaround for #169.
+ """
+ from distutils import ccompiler
+
+ monkeysession.setattr(
+ ccompiler.CCompiler, '_mangle_base', staticmethod(lambda x: x)
+ )
diff --git a/distutils/_msvccompiler.py b/distutils/_msvccompiler.py
index aa0ceccb..ade80056 100644
--- a/distutils/_msvccompiler.py
+++ b/distutils/_msvccompiler.py
@@ -17,7 +17,7 @@ import os
import subprocess
import contextlib
import warnings
-import unittest.mock
+import unittest.mock as mock
with contextlib.suppress(ImportError):
import winreg
@@ -144,12 +144,12 @@ def _get_vc_env(plat_spec):
try:
out = subprocess.check_output(
- 'cmd /u /c "{}" {} && set'.format(vcvarsall, plat_spec),
+ f'cmd /u /c "{vcvarsall}" {plat_spec} && set',
stderr=subprocess.STDOUT,
).decode('utf-16le', errors='replace')
except subprocess.CalledProcessError as exc:
log.error(exc.output)
- raise DistutilsPlatformError("Error executing {}".format(exc.cmd))
+ raise DistutilsPlatformError(f"Error executing {exc.cmd}")
env = {
key.lower(): value
@@ -224,6 +224,18 @@ class MSVCCompiler(CCompiler):
self.plat_name = None
self.initialized = False
+ @classmethod
+ def _configure(cls, vc_env):
+ """
+ Set class-level include/lib dirs.
+ """
+ cls.include_dirs = cls._parse_path(vc_env.get('include', ''))
+ cls.library_dirs = cls._parse_path(vc_env.get('lib', ''))
+
+ @staticmethod
+ def _parse_path(val):
+ return [dir.rstrip(os.sep) for dir in val.split(os.pathsep) if dir]
+
def initialize(self, plat_name=None):
# multi-init means we would need to check platform same each time...
assert not self.initialized, "don't init multiple times"
@@ -232,7 +244,7 @@ class MSVCCompiler(CCompiler):
# sanity check for platforms to prevent obscure errors later.
if plat_name not in PLAT_TO_VCVARS:
raise DistutilsPlatformError(
- "--plat-name must be one of {}".format(tuple(PLAT_TO_VCVARS))
+ f"--plat-name must be one of {tuple(PLAT_TO_VCVARS)}"
)
# Get the vcvarsall.bat spec for the requested platform.
@@ -243,6 +255,7 @@ class MSVCCompiler(CCompiler):
raise DistutilsPlatformError(
"Unable to find a compatible " "Visual Studio installation."
)
+ self._configure(vc_env)
self._paths = vc_env.get('path', '')
paths = self._paths.split(os.pathsep)
@@ -253,14 +266,6 @@ class MSVCCompiler(CCompiler):
self.mc = _find_exe("mc.exe", paths) # message compiler
self.mt = _find_exe("mt.exe", paths) # message compiler
- for dir in vc_env.get('include', '').split(os.pathsep):
- if dir:
- self.add_include_dir(dir.rstrip(os.sep))
-
- for dir in vc_env.get('lib', '').split(os.pathsep):
- if dir:
- self.add_library_dir(dir.rstrip(os.sep))
-
self.preprocess_options = None
# bpo-38597: Always compile with dynamic linking
# Future releases of Python 3.x will include all past
@@ -341,7 +346,7 @@ class MSVCCompiler(CCompiler):
# Better to raise an exception instead of silently continuing
# and later complain about sources and targets having
# different lengths
- raise CompileError("Don't know how to compile {}".format(p))
+ raise CompileError(f"Don't know how to compile {p}")
return list(map(make_out_path, source_filenames))
@@ -425,9 +430,7 @@ class MSVCCompiler(CCompiler):
continue
else:
# how to handle this file?
- raise CompileError(
- "Don't know how to compile {} to {}".format(src, obj)
- )
+ raise CompileError(f"Don't know how to compile {src} to {obj}")
args = [self.cc] + compile_opts + pp_opts
if add_cpp_opts:
@@ -556,7 +559,7 @@ class MSVCCompiler(CCompiler):
else:
return
warnings.warn("Fallback spawn triggered. Please update distutils monkeypatch.")
- with unittest.mock.patch.dict('os.environ', env):
+ with mock.patch.dict('os.environ', env):
bag.value = super().spawn(cmd)
# -- Miscellaneous methods -----------------------------------------
diff --git a/distutils/archive_util.py b/distutils/archive_util.py
index 4cb9bf39..5dfe2a16 100644
--- a/distutils/archive_util.py
+++ b/distutils/archive_util.py
@@ -121,7 +121,7 @@ def make_tarball(
# compression using `compress`
if compress == 'compress':
- warn("'compress' will be deprecated.", PendingDeprecationWarning)
+ warn("'compress' is deprecated.", DeprecationWarning)
# the option varies depending on the platform
compressed_name = archive_name + compress_ext[compress]
if sys.platform == 'win32':
diff --git a/distutils/bcppcompiler.py b/distutils/bcppcompiler.py
index 7a6f951f..ee033ed9 100644
--- a/distutils/bcppcompiler.py
+++ b/distutils/bcppcompiler.py
@@ -234,7 +234,7 @@ class BCPPCompiler(CCompiler):
def_file = os.path.join(temp_dir, '%s.def' % modname)
contents = ['EXPORTS']
for sym in export_symbols or []:
- contents.append(' %s=_%s' % (sym, sym))
+ contents.append(' {}=_{}'.format(sym, sym))
self.execute(write_file, (def_file, contents), "writing %s" % def_file)
# Borland C++ has problems with '/' in paths
@@ -346,7 +346,7 @@ class BCPPCompiler(CCompiler):
(base, ext) = os.path.splitext(os.path.normcase(src_name))
if ext not in (self.src_extensions + ['.rc', '.res']):
raise UnknownFileError(
- "unknown file type '%s' (from '%s')" % (ext, src_name)
+ "unknown file type '{}' (from '{}')".format(ext, src_name)
)
if strip_dir:
base = os.path.basename(base)
diff --git a/distutils/ccompiler.py b/distutils/ccompiler.py
index c1761d02..c8d3b24b 100644
--- a/distutils/ccompiler.py
+++ b/distutils/ccompiler.py
@@ -6,6 +6,8 @@ for the Distutils compiler abstraction model."""
import sys
import os
import re
+import warnings
+
from distutils.errors import (
CompileError,
LinkError,
@@ -91,6 +93,16 @@ class CCompiler:
}
language_order = ["c++", "objc", "c"]
+ include_dirs = []
+ """
+ include dirs specific to this compiler class
+ """
+
+ library_dirs = []
+ """
+ library dirs specific to this compiler class
+ """
+
def __init__(self, verbose=0, dry_run=0, force=0):
self.dry_run = dry_run
self.force = force
@@ -324,24 +336,7 @@ class CCompiler:
def _setup_compile(self, outdir, macros, incdirs, sources, depends, extra):
"""Process arguments and decide which source files to compile."""
- if outdir is None:
- outdir = self.output_dir
- elif not isinstance(outdir, str):
- raise TypeError("'output_dir' must be a string or None")
-
- if macros is None:
- macros = self.macros
- elif isinstance(macros, list):
- macros = macros + (self.macros or [])
- else:
- raise TypeError("'macros' (if supplied) must be a list of tuples")
-
- if incdirs is None:
- incdirs = self.include_dirs
- elif isinstance(incdirs, (list, tuple)):
- incdirs = list(incdirs) + (self.include_dirs or [])
- else:
- raise TypeError("'include_dirs' (if supplied) must be a list of strings")
+ outdir, macros, incdirs = self._fix_compile_args(outdir, macros, incdirs)
if extra is None:
extra = []
@@ -400,6 +395,9 @@ class CCompiler:
else:
raise TypeError("'include_dirs' (if supplied) must be a list of strings")
+ # add include dirs for class
+ include_dirs += self.__class__.include_dirs
+
return output_dir, macros, include_dirs
def _prep_compile(self, sources, output_dir, depends=None):
@@ -456,6 +454,9 @@ class CCompiler:
else:
raise TypeError("'library_dirs' (if supplied) must be a list of strings")
+ # add library dirs for class
+ library_dirs += self.__class__.library_dirs
+
if runtime_library_dirs is None:
runtime_library_dirs = self.runtime_library_dirs
elif isinstance(runtime_library_dirs, (list, tuple)):
@@ -926,17 +927,35 @@ int main (int argc, char **argv) {
obj_names = []
for src_name in source_filenames:
base, ext = os.path.splitext(src_name)
- base = os.path.splitdrive(base)[1] # Chop off the drive
- base = base[os.path.isabs(base) :] # If abs, chop off leading /
+ base = self._mangle_base(base)
if ext not in self.src_extensions:
raise UnknownFileError(
- "unknown file type '%s' (from '%s')" % (ext, src_name)
+ "unknown file type '{}' (from '{}')".format(ext, src_name)
)
if strip_dir:
base = os.path.basename(base)
obj_names.append(os.path.join(output_dir, base + self.obj_extension))
return obj_names
+ @staticmethod
+ def _mangle_base(base):
+ """
+ For unknown reasons, absolute paths are mangled.
+ """
+ # Chop off the drive
+ no_drive = os.path.splitdrive(base)[1]
+ # If abs, chop off leading /
+ rel = no_drive[os.path.isabs(no_drive) :]
+ if rel != base:
+ msg = (
+ f"Absolute path {base!r} is being replaced with a "
+ f"relative path {rel!r} for outputs. This behavior is "
+ "deprecated. If this behavior is desired, please "
+ "comment in pypa/distutils#169."
+ )
+ warnings.warn(msg, DeprecationWarning)
+ return rel
+
def shared_object_filename(self, basename, strip_dir=0, output_dir=''):
assert output_dir is not None
if strip_dir:
diff --git a/distutils/cmd.py b/distutils/cmd.py
index 6f68801d..68a9267c 100644
--- a/distutils/cmd.py
+++ b/distutils/cmd.py
@@ -163,7 +163,7 @@ class Command:
if option[-1] == "=":
option = option[:-1]
value = getattr(self, option)
- self.announce(indent + "%s = %s" % (option, value), level=log.INFO)
+ self.announce(indent + "{} = {}".format(option, value), level=log.INFO)
def run(self):
"""A command's raison d'etre: carry out the action it exists to
@@ -215,7 +215,7 @@ class Command:
return default
elif not isinstance(val, str):
raise DistutilsOptionError(
- "'%s' must be a %s (got `%s`)" % (option, what, val)
+ "'{}' must be a {} (got `{}`)".format(option, what, val)
)
return val
@@ -243,7 +243,7 @@ class Command:
ok = False
if not ok:
raise DistutilsOptionError(
- "'%s' must be a list of strings (got %r)" % (option, val)
+ "'{}' must be a list of strings (got {!r})".format(option, val)
)
def _ensure_tested_string(self, option, tester, what, error_fmt, default=None):
@@ -424,7 +424,7 @@ class Command:
raise TypeError("'infiles' must be a string, or a list or tuple of strings")
if exec_msg is None:
- exec_msg = "generating %s from %s" % (outfile, ', '.join(infiles))
+ exec_msg = "generating {} from {}".format(outfile, ', '.join(infiles))
# If 'outfile' must be regenerated (either because it doesn't
# exist, is out-of-date, or the 'force' flag is true) then
diff --git a/distutils/command/bdist.py b/distutils/command/bdist.py
index 6a701731..de37dae0 100644
--- a/distutils/command/bdist.py
+++ b/distutils/command/bdist.py
@@ -4,6 +4,8 @@ Implements the Distutils 'bdist' command (create a built [binary]
distribution)."""
import os
+import warnings
+
from distutils.core import Command
from distutils.errors import DistutilsPlatformError, DistutilsOptionError
from distutils.util import get_platform
@@ -15,11 +17,21 @@ def show_formats():
formats = []
for format in bdist.format_commands:
- formats.append(("formats=" + format, None, bdist.format_command[format][1]))
+ formats.append(("formats=" + format, None, bdist.format_commands[format][1]))
pretty_printer = FancyGetopt(formats)
pretty_printer.print_help("List of available distribution formats:")
+class ListCompat(dict):
+ # adapter to allow for Setuptools compatibility in format_commands
+ def append(self, item):
+ warnings.warn(
+ """format_commands is now a dict. append is deprecated.""",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+
+
class bdist(Command):
description = "create a built (binary) distribution"
@@ -64,29 +76,21 @@ class bdist(Command):
# Debian-ish Linux, Solaris, FreeBSD, ..., Windows, Mac OS.
default_format = {'posix': 'gztar', 'nt': 'zip'}
- # Establish the preferred order (for the --help-formats option).
- format_commands = [
- 'rpm',
- 'gztar',
- 'bztar',
- 'xztar',
- 'ztar',
- 'tar',
- 'wininst',
- 'zip',
- ]
-
- # And the real information.
- format_command = {
- 'rpm': ('bdist_rpm', "RPM distribution"),
- 'gztar': ('bdist_dumb', "gzip'ed tar file"),
- 'bztar': ('bdist_dumb', "bzip2'ed tar file"),
- 'xztar': ('bdist_dumb', "xz'ed tar file"),
- 'ztar': ('bdist_dumb', "compressed tar file"),
- 'tar': ('bdist_dumb', "tar file"),
- 'wininst': ('bdist_wininst', "Windows executable installer"),
- 'zip': ('bdist_dumb', "ZIP file"),
- }
+ # Define commands in preferred order for the --help-formats option
+ format_commands = ListCompat(
+ {
+ 'rpm': ('bdist_rpm', "RPM distribution"),
+ 'gztar': ('bdist_dumb', "gzip'ed tar file"),
+ 'bztar': ('bdist_dumb', "bzip2'ed tar file"),
+ 'xztar': ('bdist_dumb', "xz'ed tar file"),
+ 'ztar': ('bdist_dumb', "compressed tar file"),
+ 'tar': ('bdist_dumb', "tar file"),
+ 'zip': ('bdist_dumb', "ZIP file"),
+ }
+ )
+
+ # for compatibility until consumers only reference format_commands
+ format_command = format_commands
def initialize_options(self):
self.bdist_base = None
@@ -130,7 +134,7 @@ class bdist(Command):
commands = []
for format in self.formats:
try:
- commands.append(self.format_command[format][0])
+ commands.append(self.format_commands[format][0])
except KeyError:
raise DistutilsOptionError("invalid format '%s'" % format)
diff --git a/distutils/command/bdist_dumb.py b/distutils/command/bdist_dumb.py
index d3f519e0..0f52330f 100644
--- a/distutils/command/bdist_dumb.py
+++ b/distutils/command/bdist_dumb.py
@@ -105,7 +105,9 @@ class bdist_dumb(Command):
# And make an archive relative to the root of the
# pseudo-installation tree.
- archive_basename = "%s.%s" % (self.distribution.get_fullname(), self.plat_name)
+ archive_basename = "{}.{}".format(
+ self.distribution.get_fullname(), self.plat_name
+ )
pseudoinstall_root = os.path.join(self.dist_dir, archive_basename)
if not self.relative:
diff --git a/distutils/command/bdist_rpm.py b/distutils/command/bdist_rpm.py
index fcfd7cd8..6a50ef34 100644
--- a/distutils/command/bdist_rpm.py
+++ b/distutils/command/bdist_rpm.py
@@ -353,7 +353,7 @@ class bdist_rpm(Command):
nvr_string = "%{name}-%{version}-%{release}"
src_rpm = nvr_string + ".src.rpm"
non_src_rpm = "%{arch}/" + nvr_string + ".%{arch}.rpm"
- q_cmd = r"rpm -q --qf '%s %s\n' --specfile '%s'" % (
+ q_cmd = r"rpm -q --qf '{} {}\n' --specfile '{}'".format(
src_rpm,
non_src_rpm,
spec_path,
@@ -488,9 +488,9 @@ class bdist_rpm(Command):
):
val = getattr(self, field.lower())
if isinstance(val, list):
- spec_file.append('%s: %s' % (field, ' '.join(val)))
+ spec_file.append('{}: {}'.format(field, ' '.join(val)))
elif val is not None:
- spec_file.append('%s: %s' % (field, val))
+ spec_file.append('{}: {}'.format(field, val))
if self.distribution.get_url():
spec_file.append('Url: ' + self.distribution.get_url())
@@ -527,7 +527,7 @@ class bdist_rpm(Command):
# rpm scripts
# figure out default build script
- def_setup_call = "%s %s" % (self.python, os.path.basename(sys.argv[0]))
+ def_setup_call = "{} {}".format(self.python, os.path.basename(sys.argv[0]))
def_build = "%s build" % def_setup_call
if self.use_rpm_opt_flags:
def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build
diff --git a/distutils/command/bdist_wininst.py b/distutils/command/bdist_wininst.py
deleted file mode 100644
index 7e9a64a5..00000000
--- a/distutils/command/bdist_wininst.py
+++ /dev/null
@@ -1,418 +0,0 @@
-"""distutils.command.bdist_wininst
-
-Implements the Distutils 'bdist_wininst' command: create a windows installer
-exe-program."""
-
-import os
-import sys
-import warnings
-from distutils.core import Command
-from distutils.util import get_platform
-from distutils.dir_util import remove_tree
-from distutils.errors import DistutilsOptionError, DistutilsPlatformError
-from distutils.sysconfig import get_python_version
-from distutils import log
-
-
-class bdist_wininst(Command):
-
- description = "create an executable installer for MS Windows"
-
- user_options = [
- ('bdist-dir=', None, "temporary directory for creating the distribution"),
- (
- 'plat-name=',
- 'p',
- "platform name to embed in generated filenames "
- "(default: %s)" % get_platform(),
- ),
- (
- 'keep-temp',
- 'k',
- "keep the pseudo-installation tree around after "
- + "creating the distribution archive",
- ),
- (
- 'target-version=',
- None,
- "require a specific python version" + " on the target system",
- ),
- ('no-target-compile', 'c', "do not compile .py to .pyc on the target system"),
- (
- 'no-target-optimize',
- 'o',
- "do not compile .py to .pyo (optimized) " "on the target system",
- ),
- ('dist-dir=', 'd', "directory to put final built distributions in"),
- (
- 'bitmap=',
- 'b',
- "bitmap to use for the installer instead of python-powered logo",
- ),
- (
- 'title=',
- 't',
- "title to display on the installer background instead of default",
- ),
- ('skip-build', None, "skip rebuilding everything (for testing/debugging)"),
- (
- 'install-script=',
- None,
- "basename of installation script to be run after "
- "installation or before deinstallation",
- ),
- (
- 'pre-install-script=',
- None,
- "Fully qualified filename of a script to be run before "
- "any files are installed. This script need not be in the "
- "distribution",
- ),
- (
- 'user-access-control=',
- None,
- "specify Vista's UAC handling - 'none'/default=no "
- "handling, 'auto'=use UAC if target Python installed for "
- "all users, 'force'=always use UAC",
- ),
- ]
-
- boolean_options = [
- 'keep-temp',
- 'no-target-compile',
- 'no-target-optimize',
- 'skip-build',
- ]
-
- # bpo-10945: bdist_wininst requires mbcs encoding only available on Windows
- _unsupported = sys.platform != "win32"
-
- def __init__(self, *args, **kw):
- super().__init__(*args, **kw)
- warnings.warn(
- "bdist_wininst command is deprecated since Python 3.8, "
- "use bdist_wheel (wheel packages) instead",
- DeprecationWarning,
- 2,
- )
-
- def initialize_options(self):
- self.bdist_dir = None
- self.plat_name = None
- self.keep_temp = 0
- self.no_target_compile = 0
- self.no_target_optimize = 0
- self.target_version = None
- self.dist_dir = None
- self.bitmap = None
- self.title = None
- self.skip_build = None
- self.install_script = None
- self.pre_install_script = None
- self.user_access_control = None
-
- def finalize_options(self):
- self.set_undefined_options('bdist', ('skip_build', 'skip_build'))
-
- if self.bdist_dir is None:
- if self.skip_build and self.plat_name:
- # If build is skipped and plat_name is overridden, bdist will
- # not see the correct 'plat_name' - so set that up manually.
- bdist = self.distribution.get_command_obj('bdist')
- bdist.plat_name = self.plat_name
- # next the command will be initialized using that name
- bdist_base = self.get_finalized_command('bdist').bdist_base
- self.bdist_dir = os.path.join(bdist_base, 'wininst')
-
- if not self.target_version:
- self.target_version = ""
-
- if not self.skip_build and self.distribution.has_ext_modules():
- short_version = get_python_version()
- if self.target_version and self.target_version != short_version:
- raise DistutilsOptionError(
- "target version can only be %s, or the '--skip-build'"
- " option must be specified" % (short_version,)
- )
- self.target_version = short_version
-
- self.set_undefined_options(
- 'bdist',
- ('dist_dir', 'dist_dir'),
- ('plat_name', 'plat_name'),
- )
-
- if self.install_script:
- for script in self.distribution.scripts:
- if self.install_script == os.path.basename(script):
- break
- else:
- raise DistutilsOptionError(
- "install_script '%s' not found in scripts" % self.install_script
- )
-
- def run(self):
- if sys.platform != "win32" and (
- self.distribution.has_ext_modules() or self.distribution.has_c_libraries()
- ):
- raise DistutilsPlatformError(
- "distribution contains extensions and/or C libraries; "
- "must be compiled on a Windows 32 platform"
- )
-
- if not self.skip_build:
- self.run_command('build')
-
- install = self.reinitialize_command('install', reinit_subcommands=1)
- install.root = self.bdist_dir
- install.skip_build = self.skip_build
- install.warn_dir = 0
- install.plat_name = self.plat_name
-
- install_lib = self.reinitialize_command('install_lib')
- # we do not want to include pyc or pyo files
- install_lib.compile = 0
- install_lib.optimize = 0
-
- if self.distribution.has_ext_modules():
- # If we are building an installer for a Python version other
- # than the one we are currently running, then we need to ensure
- # our build_lib reflects the other Python version rather than ours.
- # Note that for target_version!=sys.version, we must have skipped the
- # build step, so there is no issue with enforcing the build of this
- # version.
- target_version = self.target_version
- if not target_version:
- assert self.skip_build, "Should have already checked this"
- target_version = '%d.%d' % sys.version_info[:2]
- plat_specifier = ".%s-%s" % (self.plat_name, target_version)
- build = self.get_finalized_command('build')
- build.build_lib = os.path.join(build.build_base, 'lib' + plat_specifier)
-
- # Use a custom scheme for the zip-file, because we have to decide
- # at installation time which scheme to use.
- for key in ('purelib', 'platlib', 'headers', 'scripts', 'data'):
- value = key.upper()
- if key == 'headers':
- value = value + '/Include/$dist_name'
- setattr(install, 'install_' + key, value)
-
- log.info("installing to %s", self.bdist_dir)
- install.ensure_finalized()
-
- # avoid warning of 'install_lib' about installing
- # into a directory not in sys.path
- sys.path.insert(0, os.path.join(self.bdist_dir, 'PURELIB'))
-
- install.run()
-
- del sys.path[0]
-
- # And make an archive relative to the root of the
- # pseudo-installation tree.
- from tempfile import mktemp
-
- archive_basename = mktemp()
- fullname = self.distribution.get_fullname()
- arcname = self.make_archive(archive_basename, "zip", root_dir=self.bdist_dir)
- # create an exe containing the zip-file
- self.create_exe(arcname, fullname, self.bitmap)
- if self.distribution.has_ext_modules():
- pyversion = get_python_version()
- else:
- pyversion = 'any'
- self.distribution.dist_files.append(
- ('bdist_wininst', pyversion, self.get_installer_filename(fullname))
- )
- # remove the zip-file again
- log.debug("removing temporary file '%s'", arcname)
- os.remove(arcname)
-
- if not self.keep_temp:
- remove_tree(self.bdist_dir, dry_run=self.dry_run)
-
- def get_inidata(self):
- # Return data describing the installation.
- lines = []
- metadata = self.distribution.metadata
-
- # Write the [metadata] section.
- lines.append("[metadata]")
-
- # 'info' will be displayed in the installer's dialog box,
- # describing the items to be installed.
- info = (metadata.long_description or '') + '\n'
-
- # Escape newline characters
- def escape(s):
- return s.replace("\n", "\\n")
-
- for name in [
- "author",
- "author_email",
- "description",
- "maintainer",
- "maintainer_email",
- "name",
- "url",
- "version",
- ]:
- data = getattr(metadata, name, "")
- if data:
- info = info + ("\n %s: %s" % (name.capitalize(), escape(data)))
- lines.append("%s=%s" % (name, escape(data)))
-
- # The [setup] section contains entries controlling
- # the installer runtime.
- lines.append("\n[Setup]")
- if self.install_script:
- lines.append("install_script=%s" % self.install_script)
- lines.append("info=%s" % escape(info))
- lines.append("target_compile=%d" % (not self.no_target_compile))
- lines.append("target_optimize=%d" % (not self.no_target_optimize))
- if self.target_version:
- lines.append("target_version=%s" % self.target_version)
- if self.user_access_control:
- lines.append("user_access_control=%s" % self.user_access_control)
-
- title = self.title or self.distribution.get_fullname()
- lines.append("title=%s" % escape(title))
- import time
- import distutils
-
- build_info = "Built %s with distutils-%s" % (
- time.ctime(time.time()),
- distutils.__version__,
- )
- lines.append("build_info=%s" % build_info)
- return "\n".join(lines)
-
- def create_exe(self, arcname, fullname, bitmap=None):
- import struct
-
- self.mkpath(self.dist_dir)
-
- cfgdata = self.get_inidata()
-
- installer_name = self.get_installer_filename(fullname)
- self.announce("creating %s" % installer_name)
-
- if bitmap:
- with open(bitmap, "rb") as f:
- bitmapdata = f.read()
- bitmaplen = len(bitmapdata)
- else:
- bitmaplen = 0
-
- with open(installer_name, "wb") as file:
- file.write(self.get_exe_bytes())
- if bitmap:
- file.write(bitmapdata)
-
- # Convert cfgdata from unicode to ascii, mbcs encoded
- if isinstance(cfgdata, str):
- cfgdata = cfgdata.encode("mbcs")
-
- # Append the pre-install script
- cfgdata = cfgdata + b"\0"
- if self.pre_install_script:
- # We need to normalize newlines, so we open in text mode and
- # convert back to bytes. "latin-1" simply avoids any possible
- # failures.
- with open(self.pre_install_script, "r", encoding="latin-1") as script:
- script_data = script.read().encode("latin-1")
- cfgdata = cfgdata + script_data + b"\n\0"
- else:
- # empty pre-install script
- cfgdata = cfgdata + b"\0"
- file.write(cfgdata)
-
- # The 'magic number' 0x1234567B is used to make sure that the
- # binary layout of 'cfgdata' is what the wininst.exe binary
- # expects. If the layout changes, increment that number, make
- # the corresponding changes to the wininst.exe sources, and
- # recompile them.
- header = struct.pack(
- "<iii",
- 0x1234567B, # tag
- len(cfgdata), # length
- bitmaplen, # number of bytes in bitmap
- )
- file.write(header)
- with open(arcname, "rb") as f:
- file.write(f.read())
-
- def get_installer_filename(self, fullname):
- # Factored out to allow overriding in subclasses
- if self.target_version:
- # if we create an installer for a specific python version,
- # it's better to include this in the name
- installer_name = os.path.join(
- self.dist_dir,
- "%s.%s-py%s.exe" % (fullname, self.plat_name, self.target_version),
- )
- else:
- installer_name = os.path.join(
- self.dist_dir, "%s.%s.exe" % (fullname, self.plat_name)
- )
- return installer_name
-
- def get_exe_bytes(self): # noqa: C901
- # If a target-version other than the current version has been
- # specified, then using the MSVC version from *this* build is no good.
- # Without actually finding and executing the target version and parsing
- # its sys.version, we just hard-code our knowledge of old versions.
- # NOTE: Possible alternative is to allow "--target-version" to
- # specify a Python executable rather than a simple version string.
- # We can then execute this program to obtain any info we need, such
- # as the real sys.version string for the build.
- cur_version = get_python_version()
-
- # If the target version is *later* than us, then we assume they
- # use what we use
- # string compares seem wrong, but are what sysconfig.py itself uses
- if self.target_version and self.target_version < cur_version:
- if self.target_version < "2.4":
- bv = '6.0'
- elif self.target_version == "2.4":
- bv = '7.1'
- elif self.target_version == "2.5":
- bv = '8.0'
- elif self.target_version <= "3.2":
- bv = '9.0'
- elif self.target_version <= "3.4":
- bv = '10.0'
- else:
- bv = '14.0'
- else:
- # for current version - use authoritative check.
- try:
- from msvcrt import CRT_ASSEMBLY_VERSION
- except ImportError:
- # cross-building, so assume the latest version
- bv = '14.0'
- else:
- # as far as we know, CRT is binary compatible based on
- # the first field, so assume 'x.0' until proven otherwise
- major = CRT_ASSEMBLY_VERSION.partition('.')[0]
- bv = major + '.0'
-
- # wininst-x.y.exe is in the same directory as this file
- directory = os.path.dirname(__file__)
- # we must use a wininst-x.y.exe built with the same C compiler
- # used for python. XXX What about mingw, borland, and so on?
-
- # if plat_name starts with "win" but is not "win32"
- # we want to strip "win" and leave the rest (e.g. -amd64)
- # for all other cases, we don't want any suffix
- if self.plat_name != 'win32' and self.plat_name[:3] == 'win':
- sfix = self.plat_name[3:]
- else:
- sfix = ''
-
- filename = os.path.join(directory, "wininst-%s%s.exe" % (bv, sfix))
- f = open(filename, "rb")
- try:
- return f.read()
- finally:
- f.close()
diff --git a/distutils/command/build.py b/distutils/command/build.py
index e4b06425..6d453419 100644
--- a/distutils/command/build.py
+++ b/distutils/command/build.py
@@ -79,7 +79,7 @@ class build(Command):
"using './configure --help' on your platform)"
)
- plat_specifier = ".%s-%s" % (self.plat_name, sys.implementation.cache_tag)
+ plat_specifier = ".{}-{}".format(self.plat_name, sys.implementation.cache_tag)
# Make it so Python 2.x and Python 2.x with --with-pydebug don't
# share the same build directories. Doing so confuses the build
diff --git a/distutils/command/build_ext.py b/distutils/command/build_ext.py
index 153a0b6d..3c6cee7e 100644
--- a/distutils/command/build_ext.py
+++ b/distutils/command/build_ext.py
@@ -498,7 +498,7 @@ class build_ext(Command):
except (CCompilerError, DistutilsError, CompileError) as e:
if not ext.optional:
raise
- self.warn('building extension "%s" failed: %s' % (ext.name, e))
+ self.warn('building extension "{}" failed: {}'.format(ext.name, e))
def build_extension(self, ext):
sources = ext.sources
diff --git a/distutils/command/check.py b/distutils/command/check.py
index 9c3523a8..539481c9 100644
--- a/distutils/command/check.py
+++ b/distutils/command/check.py
@@ -2,17 +2,18 @@
Implements the Distutils 'check' command.
"""
+import contextlib
+
from distutils.core import Command
from distutils.errors import DistutilsSetupError
-try:
- # docutils is installed
- from docutils.utils import Reporter
- from docutils.parsers.rst import Parser
- from docutils import frontend
- from docutils import nodes
+with contextlib.suppress(ImportError):
+ import docutils.utils
+ import docutils.parsers.rst
+ import docutils.frontend
+ import docutils.nodes
- class SilentReporter(Reporter):
+ class SilentReporter(docutils.utils.Reporter):
def __init__(
self,
source,
@@ -30,16 +31,10 @@ try:
def system_message(self, level, message, *children, **kwargs):
self.messages.append((level, message, children, kwargs))
- return nodes.system_message(
+ return docutils.nodes.system_message(
message, level=level, type=self.levels[level], *children, **kwargs
)
- HAS_DOCUTILS = True
-except Exception:
- # Catch all exceptions because exceptions besides ImportError probably
- # indicate that docutils is not ported to Py3k.
- HAS_DOCUTILS = False
-
class check(Command):
"""This command checks the meta-data of the package."""
@@ -81,8 +76,11 @@ class check(Command):
if self.metadata:
self.check_metadata()
if self.restructuredtext:
- if HAS_DOCUTILS:
- self.check_restructuredtext()
+ if 'docutils' in globals():
+ try:
+ self.check_restructuredtext()
+ except TypeError as exc:
+ raise DistutilsSetupError(str(exc))
elif self.strict:
raise DistutilsSetupError('The docutils package is needed.')
@@ -117,15 +115,17 @@ class check(Command):
if line is None:
warning = warning[1]
else:
- warning = '%s (line %s)' % (warning[1], line)
+ warning = '{} (line {})'.format(warning[1], line)
self.warn(warning)
def _check_rst_data(self, data):
"""Returns warnings when the provided data doesn't compile."""
# the include and csv_table directives need this to be a path
source_path = self.distribution.script_name or 'setup.py'
- parser = Parser()
- settings = frontend.OptionParser(components=(Parser,)).get_default_values()
+ parser = docutils.parsers.rst.Parser()
+ settings = docutils.frontend.OptionParser(
+ components=(docutils.parsers.rst.Parser,)
+ ).get_default_values()
settings.tab_width = 4
settings.pep_references = None
settings.rfc_references = None
@@ -139,7 +139,7 @@ class check(Command):
error_handler=settings.error_encoding_error_handler,
)
- document = nodes.document(settings, reporter, source=source_path)
+ document = docutils.nodes.document(settings, reporter, source=source_path)
document.note_source(source_path, -1)
try:
parser.parse(data, document)
diff --git a/distutils/command/register.py b/distutils/command/register.py
index d2351ab8..c1402650 100644
--- a/distutils/command/register.py
+++ b/distutils/command/register.py
@@ -66,9 +66,9 @@ class register(PyPIRCCommand):
def check_metadata(self):
"""Deprecated API."""
warn(
- "distutils.command.register.check_metadata is deprecated, \
- use the check command instead",
- PendingDeprecationWarning,
+ "distutils.command.register.check_metadata is deprecated; "
+ "use the check command instead",
+ DeprecationWarning,
)
check = self.distribution.get_command_obj('check')
check.ensure_finalized()
@@ -174,7 +174,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 (%s): %s' % (code, result), log.INFO)
+ self.announce('Server response ({}): {}'.format(code, result), log.INFO)
# possibly save the login
if code == 200:
@@ -224,7 +224,7 @@ Your selection [default 1]: ''',
log.info('Server response (%s): %s', code, result)
else:
log.info('You will receive an email shortly.')
- log.info(('Follow the instructions in it to ' 'complete registration.'))
+ log.info('Follow the instructions in it to ' 'complete registration.')
elif choice == '3':
data = {':action': 'password_reset'}
data['email'] = ''
@@ -265,7 +265,7 @@ Your selection [default 1]: ''',
'''Post a query to the server, and return a string response.'''
if 'name' in data:
self.announce(
- 'Registering %s to %s' % (data['name'], self.repository), log.INFO
+ 'Registering {} to {}'.format(data['name'], self.repository), log.INFO
)
# Build up the MIME payload for the urllib2 POST data
boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
diff --git a/distutils/command/sdist.py b/distutils/command/sdist.py
index ec3c97ac..d6e9489d 100644
--- a/distutils/command/sdist.py
+++ b/distutils/command/sdist.py
@@ -402,7 +402,7 @@ class sdist(Command):
seps = '/'
vcs_dirs = ['RCS', 'CVS', r'\.svn', r'\.hg', r'\.git', r'\.bzr', '_darcs']
- vcs_ptrn = r'(^|%s)(%s)(%s).*' % (seps, '|'.join(vcs_dirs), seps)
+ vcs_ptrn = r'(^|{})({})({}).*'.format(seps, '|'.join(vcs_dirs), seps)
self.filelist.exclude_pattern(vcs_ptrn, is_regex=1)
def write_manifest(self):
diff --git a/distutils/command/upload.py b/distutils/command/upload.py
index f2a8118e..6af53943 100644
--- a/distutils/command/upload.py
+++ b/distutils/command/upload.py
@@ -170,7 +170,7 @@ class upload(PyPIRCCommand):
body.write(end_boundary)
body = body.getvalue()
- msg = "Submitting %s to %s" % (filename, self.repository)
+ msg = "Submitting {} to {}".format(filename, self.repository)
self.announce(msg, log.INFO)
# build the Request
@@ -194,12 +194,12 @@ class upload(PyPIRCCommand):
raise
if status == 200:
- self.announce('Server response (%s): %s' % (status, reason), log.INFO)
+ self.announce('Server response ({}): {}'.format(status, reason), log.INFO)
if self.show_response:
text = self._read_pypi_response(result)
msg = '\n'.join(('-' * 75, text, '-' * 75))
self.announce(msg, log.INFO)
else:
- msg = 'Upload failed (%s): %s' % (status, reason)
+ msg = 'Upload failed ({}): {}'.format(status, reason)
self.announce(msg, log.ERROR)
raise DistutilsError(msg)
diff --git a/distutils/core.py b/distutils/core.py
index 333596ac..de13978f 100644
--- a/distutils/core.py
+++ b/distutils/core.py
@@ -149,7 +149,7 @@ def setup(**attrs): # noqa: C901
if 'name' not in attrs:
raise SystemExit("error in setup command: %s" % msg)
else:
- raise SystemExit("error in %s setup command: %s" % (attrs['name'], msg))
+ raise SystemExit("error in {} setup command: {}".format(attrs['name'], msg))
if _setup_stop_after == "init":
return dist
@@ -203,10 +203,10 @@ def run_commands(dist):
raise SystemExit("interrupted")
except OSError as exc:
if DEBUG:
- sys.stderr.write("error: %s\n" % (exc,))
+ sys.stderr.write("error: {}\n".format(exc))
raise
else:
- raise SystemExit("error: %s" % (exc,))
+ raise SystemExit("error: {}".format(exc))
except (DistutilsError, CCompilerError) as msg:
if DEBUG:
@@ -249,7 +249,7 @@ def run_setup(script_name, script_args=None, stop_after="run"):
used to drive the Distutils.
"""
if stop_after not in ('init', 'config', 'commandline', 'run'):
- raise ValueError("invalid value for 'stop_after': %r" % (stop_after,))
+ raise ValueError("invalid value for 'stop_after': {!r}".format(stop_after))
global _setup_stop_after, _setup_distribution
_setup_stop_after = stop_after
diff --git a/distutils/cygwinccompiler.py b/distutils/cygwinccompiler.py
index 5c23e988..63910f2a 100644
--- a/distutils/cygwinccompiler.py
+++ b/distutils/cygwinccompiler.py
@@ -6,47 +6,6 @@ the Mingw32CCompiler class which handles the mingw32 port of GCC (same as
cygwin in no-cygwin mode).
"""
-# problems:
-#
-# * if you use a msvc compiled python version (1.5.2)
-# 1. you have to insert a __GNUC__ section in its config.h
-# 2. you have to generate an import library for its dll
-# - create a def-file for python??.dll
-# - create an import library using
-# dlltool --dllname python15.dll --def python15.def \
-# --output-lib libpython15.a
-#
-# see also http://starship.python.net/crew/kernr/mingw32/Notes.html
-#
-# * We put export_symbols in a def-file, and don't use
-# --export-all-symbols because it doesn't worked reliable in some
-# tested configurations. And because other windows compilers also
-# need their symbols specified this no serious problem.
-#
-# tested configurations:
-#
-# * cygwin gcc 2.91.57/ld 2.9.4/dllwrap 0.2.4 works
-# (after patching python's config.h and for C++ some other include files)
-# see also http://starship.python.net/crew/kernr/mingw32/Notes.html
-# * mingw32 gcc 2.95.2/ld 2.9.4/dllwrap 0.2.4 works
-# (ld doesn't support -shared, so we use dllwrap)
-# * cygwin gcc 2.95.2/ld 2.10.90/dllwrap 2.10.90 works now
-# - its dllwrap doesn't work, there is a bug in binutils 2.10.90
-# see also http://sources.redhat.com/ml/cygwin/2000-06/msg01274.html
-# - using gcc -mdll instead dllwrap doesn't work without -static because
-# it tries to link against dlls instead their import libraries. (If
-# it finds the dll first.)
-# By specifying -static we force ld to link against the import libraries,
-# this is windows standard and there are normally not the necessary symbols
-# in the dlls.
-# *** only the version of June 2000 shows these problems
-# * cygwin gcc 3.2/ld 2.13.90 works
-# (ld supports -shared)
-# * mingw gcc 3.2/ld 2.13 works
-# (ld supports -shared)
-# * llvm-mingw with Clang 11 works
-# (lld supports -shared)
-
import os
import sys
import copy
@@ -101,6 +60,12 @@ def get_msvcr():
raise ValueError("Unknown MS Compiler version %s " % msc_ver)
+_runtime_library_dirs_msg = (
+ "Unable to set runtime library search path on Windows, "
+ "usually indicated by `runtime_library_dirs` parameter to Extension"
+)
+
+
class CygwinCCompiler(UnixCCompiler):
"""Handles the Cygwin port of the GNU C compiler to Windows."""
@@ -119,7 +84,9 @@ class CygwinCCompiler(UnixCCompiler):
super().__init__(verbose, dry_run, force)
status, details = check_config_h()
- self.debug_print("Python's GCC status: %s (details: %s)" % (status, details))
+ self.debug_print(
+ "Python's GCC status: {} (details: {})".format(status, details)
+ )
if status is not CONFIG_H_OK:
self.warn(
"Python's pyconfig.h doesn't seem to support your compiler. "
@@ -138,7 +105,7 @@ class CygwinCCompiler(UnixCCompiler):
compiler_so='%s -mcygwin -mdll -O -Wall' % self.cc,
compiler_cxx='%s -mcygwin -O -Wall' % self.cxx,
linker_exe='%s -mcygwin' % self.cc,
- linker_so=('%s -mcygwin %s' % (self.linker_dll, shared_option)),
+ linker_so=('{} -mcygwin {}'.format(self.linker_dll, shared_option)),
)
# Include the appropriate MSVC runtime library if Python was built
@@ -199,10 +166,7 @@ class CygwinCCompiler(UnixCCompiler):
objects = copy.copy(objects or [])
if runtime_library_dirs:
- self.warn(
- "I don't know what to do with 'runtime_library_dirs': "
- + str(runtime_library_dirs)
- )
+ self.warn(_runtime_library_dirs_msg)
# Additional libraries
libraries.extend(self.dll_libraries)
@@ -273,7 +237,7 @@ class CygwinCCompiler(UnixCCompiler):
# cygwin doesn't support rpath. While in theory we could error
# out like MSVC does, code might expect it to work like on Unix, so
# just warn and hope for the best.
- self.warn("don't know how to set runtime library search path on Windows")
+ self.warn(_runtime_library_dirs_msg)
return []
# -- Miscellaneous methods -----------------------------------------
@@ -288,7 +252,7 @@ class CygwinCCompiler(UnixCCompiler):
base, ext = os.path.splitext(os.path.normcase(src_name))
if ext not in (self.src_extensions + ['.rc', '.res']):
raise UnknownFileError(
- "unknown file type '%s' (from '%s')" % (ext, src_name)
+ "unknown file type '{}' (from '{}')".format(ext, src_name)
)
if strip_dir:
base = os.path.basename(base)
@@ -322,7 +286,7 @@ class Mingw32CCompiler(CygwinCCompiler):
compiler_so='%s -mdll -O -Wall' % self.cc,
compiler_cxx='%s -O -Wall' % self.cxx,
linker_exe='%s' % self.cc,
- linker_so='%s %s' % (self.linker_dll, shared_option),
+ linker_so='{} {}'.format(self.linker_dll, shared_option),
)
# Maybe we should also append -mthreads, but then the finished
@@ -337,9 +301,7 @@ class Mingw32CCompiler(CygwinCCompiler):
self.dll_libraries = get_msvcr()
def runtime_library_dir_option(self, dir):
- raise DistutilsPlatformError(
- "don't know how to set runtime library search path on Windows"
- )
+ raise DistutilsPlatformError(_runtime_library_dirs_msg)
# Because these compilers aren't configured in Python's pyconfig.h file by
@@ -395,7 +357,7 @@ def check_config_h():
finally:
config_h.close()
except OSError as exc:
- return (CONFIG_H_UNCERTAIN, "couldn't read '%s': %s" % (fn, exc.strerror))
+ return (CONFIG_H_UNCERTAIN, "couldn't read '{}': {}".format(fn, exc.strerror))
def is_cygwincc(cc):
diff --git a/distutils/dir_util.py b/distutils/dir_util.py
index 7a132e31..6f0bb8ad 100644
--- a/distutils/dir_util.py
+++ b/distutils/dir_util.py
@@ -34,7 +34,7 @@ def mkpath(name, mode=0o777, verbose=1, dry_run=0): # noqa: C901
# Detect a common bug -- name is None
if not isinstance(name, str):
raise DistutilsInternalError(
- "mkpath: 'name' must be a string (got %r)" % (name,)
+ "mkpath: 'name' must be a string (got {!r})".format(name)
)
# XXX what's the better way to handle verbosity? print as we create
@@ -76,7 +76,7 @@ def mkpath(name, mode=0o777, verbose=1, dry_run=0): # noqa: C901
except OSError as exc:
if not (exc.errno == errno.EEXIST and os.path.isdir(head)):
raise DistutilsFileError(
- "could not create '%s': %s" % (head, exc.args[-1])
+ "could not create '{}': {}".format(head, exc.args[-1])
)
created_dirs.append(head)
@@ -144,7 +144,7 @@ def copy_tree( # noqa: C901
names = []
else:
raise DistutilsFileError(
- "error listing files in '%s': %s" % (src, e.strerror)
+ "error listing files in '{}': {}".format(src, e.strerror)
)
if not dry_run:
diff --git a/distutils/dist.py b/distutils/dist.py
index b4535eb7..0406ab19 100644
--- a/distutils/dist.py
+++ b/distutils/dist.py
@@ -825,7 +825,7 @@ Common commands: (see '--help-commands' for more)
return klass
for pkgname in self.get_command_packages():
- module_name = "%s.%s" % (pkgname, command)
+ module_name = "{}.{}".format(pkgname, command)
klass_name = command
try:
@@ -893,7 +893,7 @@ Common commands: (see '--help-commands' for more)
self.announce(" setting options for '%s' command:" % command_name)
for (option, (source, value)) in option_dict.items():
if DEBUG:
- self.announce(" %s = %s (from %s)" % (option, value, source))
+ self.announce(" {} = {} (from {})".format(option, value, source))
try:
bool_opts = [translate_longopt(o) for o in command_obj.boolean_options]
except AttributeError:
@@ -1159,7 +1159,7 @@ class DistributionMetadata:
def maybe_write(header, val):
if val:
- file.write("{}: {}\n".format(header, val))
+ file.write(f"{header}: {val}\n")
# optional fields
maybe_write("Summary", self.get_description())
@@ -1182,7 +1182,7 @@ class DistributionMetadata:
def _write_list(self, file, name, values):
values = values or []
for value in values:
- file.write('%s: %s\n' % (name, value))
+ file.write('{}: {}\n'.format(name, value))
# -- Metadata query methods ----------------------------------------
@@ -1193,7 +1193,7 @@ class DistributionMetadata:
return self.version or "0.0.0"
def get_fullname(self):
- return "%s-%s" % (self.get_name(), self.get_version())
+ return "{}-{}".format(self.get_name(), self.get_version())
def get_author(self):
return self.author
diff --git a/distutils/extension.py b/distutils/extension.py
index dff2be9e..6b8575de 100644
--- a/distutils/extension.py
+++ b/distutils/extension.py
@@ -134,7 +134,7 @@ class Extension:
warnings.warn(msg)
def __repr__(self):
- return '<%s.%s(%r) at %#x>' % (
+ return '<{}.{}({!r}) at {:#x}>'.format(
self.__class__.__module__,
self.__class__.__qualname__,
self.name,
diff --git a/distutils/fancy_getopt.py b/distutils/fancy_getopt.py
index 9ee06420..830f047e 100644
--- a/distutils/fancy_getopt.py
+++ b/distutils/fancy_getopt.py
@@ -22,7 +22,7 @@ longopt_pat = r'[a-zA-Z](?:[a-zA-Z0-9-]*)'
longopt_re = re.compile(r'^%s$' % longopt_pat)
# For recognizing "negative alias" options, eg. "quiet=!verbose"
-neg_alias_re = re.compile("^(%s)=!(%s)$" % (longopt_pat, longopt_pat))
+neg_alias_re = re.compile("^({})=!({})$".format(longopt_pat, longopt_pat))
# This is used to translate long options to legitimate Python identifiers
# (for use as attributes of some object).
@@ -157,7 +157,7 @@ class FancyGetopt:
else:
# the option table is part of the code, so simply
# assert that it is correct
- raise ValueError("invalid option tuple: %r" % (option,))
+ raise ValueError("invalid option tuple: {!r}".format(option))
# Type- and value-check the option names
if not isinstance(long, str) or len(long) < 2:
@@ -359,7 +359,7 @@ class FancyGetopt:
# Case 2: we have a short option, so we have to include it
# just after the long option
else:
- opt_names = "%s (-%s)" % (long, short)
+ opt_names = "{} (-{})".format(long, short)
if text:
lines.append(" --%-*s %s" % (max_opt, opt_names, text[0]))
else:
diff --git a/distutils/file_util.py b/distutils/file_util.py
index 0662fe40..1f1e444b 100644
--- a/distutils/file_util.py
+++ b/distutils/file_util.py
@@ -26,27 +26,29 @@ def _copy_file_contents(src, dst, buffer_size=16 * 1024): # noqa: C901
try:
fsrc = open(src, 'rb')
except OSError as e:
- raise DistutilsFileError("could not open '%s': %s" % (src, e.strerror))
+ raise DistutilsFileError("could not open '{}': {}".format(src, e.strerror))
if os.path.exists(dst):
try:
os.unlink(dst)
except OSError as e:
raise DistutilsFileError(
- "could not delete '%s': %s" % (dst, e.strerror)
+ "could not delete '{}': {}".format(dst, e.strerror)
)
try:
fdst = open(dst, 'wb')
except OSError as e:
- raise DistutilsFileError("could not create '%s': %s" % (dst, e.strerror))
+ raise DistutilsFileError(
+ "could not create '{}': {}".format(dst, e.strerror)
+ )
while True:
try:
buf = fsrc.read(buffer_size)
except OSError as e:
raise DistutilsFileError(
- "could not read from '%s': %s" % (src, e.strerror)
+ "could not read from '{}': {}".format(src, e.strerror)
)
if not buf:
@@ -56,7 +58,7 @@ def _copy_file_contents(src, dst, buffer_size=16 * 1024): # noqa: C901
fdst.write(buf)
except OSError as e:
raise DistutilsFileError(
- "could not write to '%s': %s" % (dst, e.strerror)
+ "could not write to '{}': {}".format(dst, e.strerror)
)
finally:
if fdst:
@@ -198,12 +200,12 @@ def move_file(src, dst, verbose=1, dry_run=0): # noqa: C901
dst = os.path.join(dst, basename(src))
elif exists(dst):
raise DistutilsFileError(
- "can't move '%s': destination '%s' already exists" % (src, dst)
+ "can't move '{}': destination '{}' already exists".format(src, dst)
)
if not isdir(dirname(dst)):
raise DistutilsFileError(
- "can't move '%s': destination '%s' not a valid path" % (src, dst)
+ "can't move '{}': destination '{}' not a valid path".format(src, dst)
)
copy_it = False
@@ -214,7 +216,9 @@ def move_file(src, dst, verbose=1, dry_run=0): # noqa: C901
if num == errno.EXDEV:
copy_it = True
else:
- raise DistutilsFileError("couldn't move '%s' to '%s': %s" % (src, dst, msg))
+ raise DistutilsFileError(
+ "couldn't move '{}' to '{}': {}".format(src, dst, msg)
+ )
if copy_it:
copy_file(src, dst, verbose=verbose)
diff --git a/distutils/filelist.py b/distutils/filelist.py
index 4396d9de..987931a9 100644
--- a/distutils/filelist.py
+++ b/distutils/filelist.py
@@ -159,7 +159,7 @@ class FileList:
)
elif action == 'recursive-include':
- self.debug_print("recursive-include %s %s" % (dir, ' '.join(patterns)))
+ self.debug_print("recursive-include {} {}".format(dir, ' '.join(patterns)))
for pattern in patterns:
if not self.include_pattern(pattern, prefix=dir):
msg = (
@@ -168,7 +168,7 @@ class FileList:
log.warn(msg, pattern, dir)
elif action == 'recursive-exclude':
- self.debug_print("recursive-exclude %s %s" % (dir, ' '.join(patterns)))
+ self.debug_print("recursive-exclude {} {}".format(dir, ' '.join(patterns)))
for pattern in patterns:
if not self.exclude_pattern(pattern, prefix=dir):
log.warn(
@@ -363,9 +363,9 @@ def translate_pattern(pattern, anchor=1, prefix=None, is_regex=0):
if os.sep == '\\':
sep = r'\\'
pattern_re = pattern_re[len(start) : len(pattern_re) - len(end)]
- pattern_re = r'%s\A%s%s.*%s%s' % (start, prefix_re, sep, pattern_re, end)
+ pattern_re = r'{}\A{}{}.*{}{}'.format(start, prefix_re, sep, pattern_re, end)
else: # no prefix -- respect anchor flag
if anchor:
- pattern_re = r'%s\A%s' % (start, pattern_re[len(start) :])
+ pattern_re = r'{}\A{}'.format(start, pattern_re[len(start) :])
return re.compile(pattern_re)
diff --git a/distutils/msvc9compiler.py b/distutils/msvc9compiler.py
deleted file mode 100644
index 276e1379..00000000
--- a/distutils/msvc9compiler.py
+++ /dev/null
@@ -1,820 +0,0 @@
-"""distutils.msvc9compiler
-
-Contains MSVCCompiler, an implementation of the abstract CCompiler class
-for the Microsoft Visual Studio 2008.
-
-The module is compatible with VS 2005 and VS 2008. You can find legacy support
-for older versions of VS in distutils.msvccompiler.
-"""
-
-# Written by Perry Stoll
-# hacked by Robin Becker and Thomas Heller to do a better job of
-# finding DevStudio (through the registry)
-# ported to VS2005 and VS 2008 by Christian Heimes
-
-import os
-import subprocess
-import sys
-import re
-
-from distutils.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
-
-import winreg
-
-RegOpenKeyEx = winreg.OpenKeyEx
-RegEnumKey = winreg.EnumKey
-RegEnumValue = winreg.EnumValue
-RegError = winreg.error
-
-HKEYS = (
- winreg.HKEY_USERS,
- winreg.HKEY_CURRENT_USER,
- winreg.HKEY_LOCAL_MACHINE,
- winreg.HKEY_CLASSES_ROOT,
-)
-
-NATIVE_WIN64 = sys.platform == 'win32' and sys.maxsize > 2**32
-if NATIVE_WIN64:
- # Visual C++ is a 32-bit application, so we need to look in
- # the corresponding registry branch, if we're running a
- # 64-bit Python on Win64
- VS_BASE = r"Software\Wow6432Node\Microsoft\VisualStudio\%0.1f"
- WINSDK_BASE = r"Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows"
- NET_BASE = r"Software\Wow6432Node\Microsoft\.NETFramework"
-else:
- VS_BASE = r"Software\Microsoft\VisualStudio\%0.1f"
- WINSDK_BASE = r"Software\Microsoft\Microsoft SDKs\Windows"
- NET_BASE = r"Software\Microsoft\.NETFramework"
-
-# A map keyed by get_platform() return values to values accepted by
-# 'vcvarsall.bat'. Note a cross-compile may combine these (eg, 'x86_amd64' is
-# the param to cross-compile on x86 targeting amd64.)
-PLAT_TO_VCVARS = {
- 'win32': 'x86',
- 'win-amd64': 'amd64',
-}
-
-
-class Reg:
- """Helper class to read values from the registry"""
-
- def get_value(cls, path, key):
- for base in HKEYS:
- d = cls.read_values(base, path)
- if d and key in d:
- return d[key]
- raise KeyError(key)
-
- get_value = classmethod(get_value)
-
- def read_keys(cls, base, key):
- """Return list of registry keys."""
- try:
- handle = RegOpenKeyEx(base, key)
- except RegError:
- return None
- L = []
- i = 0
- while True:
- try:
- k = RegEnumKey(handle, i)
- except RegError:
- break
- L.append(k)
- i += 1
- return L
-
- read_keys = classmethod(read_keys)
-
- def read_values(cls, base, key):
- """Return dict of registry keys and values.
-
- All names are converted to lowercase.
- """
- try:
- handle = RegOpenKeyEx(base, key)
- except RegError:
- return None
- d = {}
- i = 0
- while True:
- try:
- name, value, type = RegEnumValue(handle, i)
- except RegError:
- break
- name = name.lower()
- d[cls.convert_mbcs(name)] = cls.convert_mbcs(value)
- i += 1
- return d
-
- read_values = classmethod(read_values)
-
- def convert_mbcs(s):
- dec = getattr(s, "decode", None)
- if dec is not None:
- try:
- s = dec("mbcs")
- except UnicodeError:
- pass
- return s
-
- convert_mbcs = staticmethod(convert_mbcs)
-
-
-class MacroExpander:
- def __init__(self, version):
- self.macros = {}
- self.vsbase = VS_BASE % version
- self.load_macros(version)
-
- def set_macro(self, macro, path, key):
- self.macros["$(%s)" % macro] = Reg.get_value(path, key)
-
- def load_macros(self, version):
- self.set_macro("VCInstallDir", self.vsbase + r"\Setup\VC", "productdir")
- self.set_macro("VSInstallDir", self.vsbase + r"\Setup\VS", "productdir")
- self.set_macro("FrameworkDir", NET_BASE, "installroot")
- try:
- if version >= 8.0:
- self.set_macro("FrameworkSDKDir", NET_BASE, "sdkinstallrootv2.0")
- else:
- raise KeyError("sdkinstallrootv2.0")
- except KeyError:
- raise DistutilsPlatformError(
- """Python was built with Visual Studio 2008;
-extensions must be built with a compiler than can generate compatible binaries.
-Visual Studio 2008 was not found on this system. If you have Cygwin installed,
-you can try compiling with MingW32, by passing "-c mingw32" to setup.py."""
- )
-
- if version >= 9.0:
- self.set_macro("FrameworkVersion", self.vsbase, "clr version")
- self.set_macro("WindowsSdkDir", WINSDK_BASE, "currentinstallfolder")
- else:
- p = r"Software\Microsoft\NET Framework Setup\Product"
- for base in HKEYS:
- try:
- h = RegOpenKeyEx(base, p)
- except RegError:
- continue
- key = RegEnumKey(h, 0)
- d = Reg.get_value(base, r"%s\%s" % (p, key))
- self.macros["$(FrameworkVersion)"] = d["version"]
-
- def sub(self, s):
- for k, v in self.macros.items():
- s = s.replace(k, v)
- return s
-
-
-def get_build_version():
- """Return the version of MSVC that was used to build Python.
-
- For Python 2.3 and up, the version number is included in
- sys.version. For earlier versions, assume the compiler is MSVC 6.
- """
- prefix = "MSC v."
- i = sys.version.find(prefix)
- if i == -1:
- return 6
- i = i + len(prefix)
- s, rest = sys.version[i:].split(" ", 1)
- majorVersion = int(s[:-2]) - 6
- if majorVersion >= 13:
- # v13 was skipped and should be v14
- majorVersion += 1
- minorVersion = int(s[2:3]) / 10.0
- # I don't think paths are affected by minor version in version 6
- if majorVersion == 6:
- minorVersion = 0
- if majorVersion >= 6:
- return majorVersion + minorVersion
- # else we don't know what version of the compiler this is
- return None
-
-
-def normalize_and_reduce_paths(paths):
- """Return a list of normalized paths with duplicates removed.
-
- The current order of paths is maintained.
- """
- # Paths are normalized so things like: /a and /a/ aren't both preserved.
- reduced_paths = []
- for p in paths:
- np = os.path.normpath(p)
- # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
- if np not in reduced_paths:
- reduced_paths.append(np)
- return reduced_paths
-
-
-def removeDuplicates(variable):
- """Remove duplicate values of an environment variable."""
- oldList = variable.split(os.pathsep)
- newList = []
- for i in oldList:
- if i not in newList:
- newList.append(i)
- newVariable = os.pathsep.join(newList)
- return newVariable
-
-
-def find_vcvarsall(version):
- """Find the vcvarsall.bat file
-
- At first it tries to find the productdir of VS 2008 in the registry. If
- that fails it falls back to the VS90COMNTOOLS env var.
- """
- vsbase = VS_BASE % version
- try:
- productdir = Reg.get_value(r"%s\Setup\VC" % vsbase, "productdir")
- except KeyError:
- log.debug("Unable to find productdir in registry")
- productdir = None
-
- if not productdir or not os.path.isdir(productdir):
- toolskey = "VS%0.f0COMNTOOLS" % version
- toolsdir = os.environ.get(toolskey, None)
-
- if toolsdir and os.path.isdir(toolsdir):
- productdir = os.path.join(toolsdir, os.pardir, os.pardir, "VC")
- productdir = os.path.abspath(productdir)
- if not os.path.isdir(productdir):
- log.debug("%s is not a valid directory" % productdir)
- return None
- else:
- log.debug("Env var %s is not set or invalid" % toolskey)
- if not productdir:
- log.debug("No productdir found")
- return None
- vcvarsall = os.path.join(productdir, "vcvarsall.bat")
- if os.path.isfile(vcvarsall):
- return vcvarsall
- log.debug("Unable to find vcvarsall.bat")
- return None
-
-
-def query_vcvarsall(version, arch="x86"):
- """Launch vcvarsall.bat and read the settings from its environment"""
- vcvarsall = find_vcvarsall(version)
- interesting = {"include", "lib", "libpath", "path"}
- result = {}
-
- if vcvarsall is None:
- raise DistutilsPlatformError("Unable to find vcvarsall.bat")
- log.debug("Calling 'vcvarsall.bat %s' (version=%s)", arch, version)
- popen = subprocess.Popen(
- '"%s" %s & set' % (vcvarsall, arch),
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- )
- try:
- stdout, stderr = popen.communicate()
- if popen.wait() != 0:
- raise DistutilsPlatformError(stderr.decode("mbcs"))
-
- stdout = stdout.decode("mbcs")
- for line in stdout.split("\n"):
- line = Reg.convert_mbcs(line)
- if '=' not in line:
- continue
- line = line.strip()
- key, value = line.split('=', 1)
- key = key.lower()
- if key in interesting:
- if value.endswith(os.pathsep):
- value = value[:-1]
- result[key] = removeDuplicates(value)
-
- finally:
- popen.stdout.close()
- popen.stderr.close()
-
- if len(result) != len(interesting):
- raise ValueError(str(list(result.keys())))
-
- return result
-
-
-# More globals
-VERSION = get_build_version()
-# MACROS = MacroExpander(VERSION)
-
-
-class MSVCCompiler(CCompiler):
- """Concrete class that implements an interface to Microsoft Visual C++,
- as defined by the CCompiler abstract class."""
-
- compiler_type = 'msvc'
-
- # Just set this so CCompiler's constructor doesn't barf. We currently
- # don't use the 'set_executables()' bureaucracy provided by CCompiler,
- # as it really isn't necessary for this sort of single-compiler class.
- # Would be nice to have a consistent interface with UnixCCompiler,
- # though, so it's worth thinking about.
- executables = {}
-
- # Private class data (need to distinguish C from C++ source for compiler)
- _c_extensions = ['.c']
- _cpp_extensions = ['.cc', '.cpp', '.cxx']
- _rc_extensions = ['.rc']
- _mc_extensions = ['.mc']
-
- # Needed for the filename generation methods provided by the
- # base class, CCompiler.
- src_extensions = _c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions
- res_extension = '.res'
- obj_extension = '.obj'
- static_lib_extension = '.lib'
- shared_lib_extension = '.dll'
- static_lib_format = shared_lib_format = '%s%s'
- exe_extension = '.exe'
-
- def __init__(self, verbose=0, dry_run=0, force=0):
- super().__init__(verbose, dry_run, force)
- self.__version = VERSION
- self.__root = r"Software\Microsoft\VisualStudio"
- # self.__macros = MACROS
- self.__paths = []
- # target platform (.plat_name is consistent with 'bdist')
- self.plat_name = None
- self.__arch = None # deprecated name
- self.initialized = False
-
- def initialize(self, plat_name=None): # noqa: C901
- # multi-init means we would need to check platform same each time...
- assert not self.initialized, "don't init multiple times"
- if self.__version < 8.0:
- raise DistutilsPlatformError(
- "VC %0.1f is not supported by this module" % self.__version
- )
- if plat_name is None:
- plat_name = get_platform()
- # sanity check for platforms to prevent obscure errors later.
- ok_plats = 'win32', 'win-amd64'
- if plat_name not in ok_plats:
- raise DistutilsPlatformError("--plat-name must be one of %s" % (ok_plats,))
-
- if (
- "DISTUTILS_USE_SDK" in os.environ
- and "MSSdk" in os.environ
- and self.find_exe("cl.exe")
- ):
- # Assume that the SDK set up everything alright; don't try to be
- # smarter
- self.cc = "cl.exe"
- self.linker = "link.exe"
- self.lib = "lib.exe"
- self.rc = "rc.exe"
- self.mc = "mc.exe"
- else:
- # On x86, 'vcvars32.bat amd64' creates an env that doesn't work;
- # to cross compile, you use 'x86_amd64'.
- # On AMD64, 'vcvars32.bat amd64' is a native build env; to cross
- # compile use 'x86' (ie, it runs the x86 compiler directly)
- if plat_name == get_platform() or plat_name == 'win32':
- # native build or cross-compile to win32
- plat_spec = PLAT_TO_VCVARS[plat_name]
- else:
- # cross compile from win32 -> some 64bit
- plat_spec = (
- PLAT_TO_VCVARS[get_platform()] + '_' + PLAT_TO_VCVARS[plat_name]
- )
-
- vc_env = query_vcvarsall(VERSION, plat_spec)
-
- self.__paths = vc_env['path'].split(os.pathsep)
- os.environ['lib'] = vc_env['lib']
- os.environ['include'] = vc_env['include']
-
- if len(self.__paths) == 0:
- raise DistutilsPlatformError(
- "Python was built with %s, "
- "and extensions need to be built with the same "
- "version of the compiler, but it isn't installed." % self.__product
- )
-
- self.cc = self.find_exe("cl.exe")
- self.linker = self.find_exe("link.exe")
- self.lib = self.find_exe("lib.exe")
- self.rc = self.find_exe("rc.exe") # resource compiler
- self.mc = self.find_exe("mc.exe") # message compiler
- # self.set_path_env_var('lib')
- # self.set_path_env_var('include')
-
- # extend the MSVC path with the current path
- try:
- for p in os.environ['path'].split(';'):
- self.__paths.append(p)
- except KeyError:
- pass
- self.__paths = normalize_and_reduce_paths(self.__paths)
- os.environ['path'] = ";".join(self.__paths)
-
- self.preprocess_options = None
- if self.__arch == "x86":
- self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/DNDEBUG']
- self.compile_options_debug = [
- '/nologo',
- '/Od',
- '/MDd',
- '/W3',
- '/Z7',
- '/D_DEBUG',
- ]
- else:
- # Win64
- self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GS-', '/DNDEBUG']
- self.compile_options_debug = [
- '/nologo',
- '/Od',
- '/MDd',
- '/W3',
- '/GS-',
- '/Z7',
- '/D_DEBUG',
- ]
-
- self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
- if self.__version >= 7:
- self.ldflags_shared_debug = ['/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG']
- self.ldflags_static = ['/nologo']
-
- self.initialized = True
-
- # -- Worker methods ------------------------------------------------
-
- def object_filenames(self, source_filenames, strip_dir=0, output_dir=''):
- # Copied from ccompiler.py, extended to return .res as 'object'-file
- # for .rc input file
- if output_dir is None:
- output_dir = ''
- obj_names = []
- for src_name in source_filenames:
- (base, ext) = os.path.splitext(src_name)
- base = os.path.splitdrive(base)[1] # Chop off the drive
- base = base[os.path.isabs(base) :] # If abs, chop off leading /
- if ext not in self.src_extensions:
- # Better to raise an exception instead of silently continuing
- # and later complain about sources and targets having
- # different lengths
- raise CompileError("Don't know how to compile %s" % src_name)
- if strip_dir:
- base = os.path.basename(base)
- if ext in self._rc_extensions:
- obj_names.append(os.path.join(output_dir, base + self.res_extension))
- elif ext in self._mc_extensions:
- obj_names.append(os.path.join(output_dir, base + self.res_extension))
- else:
- obj_names.append(os.path.join(output_dir, base + self.obj_extension))
- return obj_names
-
- def compile( # noqa: C901
- self,
- sources,
- output_dir=None,
- macros=None,
- include_dirs=None,
- debug=0,
- extra_preargs=None,
- extra_postargs=None,
- depends=None,
- ):
-
- if not self.initialized:
- self.initialize()
- compile_info = self._setup_compile(
- output_dir, macros, include_dirs, sources, depends, extra_postargs
- )
- macros, objects, extra_postargs, pp_opts, build = compile_info
-
- compile_opts = extra_preargs or []
- compile_opts.append('/c')
- if debug:
- compile_opts.extend(self.compile_options_debug)
- else:
- compile_opts.extend(self.compile_options)
-
- for obj in objects:
- try:
- src, ext = build[obj]
- except KeyError:
- continue
- if debug:
- # pass the full pathname to MSVC in debug mode,
- # this allows the debugger to find the source file
- # without asking the user to browse for it
- src = os.path.abspath(src)
-
- if ext in self._c_extensions:
- input_opt = "/Tc" + src
- elif ext in self._cpp_extensions:
- input_opt = "/Tp" + src
- elif ext in self._rc_extensions:
- # compile .RC to .RES file
- input_opt = src
- output_opt = "/fo" + obj
- try:
- self.spawn([self.rc] + pp_opts + [output_opt] + [input_opt])
- except DistutilsExecError as msg:
- raise CompileError(msg)
- continue
- elif ext in self._mc_extensions:
- # Compile .MC to .RC file to .RES file.
- # * '-h dir' specifies the directory for the
- # generated include file
- # * '-r dir' specifies the target directory of the
- # generated RC file and the binary message resource
- # it includes
- #
- # For now (since there are no options to change this),
- # we use the source-directory for the include file and
- # the build directory for the RC file and message
- # resources. This works at least for win32all.
- h_dir = os.path.dirname(src)
- rc_dir = os.path.dirname(obj)
- try:
- # first compile .MC to .RC and .H file
- self.spawn([self.mc] + ['-h', h_dir, '-r', rc_dir] + [src])
- base, _ = os.path.splitext(os.path.basename(src))
- rc_file = os.path.join(rc_dir, base + '.rc')
- # then compile .RC to .RES file
- self.spawn([self.rc] + ["/fo" + obj] + [rc_file])
-
- except DistutilsExecError as msg:
- raise CompileError(msg)
- continue
- else:
- # how to handle this file?
- raise CompileError("Don't know how to compile %s to %s" % (src, obj))
-
- output_opt = "/Fo" + obj
- try:
- self.spawn(
- [self.cc]
- + compile_opts
- + pp_opts
- + [input_opt, output_opt]
- + extra_postargs
- )
- except DistutilsExecError as msg:
- raise CompileError(msg)
-
- return objects
-
- def create_static_lib(
- self, objects, output_libname, output_dir=None, debug=0, target_lang=None
- ):
-
- if not self.initialized:
- self.initialize()
- (objects, output_dir) = self._fix_object_args(objects, output_dir)
- output_filename = self.library_filename(output_libname, output_dir=output_dir)
-
- if self._need_link(objects, output_filename):
- lib_args = objects + ['/OUT:' + output_filename]
- if debug:
- pass # XXX what goes here?
- try:
- self.spawn([self.lib] + lib_args)
- except DistutilsExecError as msg:
- raise LibError(msg)
- else:
- log.debug("skipping %s (up-to-date)", output_filename)
-
- def link( # noqa: C901
- self,
- target_desc,
- objects,
- output_filename,
- output_dir=None,
- libraries=None,
- library_dirs=None,
- runtime_library_dirs=None,
- export_symbols=None,
- debug=0,
- extra_preargs=None,
- extra_postargs=None,
- build_temp=None,
- target_lang=None,
- ):
-
- if not self.initialized:
- self.initialize()
- (objects, output_dir) = self._fix_object_args(objects, output_dir)
- fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
- (libraries, library_dirs, runtime_library_dirs) = fixed_args
-
- if runtime_library_dirs:
- self.warn(
- "I don't know what to do with 'runtime_library_dirs': "
- + str(runtime_library_dirs)
- )
-
- lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries)
- if output_dir is not None:
- output_filename = os.path.join(output_dir, output_filename)
-
- if self._need_link(objects, output_filename):
- if target_desc == CCompiler.EXECUTABLE:
- if debug:
- ldflags = self.ldflags_shared_debug[1:]
- else:
- ldflags = self.ldflags_shared[1:]
- else:
- if debug:
- ldflags = self.ldflags_shared_debug
- else:
- ldflags = self.ldflags_shared
-
- export_opts = []
- for sym in export_symbols or []:
- export_opts.append("/EXPORT:" + sym)
-
- ld_args = (
- ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename]
- )
-
- # The MSVC linker generates .lib and .exp files, which cannot be
- # suppressed by any linker switches. The .lib files may even be
- # needed! Make sure they are generated in the temporary build
- # directory. Since they have different names for debug and release
- # builds, they can go into the same directory.
- build_temp = os.path.dirname(objects[0])
- if export_symbols is not None:
- (dll_name, dll_ext) = os.path.splitext(
- os.path.basename(output_filename)
- )
- implib_file = os.path.join(build_temp, self.library_filename(dll_name))
- ld_args.append('/IMPLIB:' + implib_file)
-
- self.manifest_setup_ldargs(output_filename, build_temp, ld_args)
-
- if extra_preargs:
- ld_args[:0] = extra_preargs
- if extra_postargs:
- ld_args.extend(extra_postargs)
-
- self.mkpath(os.path.dirname(output_filename))
- try:
- self.spawn([self.linker] + ld_args)
- except DistutilsExecError as msg:
- raise LinkError(msg)
-
- # embed the manifest
- # XXX - this is somewhat fragile - if mt.exe fails, distutils
- # will still consider the DLL up-to-date, but it will not have a
- # manifest. Maybe we should link to a temp file? OTOH, that
- # implies a build environment error that shouldn't go undetected.
- mfinfo = self.manifest_get_embed_info(target_desc, ld_args)
- if mfinfo is not None:
- mffilename, mfid = mfinfo
- out_arg = '-outputresource:%s;%s' % (output_filename, mfid)
- try:
- self.spawn(['mt.exe', '-nologo', '-manifest', mffilename, out_arg])
- except DistutilsExecError as msg:
- raise LinkError(msg)
- else:
- log.debug("skipping %s (up-to-date)", output_filename)
-
- def manifest_setup_ldargs(self, output_filename, build_temp, ld_args):
- # If we need a manifest at all, an embedded manifest is recommended.
- # See MSDN article titled
- # "How to: Embed a Manifest Inside a C/C++ Application"
- # (currently at http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx)
- # Ask the linker to generate the manifest in the temp dir, so
- # we can check it, and possibly embed it, later.
- temp_manifest = os.path.join(
- build_temp, os.path.basename(output_filename) + ".manifest"
- )
- ld_args.append('/MANIFESTFILE:' + temp_manifest)
-
- def manifest_get_embed_info(self, target_desc, ld_args):
- # If a manifest should be embedded, return a tuple of
- # (manifest_filename, resource_id). Returns None if no manifest
- # should be embedded. See http://bugs.python.org/issue7833 for why
- # we want to avoid any manifest for extension modules if we can)
- for arg in ld_args:
- if arg.startswith("/MANIFESTFILE:"):
- temp_manifest = arg.split(":", 1)[1]
- break
- else:
- # no /MANIFESTFILE so nothing to do.
- return None
- if target_desc == CCompiler.EXECUTABLE:
- # by default, executables always get the manifest with the
- # CRT referenced.
- mfid = 1
- else:
- # Extension modules try and avoid any manifest if possible.
- mfid = 2
- temp_manifest = self._remove_visual_c_ref(temp_manifest)
- if temp_manifest is None:
- return None
- return temp_manifest, mfid
-
- def _remove_visual_c_ref(self, manifest_file):
- try:
- # Remove references to the Visual C runtime, so they will
- # fall through to the Visual C dependency of Python.exe.
- # This way, when installed for a restricted user (e.g.
- # runtimes are not in WinSxS folder, but in Python's own
- # folder), the runtimes do not need to be in every folder
- # with .pyd's.
- # Returns either the filename of the modified manifest or
- # None if no manifest should be embedded.
- manifest_f = open(manifest_file)
- try:
- manifest_buf = manifest_f.read()
- finally:
- manifest_f.close()
- pattern = re.compile(
- r"""<assemblyIdentity.*?name=("|')Microsoft\."""
- r"""VC\d{2}\.CRT("|').*?(/>|</assemblyIdentity>)""",
- re.DOTALL,
- )
- manifest_buf = re.sub(pattern, "", manifest_buf)
- pattern = r"<dependentAssembly>\s*</dependentAssembly>"
- manifest_buf = re.sub(pattern, "", manifest_buf)
- # Now see if any other assemblies are referenced - if not, we
- # don't want a manifest embedded.
- pattern = re.compile(
- r"""<assemblyIdentity.*?name=(?:"|')(.+?)(?:"|')"""
- r""".*?(?:/>|</assemblyIdentity>)""",
- re.DOTALL,
- )
- if re.search(pattern, manifest_buf) is None:
- return None
-
- manifest_f = open(manifest_file, 'w')
- try:
- manifest_f.write(manifest_buf)
- return manifest_file
- finally:
- manifest_f.close()
- except OSError:
- pass
-
- # -- Miscellaneous methods -----------------------------------------
- # These are all used by the 'gen_lib_options() function, in
- # ccompiler.py.
-
- def library_dir_option(self, dir):
- return "/LIBPATH:" + dir
-
- def runtime_library_dir_option(self, dir):
- raise DistutilsPlatformError(
- "don't know how to set runtime library search path for MSVC++"
- )
-
- def library_option(self, lib):
- return self.library_filename(lib)
-
- def find_library_file(self, dirs, lib, debug=0):
- # Prefer a debugging library if found (and requested), but deal
- # with it if we don't have one.
- if debug:
- try_names = [lib + "_d", lib]
- else:
- try_names = [lib]
- for dir in dirs:
- for name in try_names:
- libfile = os.path.join(dir, self.library_filename(name))
- if os.path.exists(libfile):
- return libfile
- else:
- # Oops, didn't find it in *any* of 'dirs'
- return None
-
- # Helper methods for using the MSVC registry settings
-
- def find_exe(self, exe):
- """Return path to an MSVC executable program.
-
- Tries to find the program in several places: first, one of the
- MSVC program search paths from the registry; next, the directories
- in the PATH environment variable. If any of those work, return an
- absolute path that is known to exist. If none of them work, just
- return the original program name, 'exe'.
- """
- for p in self.__paths:
- fn = os.path.join(os.path.abspath(p), exe)
- if os.path.isfile(fn):
- return fn
-
- # didn't find it; try existing path
- for p in os.environ['Path'].split(';'):
- fn = os.path.join(os.path.abspath(p), exe)
- if os.path.isfile(fn):
- return fn
-
- return exe
diff --git a/distutils/msvccompiler.py b/distutils/msvccompiler.py
deleted file mode 100644
index 8e509ac5..00000000
--- a/distutils/msvccompiler.py
+++ /dev/null
@@ -1,684 +0,0 @@
-"""distutils.msvccompiler
-
-Contains MSVCCompiler, an implementation of the abstract CCompiler class
-for the Microsoft Visual Studio.
-"""
-
-# Written by Perry Stoll
-# hacked by Robin Becker and Thomas Heller to do a better job of
-# finding DevStudio (through the registry)
-
-import sys
-import os
-from distutils.errors import (
- DistutilsExecError,
- DistutilsPlatformError,
- CompileError,
- LibError,
- LinkError,
-)
-from distutils.ccompiler import CCompiler, gen_lib_options
-from distutils import log
-
-_can_read_reg = False
-try:
- import winreg
-
- _can_read_reg = True
- hkey_mod = winreg
-
- RegOpenKeyEx = winreg.OpenKeyEx
- RegEnumKey = winreg.EnumKey
- RegEnumValue = winreg.EnumValue
- RegError = winreg.error
-
-except ImportError:
- try:
- import win32api
- import win32con
-
- _can_read_reg = True
- hkey_mod = win32con
-
- RegOpenKeyEx = win32api.RegOpenKeyEx
- RegEnumKey = win32api.RegEnumKey
- RegEnumValue = win32api.RegEnumValue
- RegError = win32api.error
- except ImportError:
- log.info(
- "Warning: Can't read registry to find the "
- "necessary compiler setting\n"
- "Make sure that Python modules winreg, "
- "win32api or win32con are installed."
- )
- pass
-
-if _can_read_reg:
- HKEYS = (
- hkey_mod.HKEY_USERS,
- hkey_mod.HKEY_CURRENT_USER,
- hkey_mod.HKEY_LOCAL_MACHINE,
- hkey_mod.HKEY_CLASSES_ROOT,
- )
-
-
-def read_keys(base, key):
- """Return list of registry keys."""
- try:
- handle = RegOpenKeyEx(base, key)
- except RegError:
- return None
- L = []
- i = 0
- while True:
- try:
- k = RegEnumKey(handle, i)
- except RegError:
- break
- L.append(k)
- i += 1
- return L
-
-
-def read_values(base, key):
- """Return dict of registry keys and values.
-
- All names are converted to lowercase.
- """
- try:
- handle = RegOpenKeyEx(base, key)
- except RegError:
- return None
- d = {}
- i = 0
- while True:
- try:
- name, value, type = RegEnumValue(handle, i)
- except RegError:
- break
- name = name.lower()
- d[convert_mbcs(name)] = convert_mbcs(value)
- i += 1
- return d
-
-
-def convert_mbcs(s):
- dec = getattr(s, "decode", None)
- if dec is not None:
- try:
- s = dec("mbcs")
- except UnicodeError:
- pass
- return s
-
-
-class MacroExpander:
- def __init__(self, version):
- self.macros = {}
- self.load_macros(version)
-
- def set_macro(self, macro, path, key):
- for base in HKEYS:
- d = read_values(base, path)
- if d:
- self.macros["$(%s)" % macro] = d[key]
- break
-
- def load_macros(self, version):
- vsbase = r"Software\Microsoft\VisualStudio\%0.1f" % version
- self.set_macro("VCInstallDir", vsbase + r"\Setup\VC", "productdir")
- self.set_macro("VSInstallDir", vsbase + r"\Setup\VS", "productdir")
- net = r"Software\Microsoft\.NETFramework"
- self.set_macro("FrameworkDir", net, "installroot")
- try:
- if version > 7.0:
- self.set_macro("FrameworkSDKDir", net, "sdkinstallrootv1.1")
- else:
- self.set_macro("FrameworkSDKDir", net, "sdkinstallroot")
- except KeyError:
- raise DistutilsPlatformError(
- """Python was built with Visual Studio 2003;
-extensions must be built with a compiler than can generate compatible binaries.
-Visual Studio 2003 was not found on this system. If you have Cygwin installed,
-you can try compiling with MingW32, by passing "-c mingw32" to setup.py."""
- )
-
- p = r"Software\Microsoft\NET Framework Setup\Product"
- for base in HKEYS:
- try:
- h = RegOpenKeyEx(base, p)
- except RegError:
- continue
- key = RegEnumKey(h, 0)
- d = read_values(base, r"%s\%s" % (p, key))
- self.macros["$(FrameworkVersion)"] = d["version"]
-
- def sub(self, s):
- for k, v in self.macros.items():
- s = s.replace(k, v)
- return s
-
-
-def get_build_version():
- """Return the version of MSVC that was used to build Python.
-
- For Python 2.3 and up, the version number is included in
- sys.version. For earlier versions, assume the compiler is MSVC 6.
- """
- prefix = "MSC v."
- i = sys.version.find(prefix)
- if i == -1:
- return 6
- i = i + len(prefix)
- s, rest = sys.version[i:].split(" ", 1)
- majorVersion = int(s[:-2]) - 6
- if majorVersion >= 13:
- # v13 was skipped and should be v14
- majorVersion += 1
- minorVersion = int(s[2:3]) / 10.0
- # I don't think paths are affected by minor version in version 6
- if majorVersion == 6:
- minorVersion = 0
- if majorVersion >= 6:
- return majorVersion + minorVersion
- # else we don't know what version of the compiler this is
- return None
-
-
-def get_build_architecture():
- """Return the processor architecture.
-
- Possible results are "Intel" or "AMD64".
- """
-
- prefix = " bit ("
- i = sys.version.find(prefix)
- if i == -1:
- return "Intel"
- j = sys.version.find(")", i)
- return sys.version[i + len(prefix) : j]
-
-
-def normalize_and_reduce_paths(paths):
- """Return a list of normalized paths with duplicates removed.
-
- The current order of paths is maintained.
- """
- # Paths are normalized so things like: /a and /a/ aren't both preserved.
- reduced_paths = []
- for p in paths:
- np = os.path.normpath(p)
- # XXX(nnorwitz): O(n**2), if reduced_paths gets long perhaps use a set.
- if np not in reduced_paths:
- reduced_paths.append(np)
- return reduced_paths
-
-
-class MSVCCompiler(CCompiler):
- """Concrete class that implements an interface to Microsoft Visual C++,
- as defined by the CCompiler abstract class."""
-
- compiler_type = 'msvc'
-
- # Just set this so CCompiler's constructor doesn't barf. We currently
- # don't use the 'set_executables()' bureaucracy provided by CCompiler,
- # as it really isn't necessary for this sort of single-compiler class.
- # Would be nice to have a consistent interface with UnixCCompiler,
- # though, so it's worth thinking about.
- executables = {}
-
- # Private class data (need to distinguish C from C++ source for compiler)
- _c_extensions = ['.c']
- _cpp_extensions = ['.cc', '.cpp', '.cxx']
- _rc_extensions = ['.rc']
- _mc_extensions = ['.mc']
-
- # Needed for the filename generation methods provided by the
- # base class, CCompiler.
- src_extensions = _c_extensions + _cpp_extensions + _rc_extensions + _mc_extensions
- res_extension = '.res'
- obj_extension = '.obj'
- static_lib_extension = '.lib'
- shared_lib_extension = '.dll'
- static_lib_format = shared_lib_format = '%s%s'
- exe_extension = '.exe'
-
- def __init__(self, verbose=0, dry_run=0, force=0):
- super().__init__(verbose, dry_run, force)
- self.__version = get_build_version()
- self.__arch = get_build_architecture()
- if self.__arch == "Intel":
- # x86
- if self.__version >= 7:
- self.__root = r"Software\Microsoft\VisualStudio"
- self.__macros = MacroExpander(self.__version)
- else:
- self.__root = r"Software\Microsoft\Devstudio"
- self.__product = "Visual Studio version %s" % self.__version
- else:
- # Win64. Assume this was built with the platform SDK
- self.__product = "Microsoft SDK compiler %s" % (self.__version + 6)
-
- self.initialized = False
-
- def initialize(self):
- self.__paths = []
- if (
- "DISTUTILS_USE_SDK" in os.environ
- and "MSSdk" in os.environ
- and self.find_exe("cl.exe")
- ):
- # Assume that the SDK set up everything alright; don't try to be
- # smarter
- self.cc = "cl.exe"
- self.linker = "link.exe"
- self.lib = "lib.exe"
- self.rc = "rc.exe"
- self.mc = "mc.exe"
- else:
- self.__paths = self.get_msvc_paths("path")
-
- if len(self.__paths) == 0:
- raise DistutilsPlatformError(
- "Python was built with %s, "
- "and extensions need to be built with the same "
- "version of the compiler, but it isn't installed." % self.__product
- )
-
- self.cc = self.find_exe("cl.exe")
- self.linker = self.find_exe("link.exe")
- self.lib = self.find_exe("lib.exe")
- self.rc = self.find_exe("rc.exe") # resource compiler
- self.mc = self.find_exe("mc.exe") # message compiler
- self.set_path_env_var('lib')
- self.set_path_env_var('include')
-
- # extend the MSVC path with the current path
- try:
- for p in os.environ['path'].split(';'):
- self.__paths.append(p)
- except KeyError:
- pass
- self.__paths = normalize_and_reduce_paths(self.__paths)
- os.environ['path'] = ";".join(self.__paths)
-
- self.preprocess_options = None
- if self.__arch == "Intel":
- self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GX', '/DNDEBUG']
- self.compile_options_debug = [
- '/nologo',
- '/Od',
- '/MDd',
- '/W3',
- '/GX',
- '/Z7',
- '/D_DEBUG',
- ]
- else:
- # Win64
- self.compile_options = ['/nologo', '/O2', '/MD', '/W3', '/GS-', '/DNDEBUG']
- self.compile_options_debug = [
- '/nologo',
- '/Od',
- '/MDd',
- '/W3',
- '/GS-',
- '/Z7',
- '/D_DEBUG',
- ]
-
- self.ldflags_shared = ['/DLL', '/nologo', '/INCREMENTAL:NO']
- if self.__version >= 7:
- self.ldflags_shared_debug = ['/DLL', '/nologo', '/INCREMENTAL:no', '/DEBUG']
- else:
- self.ldflags_shared_debug = [
- '/DLL',
- '/nologo',
- '/INCREMENTAL:no',
- '/pdb:None',
- '/DEBUG',
- ]
- self.ldflags_static = ['/nologo']
-
- self.initialized = True
-
- # -- Worker methods ------------------------------------------------
-
- def object_filenames(self, source_filenames, strip_dir=0, output_dir=''):
- # Copied from ccompiler.py, extended to return .res as 'object'-file
- # for .rc input file
- if output_dir is None:
- output_dir = ''
- obj_names = []
- for src_name in source_filenames:
- (base, ext) = os.path.splitext(src_name)
- base = os.path.splitdrive(base)[1] # Chop off the drive
- base = base[os.path.isabs(base) :] # If abs, chop off leading /
- if ext not in self.src_extensions:
- # Better to raise an exception instead of silently continuing
- # and later complain about sources and targets having
- # different lengths
- raise CompileError("Don't know how to compile %s" % src_name)
- if strip_dir:
- base = os.path.basename(base)
- if ext in self._rc_extensions:
- obj_names.append(os.path.join(output_dir, base + self.res_extension))
- elif ext in self._mc_extensions:
- obj_names.append(os.path.join(output_dir, base + self.res_extension))
- else:
- obj_names.append(os.path.join(output_dir, base + self.obj_extension))
- return obj_names
-
- def compile( # noqa: C901
- self,
- sources,
- output_dir=None,
- macros=None,
- include_dirs=None,
- debug=0,
- extra_preargs=None,
- extra_postargs=None,
- depends=None,
- ):
-
- if not self.initialized:
- self.initialize()
- compile_info = self._setup_compile(
- output_dir, macros, include_dirs, sources, depends, extra_postargs
- )
- macros, objects, extra_postargs, pp_opts, build = compile_info
-
- compile_opts = extra_preargs or []
- compile_opts.append('/c')
- if debug:
- compile_opts.extend(self.compile_options_debug)
- else:
- compile_opts.extend(self.compile_options)
-
- for obj in objects:
- try:
- src, ext = build[obj]
- except KeyError:
- continue
- if debug:
- # pass the full pathname to MSVC in debug mode,
- # this allows the debugger to find the source file
- # without asking the user to browse for it
- src = os.path.abspath(src)
-
- if ext in self._c_extensions:
- input_opt = "/Tc" + src
- elif ext in self._cpp_extensions:
- input_opt = "/Tp" + src
- elif ext in self._rc_extensions:
- # compile .RC to .RES file
- input_opt = src
- output_opt = "/fo" + obj
- try:
- self.spawn([self.rc] + pp_opts + [output_opt] + [input_opt])
- except DistutilsExecError as msg:
- raise CompileError(msg)
- continue
- elif ext in self._mc_extensions:
- # Compile .MC to .RC file to .RES file.
- # * '-h dir' specifies the directory for the
- # generated include file
- # * '-r dir' specifies the target directory of the
- # generated RC file and the binary message resource
- # it includes
- #
- # For now (since there are no options to change this),
- # we use the source-directory for the include file and
- # the build directory for the RC file and message
- # resources. This works at least for win32all.
- h_dir = os.path.dirname(src)
- rc_dir = os.path.dirname(obj)
- try:
- # first compile .MC to .RC and .H file
- self.spawn([self.mc] + ['-h', h_dir, '-r', rc_dir] + [src])
- base, _ = os.path.splitext(os.path.basename(src))
- rc_file = os.path.join(rc_dir, base + '.rc')
- # then compile .RC to .RES file
- self.spawn([self.rc] + ["/fo" + obj] + [rc_file])
-
- except DistutilsExecError as msg:
- raise CompileError(msg)
- continue
- else:
- # how to handle this file?
- raise CompileError("Don't know how to compile %s to %s" % (src, obj))
-
- output_opt = "/Fo" + obj
- try:
- self.spawn(
- [self.cc]
- + compile_opts
- + pp_opts
- + [input_opt, output_opt]
- + extra_postargs
- )
- except DistutilsExecError as msg:
- raise CompileError(msg)
-
- return objects
-
- def create_static_lib(
- self, objects, output_libname, output_dir=None, debug=0, target_lang=None
- ):
-
- if not self.initialized:
- self.initialize()
- (objects, output_dir) = self._fix_object_args(objects, output_dir)
- output_filename = self.library_filename(output_libname, output_dir=output_dir)
-
- if self._need_link(objects, output_filename):
- lib_args = objects + ['/OUT:' + output_filename]
- if debug:
- pass # XXX what goes here?
- try:
- self.spawn([self.lib] + lib_args)
- except DistutilsExecError as msg:
- raise LibError(msg)
- else:
- log.debug("skipping %s (up-to-date)", output_filename)
-
- def link( # noqa: C901
- self,
- target_desc,
- objects,
- output_filename,
- output_dir=None,
- libraries=None,
- library_dirs=None,
- runtime_library_dirs=None,
- export_symbols=None,
- debug=0,
- extra_preargs=None,
- extra_postargs=None,
- build_temp=None,
- target_lang=None,
- ):
-
- if not self.initialized:
- self.initialize()
- (objects, output_dir) = self._fix_object_args(objects, output_dir)
- fixed_args = self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
- (libraries, library_dirs, runtime_library_dirs) = fixed_args
-
- if runtime_library_dirs:
- self.warn(
- "I don't know what to do with 'runtime_library_dirs': "
- + str(runtime_library_dirs)
- )
-
- lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs, libraries)
- if output_dir is not None:
- output_filename = os.path.join(output_dir, output_filename)
-
- if self._need_link(objects, output_filename):
- if target_desc == CCompiler.EXECUTABLE:
- if debug:
- ldflags = self.ldflags_shared_debug[1:]
- else:
- ldflags = self.ldflags_shared[1:]
- else:
- if debug:
- ldflags = self.ldflags_shared_debug
- else:
- ldflags = self.ldflags_shared
-
- export_opts = []
- for sym in export_symbols or []:
- export_opts.append("/EXPORT:" + sym)
-
- ld_args = (
- ldflags + lib_opts + export_opts + objects + ['/OUT:' + output_filename]
- )
-
- # The MSVC linker generates .lib and .exp files, which cannot be
- # suppressed by any linker switches. The .lib files may even be
- # needed! Make sure they are generated in the temporary build
- # directory. Since they have different names for debug and release
- # builds, they can go into the same directory.
- if export_symbols is not None:
- (dll_name, dll_ext) = os.path.splitext(
- os.path.basename(output_filename)
- )
- implib_file = os.path.join(
- os.path.dirname(objects[0]), self.library_filename(dll_name)
- )
- ld_args.append('/IMPLIB:' + implib_file)
-
- if extra_preargs:
- ld_args[:0] = extra_preargs
- if extra_postargs:
- ld_args.extend(extra_postargs)
-
- self.mkpath(os.path.dirname(output_filename))
- try:
- self.spawn([self.linker] + ld_args)
- except DistutilsExecError as msg:
- raise LinkError(msg)
-
- else:
- log.debug("skipping %s (up-to-date)", output_filename)
-
- # -- Miscellaneous methods -----------------------------------------
- # These are all used by the 'gen_lib_options() function, in
- # ccompiler.py.
-
- def library_dir_option(self, dir):
- return "/LIBPATH:" + dir
-
- def runtime_library_dir_option(self, dir):
- raise DistutilsPlatformError(
- "don't know how to set runtime library search path for MSVC++"
- )
-
- def library_option(self, lib):
- return self.library_filename(lib)
-
- def find_library_file(self, dirs, lib, debug=0):
- # Prefer a debugging library if found (and requested), but deal
- # with it if we don't have one.
- if debug:
- try_names = [lib + "_d", lib]
- else:
- try_names = [lib]
- for dir in dirs:
- for name in try_names:
- libfile = os.path.join(dir, self.library_filename(name))
- if os.path.exists(libfile):
- return libfile
- else:
- # Oops, didn't find it in *any* of 'dirs'
- return None
-
- # Helper methods for using the MSVC registry settings
-
- def find_exe(self, exe):
- """Return path to an MSVC executable program.
-
- Tries to find the program in several places: first, one of the
- MSVC program search paths from the registry; next, the directories
- in the PATH environment variable. If any of those work, return an
- absolute path that is known to exist. If none of them work, just
- return the original program name, 'exe'.
- """
- for p in self.__paths:
- fn = os.path.join(os.path.abspath(p), exe)
- if os.path.isfile(fn):
- return fn
-
- # didn't find it; try existing path
- for p in os.environ['Path'].split(';'):
- fn = os.path.join(os.path.abspath(p), exe)
- if os.path.isfile(fn):
- return fn
-
- return exe
-
- def get_msvc_paths(self, path, platform='x86'):
- """Get a list of devstudio directories (include, lib or path).
-
- Return a list of strings. The list will be empty if unable to
- access the registry or appropriate registry keys not found.
- """
- if not _can_read_reg:
- return []
-
- path = path + " dirs"
- if self.__version >= 7:
- key = r"%s\%0.1f\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories" % (
- self.__root,
- self.__version,
- )
- else:
- key = (
- r"%s\6.0\Build System\Components\Platforms"
- r"\Win32 (%s)\Directories" % (self.__root, platform)
- )
-
- for base in HKEYS:
- d = read_values(base, key)
- if d:
- if self.__version >= 7:
- return self.__macros.sub(d[path]).split(";")
- else:
- return d[path].split(";")
- # MSVC 6 seems to create the registry entries we need only when
- # the GUI is run.
- if self.__version == 6:
- for base in HKEYS:
- if read_values(base, r"%s\6.0" % self.__root) is not None:
- self.warn(
- "It seems you have Visual Studio 6 installed, "
- "but the expected registry settings are not present.\n"
- "You must at least run the Visual Studio GUI once "
- "so that these entries are created."
- )
- break
- return []
-
- def set_path_env_var(self, name):
- """Set environment variable 'name' to an MSVC path type value.
-
- This is equivalent to a SET command prior to execution of spawned
- commands.
- """
-
- if name == "lib":
- p = self.get_msvc_paths("library")
- else:
- p = self.get_msvc_paths(name)
- if p:
- os.environ[name] = ';'.join(p)
-
-
-if get_build_version() >= 8.0:
- log.debug("Importing new compiler from distutils.msvc9compiler")
- OldMSVCCompiler = MSVCCompiler
- from distutils.msvc9compiler import MSVCCompiler
-
- # get_build_architecture not really relevant now we support cross-compile
- from distutils.msvc9compiler import MacroExpander # noqa: F811
diff --git a/distutils/py38compat.py b/distutils/py38compat.py
index e556b69e..59224e71 100644
--- a/distutils/py38compat.py
+++ b/distutils/py38compat.py
@@ -5,4 +5,4 @@ def aix_platform(osname, version, release):
return _aix_support.aix_platform()
except ImportError:
pass
- return "%s-%s.%s" % (osname, version, release)
+ return "{}-{}.{}".format(osname, version, release)
diff --git a/distutils/spawn.py b/distutils/spawn.py
index db9f08ee..b18ba9db 100644
--- a/distutils/spawn.py
+++ b/distutils/spawn.py
@@ -60,13 +60,15 @@ def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None): # noqa: C901
except OSError as exc:
if not DEBUG:
cmd = cmd[0]
- raise DistutilsExecError("command %r failed: %s" % (cmd, exc.args[-1])) from exc
+ raise DistutilsExecError(
+ "command {!r} failed: {}".format(cmd, exc.args[-1])
+ ) from exc
if exitcode:
if not DEBUG:
cmd = cmd[0]
raise DistutilsExecError(
- "command %r failed with exit code %s" % (cmd, exitcode)
+ "command {!r} failed with exit code {}".format(cmd, exitcode)
)
diff --git a/distutils/tests/py38compat.py b/distutils/tests/py38compat.py
index 96f93a31..35ddbb5b 100644
--- a/distutils/tests/py38compat.py
+++ b/distutils/tests/py38compat.py
@@ -42,5 +42,17 @@ except (ModuleNotFoundError, ImportError):
)
+try:
+ from test.support.import_helper import (
+ DirsOnSysPath,
+ CleanImport,
+ )
+except (ModuleNotFoundError, ImportError):
+ from test.support import (
+ DirsOnSysPath,
+ CleanImport,
+ )
+
+
if sys.version_info < (3, 9):
requires_zlib = lambda: test.support.requires_zlib
diff --git a/distutils/tests/support.py b/distutils/tests/support.py
index 2e9d66b7..5203ed19 100644
--- a/distutils/tests/support.py
+++ b/distutils/tests/support.py
@@ -3,32 +3,17 @@ import os
import sys
import shutil
import tempfile
-import unittest
import sysconfig
+import itertools
-from . import py38compat as os_helper
+import pytest
-from distutils import log
from distutils.log import DEBUG, INFO, WARN, ERROR, FATAL
from distutils.core import Distribution
-class LoggingSilencer(object):
- def setUp(self):
- super().setUp()
- self.threshold = log.set_threshold(log.FATAL)
- # catching warnings
- # when log will be replaced by logging
- # we won't need such monkey-patch anymore
- self._old_log = log.Log._log
- log.Log._log = self._log
- self.logs = []
-
- def tearDown(self):
- log.set_threshold(self.threshold)
- log.Log._log = self._old_log
- super().tearDown()
-
+@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))
@@ -43,25 +28,11 @@ class LoggingSilencer(object):
self.logs = []
-class TempdirManager(object):
- """Mix-in class that handles temporary directories for test cases.
-
- This is intended to be used with unittest.TestCase.
+@pytest.mark.usefixtures('distutils_managed_tempdir')
+class TempdirManager:
+ """
+ Mix-in class that handles temporary directories for test cases.
"""
-
- def setUp(self):
- super().setUp()
- self.old_cwd = os.getcwd()
- self.tempdirs = []
-
- def tearDown(self):
- # Restore working dir, for Solaris and derivatives, where rmdir()
- # on the current directory fails.
- os.chdir(self.old_cwd)
- super().tearDown()
- while self.tempdirs:
- tmpdir = self.tempdirs.pop()
- os_helper.rmtree(tmpdir)
def mkdtemp(self):
"""Create a temporary directory that will be cleaned up.
@@ -108,8 +79,7 @@ class DummyCommand:
"""Class to store options for retrieval via set_undefined_options()."""
def __init__(self, **kwargs):
- for kw, val in kwargs.items():
- setattr(self, kw, val)
+ vars(self).update(kwargs)
def ensure_finalized(self):
pass
@@ -127,29 +97,12 @@ def copy_xxmodule_c(directory):
If the source file can be found, it will be copied to *directory*. If not,
the test will be skipped. Errors during copy are not caught.
"""
- filename = _get_xxmodule_path()
- if filename is None:
- raise unittest.SkipTest(
- 'cannot find xxmodule.c (test must run in ' 'the python build dir)'
- )
- shutil.copy(filename, directory)
+ shutil.copy(_get_xxmodule_path(), os.path.join(directory, 'xxmodule.c'))
def _get_xxmodule_path():
- srcdir = sysconfig.get_config_var('srcdir')
- candidates = [
- # use installed copy if available
- os.path.join(os.path.dirname(__file__), 'xxmodule.c'),
- # otherwise try using copy from build directory
- os.path.join(srcdir, 'Modules', 'xxmodule.c'),
- # srcdir mysteriously can be $srcdir/Lib/distutils/tests when
- # this file is run from its parent directory, so walk up the
- # tree to find the real srcdir
- os.path.join(srcdir, '..', '..', '..', 'Modules', 'xxmodule.c'),
- ]
- for path in candidates:
- if os.path.exists(path):
- return path
+ source_name = 'xxmodule.c' if sys.version_info > (3, 9) else 'xxmodule-3.8.c'
+ return os.path.join(os.path.dirname(__file__), source_name)
def fixup_build_ext(cmd):
@@ -187,3 +140,17 @@ def fixup_build_ext(cmd):
else:
name, equals, value = runshared.partition('=')
cmd.library_dirs = [d for d in value.split(os.pathsep) if d]
+
+
+def combine_markers(cls):
+ """
+ pytest will honor markers as found on the class, but when
+ markers are on multiple subclasses, only one appears. Use
+ this decorator to combine those markers.
+ """
+ cls.pytestmark = [
+ mark
+ for base in itertools.chain([cls], cls.__bases__)
+ for mark in getattr(base, 'pytestmark', [])
+ ]
+ return cls
diff --git a/distutils/tests/test_archive_util.py b/distutils/tests/test_archive_util.py
index 7a324c45..72aa9d7c 100644
--- a/distutils/tests/test_archive_util.py
+++ b/distutils/tests/test_archive_util.py
@@ -1,11 +1,12 @@
-# -*- coding: utf-8 -*-
"""Tests for distutils.archive_util."""
-import unittest
import os
import sys
import tarfile
from os.path import splitdrive
import warnings
+import functools
+import operator
+import pathlib
import pytest
@@ -17,7 +18,7 @@ from distutils.archive_util import (
make_archive,
ARCHIVE_FORMATS,
)
-from distutils.spawn import find_executable, spawn
+from distutils.spawn import spawn
from distutils.tests import support
from test.support import patch
from .unix_compat import require_unix_id, require_uid_0, grp, pwd, UID_0_SUPPORT
@@ -26,24 +27,6 @@ from .py38compat import change_cwd
from .py38compat import check_warnings
-try:
- import zipfile
-
- ZIP_SUPPORT = True
-except ImportError:
- ZIP_SUPPORT = find_executable('zip')
-
-try:
- import bz2
-except ImportError:
- bz2 = None
-
-try:
- import lzma
-except ImportError:
- lzma = None
-
-
def can_fs_encode(filename):
"""
Return True if the filename can be saved in the file system.
@@ -57,9 +40,15 @@ def can_fs_encode(filename):
return True
-class ArchiveUtilTestCase(
- support.TempdirManager, support.LoggingSilencer, unittest.TestCase
-):
+def all_equal(values):
+ return functools.reduce(operator.eq, values)
+
+
+def same_drive(*paths):
+ return all_equal(pathlib.Path(path).drive for path in paths)
+
+
+class ArchiveUtilTestCase(support.TempdirManager, support.LoggingSilencer):
@pytest.mark.usefixtures('needs_zlib')
def test_make_tarball(self, name='archive'):
# creating something to tar
@@ -73,28 +62,24 @@ class ArchiveUtilTestCase(
tmpdir = self._create_files()
self._make_tarball(tmpdir, 'archive', '.tar.gz', compress='gzip')
- @unittest.skipUnless(bz2, 'Need bz2 support to run')
def test_make_tarball_bzip2(self):
+ pytest.importorskip('bz2')
tmpdir = self._create_files()
self._make_tarball(tmpdir, 'archive', '.tar.bz2', compress='bzip2')
- @unittest.skipUnless(lzma, 'Need lzma support to run')
def test_make_tarball_xz(self):
+ pytest.importorskip('lzma')
tmpdir = self._create_files()
self._make_tarball(tmpdir, 'archive', '.tar.xz', compress='xz')
- @unittest.skipUnless(
- can_fs_encode('årchiv'), 'File system cannot handle this filename'
- )
+ @pytest.mark.skipif("not can_fs_encode('årchiv')")
def test_make_tarball_latin1(self):
"""
Mirror test_make_tarball, except filename contains latin characters.
"""
self.test_make_tarball('årchiv') # note this isn't a real word
- @unittest.skipUnless(
- can_fs_encode('のアーカイブ'), 'File system cannot handle this filename'
- )
+ @pytest.mark.skipif("not can_fs_encode('のアーカイブ')")
def test_make_tarball_extended(self):
"""
Mirror test_make_tarball, except filename contains extended
@@ -104,10 +89,8 @@ class ArchiveUtilTestCase(
def _make_tarball(self, tmpdir, target_name, suffix, **kwargs):
tmpdir2 = self.mkdtemp()
- unittest.skipUnless(
- splitdrive(tmpdir)[0] == splitdrive(tmpdir2)[0],
- "source and target should be on same drive",
- )
+ if same_drive(tmpdir, tmpdir2):
+ pytest.skip("source and target should be on same drive")
base_name = os.path.join(tmpdir2, target_name)
@@ -117,8 +100,8 @@ class ArchiveUtilTestCase(
# check if the compressed tarball was created
tarball = base_name + suffix
- self.assertTrue(os.path.exists(tarball))
- self.assertEqual(self._tarinfo(tarball), self._created_files)
+ assert os.path.exists(tarball)
+ assert self._tarinfo(tarball) == self._created_files
def _tarinfo(self, path):
tar = tarfile.open(path)
@@ -152,10 +135,7 @@ class ArchiveUtilTestCase(
return tmpdir
@pytest.mark.usefixtures('needs_zlib')
- @unittest.skipUnless(
- find_executable('tar') and find_executable('gzip'),
- 'Need the tar and gzip commands to run',
- )
+ @pytest.mark.skipif("not (find_executable('tar') and find_executable('gzip'))")
def test_tarfile_vs_tar(self):
tmpdir = self._create_files()
tmpdir2 = self.mkdtemp()
@@ -169,7 +149,7 @@ class ArchiveUtilTestCase(
# check if the compressed tarball was created
tarball = base_name + '.tar.gz'
- self.assertTrue(os.path.exists(tarball))
+ assert os.path.exists(tarball)
# now create another tarball using `tar`
tarball2 = os.path.join(tmpdir, 'archive2.tar.gz')
@@ -183,10 +163,10 @@ class ArchiveUtilTestCase(
finally:
os.chdir(old_dir)
- self.assertTrue(os.path.exists(tarball2))
+ assert os.path.exists(tarball2)
# let's compare both tarballs
- self.assertEqual(self._tarinfo(tarball), self._created_files)
- self.assertEqual(self._tarinfo(tarball2), self._created_files)
+ assert self._tarinfo(tarball) == self._created_files
+ assert self._tarinfo(tarball2) == self._created_files
# trying an uncompressed one
base_name = os.path.join(tmpdir2, 'archive')
@@ -197,7 +177,7 @@ class ArchiveUtilTestCase(
finally:
os.chdir(old_dir)
tarball = base_name + '.tar'
- self.assertTrue(os.path.exists(tarball))
+ assert os.path.exists(tarball)
# now for a dry_run
base_name = os.path.join(tmpdir2, 'archive')
@@ -208,16 +188,14 @@ class ArchiveUtilTestCase(
finally:
os.chdir(old_dir)
tarball = base_name + '.tar'
- self.assertTrue(os.path.exists(tarball))
+ assert os.path.exists(tarball)
- @unittest.skipUnless(
- find_executable('compress'), 'The compress program is required'
- )
+ @pytest.mark.skipif("not find_executable('compress')")
def test_compress_deprecated(self):
tmpdir = self._create_files()
base_name = os.path.join(self.mkdtemp(), 'archive')
- # using compress and testing the PendingDeprecationWarning
+ # using compress and testing the DeprecationWarning
old_dir = os.getcwd()
os.chdir(tmpdir)
try:
@@ -227,8 +205,8 @@ class ArchiveUtilTestCase(
finally:
os.chdir(old_dir)
tarball = base_name + '.tar.Z'
- self.assertTrue(os.path.exists(tarball))
- self.assertEqual(len(w.warnings), 1)
+ assert os.path.exists(tarball)
+ assert len(w.warnings) == 1
# same test with dry_run
os.remove(tarball)
@@ -240,12 +218,12 @@ class ArchiveUtilTestCase(
make_tarball(base_name, 'dist', compress='compress', dry_run=True)
finally:
os.chdir(old_dir)
- self.assertFalse(os.path.exists(tarball))
- self.assertEqual(len(w.warnings), 1)
+ assert not os.path.exists(tarball)
+ assert len(w.warnings) == 1
@pytest.mark.usefixtures('needs_zlib')
- @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run')
def test_make_zipfile(self):
+ zipfile = pytest.importorskip('zipfile')
# creating something to tar
tmpdir = self._create_files()
base_name = os.path.join(self.mkdtemp(), 'archive')
@@ -254,12 +232,12 @@ class ArchiveUtilTestCase(
# check if the compressed tarball was created
tarball = base_name + '.zip'
- self.assertTrue(os.path.exists(tarball))
+ assert os.path.exists(tarball)
with zipfile.ZipFile(tarball) as zf:
- self.assertEqual(sorted(zf.namelist()), self._zip_created_files)
+ assert sorted(zf.namelist()) == self._zip_created_files
- @unittest.skipUnless(ZIP_SUPPORT, 'Need zip support to run')
def test_make_zipfile_no_zlib(self):
+ zipfile = pytest.importorskip('zipfile')
patch(self, archive_util.zipfile, 'zlib', None) # force zlib ImportError
called = []
@@ -279,23 +257,23 @@ class ArchiveUtilTestCase(
make_zipfile(base_name, 'dist')
tarball = base_name + '.zip'
- self.assertEqual(
- called, [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]
- )
- self.assertTrue(os.path.exists(tarball))
+ assert called == [((tarball, "w"), {'compression': zipfile.ZIP_STORED})]
+ assert os.path.exists(tarball)
with zipfile.ZipFile(tarball) as zf:
- self.assertEqual(sorted(zf.namelist()), self._zip_created_files)
+ assert sorted(zf.namelist()) == self._zip_created_files
def test_check_archive_formats(self):
- self.assertEqual(check_archive_formats(['gztar', 'xxx', 'zip']), 'xxx')
- self.assertIsNone(
+ assert check_archive_formats(['gztar', 'xxx', 'zip']) == 'xxx'
+ assert (
check_archive_formats(['gztar', 'bztar', 'xztar', 'ztar', 'tar', 'zip'])
+ is None
)
def test_make_archive(self):
tmpdir = self.mkdtemp()
base_name = os.path.join(tmpdir, 'archive')
- self.assertRaises(ValueError, make_archive, base_name, 'xxx')
+ with pytest.raises(ValueError):
+ make_archive(base_name, 'xxx')
def test_make_archive_cwd(self):
current_dir = os.getcwd()
@@ -309,7 +287,7 @@ class ArchiveUtilTestCase(
make_archive('xxx', 'xxx', root_dir=self.mkdtemp())
except Exception:
pass
- self.assertEqual(os.getcwd(), current_dir)
+ assert os.getcwd() == current_dir
finally:
del ARCHIVE_FORMATS['xxx']
@@ -317,36 +295,36 @@ class ArchiveUtilTestCase(
base_dir = self._create_files()
base_name = os.path.join(self.mkdtemp(), 'archive')
res = make_archive(base_name, 'tar', base_dir, 'dist')
- self.assertTrue(os.path.exists(res))
- self.assertEqual(os.path.basename(res), 'archive.tar')
- self.assertEqual(self._tarinfo(res), self._created_files)
+ assert os.path.exists(res)
+ assert os.path.basename(res) == 'archive.tar'
+ assert self._tarinfo(res) == self._created_files
@pytest.mark.usefixtures('needs_zlib')
def test_make_archive_gztar(self):
base_dir = self._create_files()
base_name = os.path.join(self.mkdtemp(), 'archive')
res = make_archive(base_name, 'gztar', base_dir, 'dist')
- self.assertTrue(os.path.exists(res))
- self.assertEqual(os.path.basename(res), 'archive.tar.gz')
- self.assertEqual(self._tarinfo(res), self._created_files)
+ assert os.path.exists(res)
+ assert os.path.basename(res) == 'archive.tar.gz'
+ assert self._tarinfo(res) == self._created_files
- @unittest.skipUnless(bz2, 'Need bz2 support to run')
def test_make_archive_bztar(self):
+ pytest.importorskip('bz2')
base_dir = self._create_files()
base_name = os.path.join(self.mkdtemp(), 'archive')
res = make_archive(base_name, 'bztar', base_dir, 'dist')
- self.assertTrue(os.path.exists(res))
- self.assertEqual(os.path.basename(res), 'archive.tar.bz2')
- self.assertEqual(self._tarinfo(res), self._created_files)
+ assert os.path.exists(res)
+ assert os.path.basename(res) == 'archive.tar.bz2'
+ assert self._tarinfo(res) == self._created_files
- @unittest.skipUnless(lzma, 'Need xz support to run')
def test_make_archive_xztar(self):
+ pytest.importorskip('lzma')
base_dir = self._create_files()
base_name = os.path.join(self.mkdtemp(), 'archive')
res = make_archive(base_name, 'xztar', base_dir, 'dist')
- self.assertTrue(os.path.exists(res))
- self.assertEqual(os.path.basename(res), 'archive.tar.xz')
- self.assertEqual(self._tarinfo(res), self._created_files)
+ assert os.path.exists(res)
+ assert os.path.basename(res) == 'archive.tar.xz'
+ assert self._tarinfo(res) == self._created_files
def test_make_archive_owner_group(self):
# testing make_archive with owner and group, with various combinations
@@ -363,20 +341,20 @@ class ArchiveUtilTestCase(
res = make_archive(
base_name, 'zip', root_dir, base_dir, owner=owner, group=group
)
- self.assertTrue(os.path.exists(res))
+ assert os.path.exists(res)
res = make_archive(base_name, 'zip', root_dir, base_dir)
- self.assertTrue(os.path.exists(res))
+ assert os.path.exists(res)
res = make_archive(
base_name, 'tar', root_dir, base_dir, owner=owner, group=group
)
- self.assertTrue(os.path.exists(res))
+ assert os.path.exists(res)
res = make_archive(
base_name, 'tar', root_dir, base_dir, owner='kjhkjhkjg', group='oihohoh'
)
- self.assertTrue(os.path.exists(res))
+ assert os.path.exists(res)
@pytest.mark.usefixtures('needs_zlib')
@require_unix_id
@@ -396,13 +374,13 @@ class ArchiveUtilTestCase(
os.chdir(old_dir)
# check if the compressed tarball was created
- self.assertTrue(os.path.exists(archive_name))
+ assert os.path.exists(archive_name)
# now checks the rights
archive = tarfile.open(archive_name)
try:
for member in archive.getmembers():
- self.assertEqual(member.uid, 0)
- self.assertEqual(member.gid, 0)
+ assert member.uid == 0
+ assert member.gid == 0
finally:
archive.close()
diff --git a/distutils/tests/test_bdist.py b/distutils/tests/test_bdist.py
index 56976c23..8db72efc 100644
--- a/distutils/tests/test_bdist.py
+++ b/distutils/tests/test_bdist.py
@@ -1,13 +1,12 @@
"""Tests for distutils.command.bdist."""
import os
-import unittest
import warnings
from distutils.command.bdist import bdist
from distutils.tests import support
-class BuildTestCase(support.TempdirManager, unittest.TestCase):
+class TestBuild(support.TempdirManager):
def test_formats(self):
# let's create a command and make sure
# we can set the format
@@ -15,7 +14,7 @@ class BuildTestCase(support.TempdirManager, unittest.TestCase):
cmd = bdist(dist)
cmd.formats = ['msi']
cmd.ensure_finalized()
- self.assertEqual(cmd.formats, ['msi'])
+ assert cmd.formats == ['msi']
# what formats does bdist offer?
formats = [
@@ -23,13 +22,12 @@ class BuildTestCase(support.TempdirManager, unittest.TestCase):
'gztar',
'rpm',
'tar',
- 'wininst',
'xztar',
'zip',
'ztar',
]
- found = sorted(cmd.format_command)
- self.assertEqual(found, formats)
+ found = sorted(cmd.format_commands)
+ assert found == formats
def test_skip_build(self):
# bug #10946: bdist --skip-build should trickle down to subcommands
@@ -41,7 +39,6 @@ class BuildTestCase(support.TempdirManager, unittest.TestCase):
names = [
'bdist_dumb',
- 'bdist_wininst',
] # bdist_rpm does not support --skip-build
for name in names:
@@ -53,6 +50,4 @@ class BuildTestCase(support.TempdirManager, unittest.TestCase):
if getattr(subcmd, '_unsupported', False):
# command is not supported on this build
continue
- self.assertTrue(
- subcmd.skip_build, '%s should take --skip-build from bdist' % name
- )
+ assert subcmd.skip_build, '%s should take --skip-build from bdist' % name
diff --git a/distutils/tests/test_bdist_dumb.py b/distutils/tests/test_bdist_dumb.py
index 7c4d5964..8624a429 100644
--- a/distutils/tests/test_bdist_dumb.py
+++ b/distutils/tests/test_bdist_dumb.py
@@ -3,7 +3,6 @@
import os
import sys
import zipfile
-import unittest
import pytest
@@ -21,23 +20,14 @@ setup(name='foo', version='0.1', py_modules=['foo'],
"""
+@support.combine_markers
@pytest.mark.usefixtures('save_env')
-class BuildDumbTestCase(
+@pytest.mark.usefixtures('save_argv')
+@pytest.mark.usefixtures('save_cwd')
+class TestBuildDumb(
support.TempdirManager,
support.LoggingSilencer,
- unittest.TestCase,
):
- def setUp(self):
- super(BuildDumbTestCase, self).setUp()
- self.old_location = os.getcwd()
- self.old_sys_argv = sys.argv, sys.argv[:]
-
- def tearDown(self):
- os.chdir(self.old_location)
- sys.argv = self.old_sys_argv[0]
- sys.argv[:] = self.old_sys_argv[1]
- super(BuildDumbTestCase, self).tearDown()
-
@pytest.mark.usefixtures('needs_zlib')
def test_simple_built(self):
@@ -75,9 +65,9 @@ class BuildDumbTestCase(
# see what we have
dist_created = os.listdir(os.path.join(pkg_dir, 'dist'))
- base = "%s.%s.zip" % (dist.get_fullname(), cmd.plat_name)
+ base = "{}.{}.zip".format(dist.get_fullname(), cmd.plat_name)
- self.assertEqual(dist_created, [base])
+ assert dist_created == [base]
# now let's check what we have in the zip file
fp = zipfile.ZipFile(os.path.join('dist', base))
@@ -90,4 +80,4 @@ class BuildDumbTestCase(
wanted = ['foo-0.1-py%s.%s.egg-info' % sys.version_info[:2], 'foo.py']
if not sys.dont_write_bytecode:
wanted.append('foo.%s.pyc' % sys.implementation.cache_tag)
- self.assertEqual(contents, sorted(wanted))
+ assert contents == sorted(wanted)
diff --git a/distutils/tests/test_bdist_rpm.py b/distutils/tests/test_bdist_rpm.py
index ba809392..2d14bafc 100644
--- a/distutils/tests/test_bdist_rpm.py
+++ b/distutils/tests/test_bdist_rpm.py
@@ -1,6 +1,5 @@
"""Tests for distutils.command.bdist_rpm."""
-import unittest
import sys
import os
@@ -9,7 +8,7 @@ import pytest
from distutils.core import Distribution
from distutils.command.bdist_rpm import bdist_rpm
from distutils.tests import support
-from distutils.spawn import find_executable
+from distutils.spawn import find_executable # noqa: F401
from .py38compat import requires_zlib
@@ -24,38 +23,31 @@ setup(name='foo', version='0.1', py_modules=['foo'],
"""
+@pytest.fixture(autouse=True)
+def sys_executable_encodable():
+ try:
+ sys.executable.encode('UTF-8')
+ except UnicodeEncodeError:
+ pytest.skip("sys.executable is not encodable to UTF-8")
+
+
+mac_woes = pytest.mark.skipif(
+ "not sys.platform.startswith('linux')",
+ reason='spurious sdtout/stderr output under macOS',
+)
+
+
@pytest.mark.usefixtures('save_env')
-class BuildRpmTestCase(
+@pytest.mark.usefixtures('save_argv')
+@pytest.mark.usefixtures('save_cwd')
+class TestBuildRpm(
support.TempdirManager,
support.LoggingSilencer,
- unittest.TestCase,
):
- def setUp(self):
- try:
- sys.executable.encode("UTF-8")
- except UnicodeEncodeError:
- raise unittest.SkipTest("sys.executable is not encodable to UTF-8")
-
- super(BuildRpmTestCase, self).setUp()
- self.old_location = os.getcwd()
- self.old_sys_argv = sys.argv, sys.argv[:]
-
- def tearDown(self):
- os.chdir(self.old_location)
- sys.argv = self.old_sys_argv[0]
- sys.argv[:] = self.old_sys_argv[1]
- super(BuildRpmTestCase, self).tearDown()
-
- # XXX I am unable yet to make this test work without
- # spurious sdtout/stderr output under Mac OS X
- @unittest.skipUnless(
- sys.platform.startswith('linux'), 'spurious sdtout/stderr output under Mac OS X'
- )
+ @mac_woes
@requires_zlib()
- @unittest.skipIf(find_executable('rpm') is None, 'the rpm command is not found')
- @unittest.skipIf(
- find_executable('rpmbuild') is None, 'the rpmbuild command is not found'
- )
+ @pytest.mark.skipif("not find_executable('rpm')")
+ @pytest.mark.skipif("not find_executable('rpmbuild')")
def test_quiet(self):
# let's create a package
tmp_dir = self.mkdtemp()
@@ -90,25 +82,17 @@ class BuildRpmTestCase(
cmd.run()
dist_created = os.listdir(os.path.join(pkg_dir, 'dist'))
- self.assertIn('foo-0.1-1.noarch.rpm', dist_created)
+ assert 'foo-0.1-1.noarch.rpm' in dist_created
# bug #2945: upload ignores bdist_rpm files
- self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files)
- self.assertIn(
- ('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files
- )
+ assert ('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm') in dist.dist_files
+ assert ('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm') in dist.dist_files
- # XXX I am unable yet to make this test work without
- # spurious sdtout/stderr output under Mac OS X
- @unittest.skipUnless(
- sys.platform.startswith('linux'), 'spurious sdtout/stderr output under Mac OS X'
- )
+ @mac_woes
@requires_zlib()
# http://bugs.python.org/issue1533164
- @unittest.skipIf(find_executable('rpm') is None, 'the rpm command is not found')
- @unittest.skipIf(
- find_executable('rpmbuild') is None, 'the rpmbuild command is not found'
- )
+ @pytest.mark.skipif("not find_executable('rpm')")
+ @pytest.mark.skipif("not find_executable('rpmbuild')")
def test_no_optimize_flag(self):
# let's create a package that breaks bdist_rpm
tmp_dir = self.mkdtemp()
@@ -142,12 +126,10 @@ class BuildRpmTestCase(
cmd.run()
dist_created = os.listdir(os.path.join(pkg_dir, 'dist'))
- self.assertIn('foo-0.1-1.noarch.rpm', dist_created)
+ assert 'foo-0.1-1.noarch.rpm' in dist_created
# bug #2945: upload ignores bdist_rpm files
- self.assertIn(('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm'), dist.dist_files)
- self.assertIn(
- ('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm'), dist.dist_files
- )
+ assert ('bdist_rpm', 'any', 'dist/foo-0.1-1.src.rpm') in dist.dist_files
+ assert ('bdist_rpm', 'any', 'dist/foo-0.1-1.noarch.rpm') in dist.dist_files
os.remove(os.path.join(pkg_dir, 'dist', 'foo-0.1-1.noarch.rpm'))
diff --git a/distutils/tests/test_bdist_wininst.py b/distutils/tests/test_bdist_wininst.py
deleted file mode 100644
index 4e4fcc5b..00000000
--- a/distutils/tests/test_bdist_wininst.py
+++ /dev/null
@@ -1,37 +0,0 @@
-"""Tests for distutils.command.bdist_wininst."""
-import sys
-import platform
-import unittest
-
-from .py38compat import check_warnings
-
-from distutils.command.bdist_wininst import bdist_wininst
-from distutils.tests import support
-
-
-@unittest.skipIf(
- sys.platform == 'win32' and platform.machine() == 'ARM64',
- 'bdist_wininst is not supported in this install',
-)
-@unittest.skipIf(
- getattr(bdist_wininst, '_unsupported', False),
- 'bdist_wininst is not supported in this install',
-)
-class BuildWinInstTestCase(
- support.TempdirManager, support.LoggingSilencer, unittest.TestCase
-):
- def test_get_exe_bytes(self):
-
- # issue5731: command was broken on non-windows platforms
- # this test makes sure it works now for every platform
- # let's create a command
- pkg_pth, dist = self.create_dist()
- with check_warnings(("", DeprecationWarning)):
- cmd = bdist_wininst(dist)
- cmd.ensure_finalized()
-
- # let's run the code that finds the right wininst*.exe file
- # and make sure it finds it and returns its content
- # no matter what platform we have
- exe_file = cmd.get_exe_bytes()
- self.assertGreater(len(exe_file), 10)
diff --git a/distutils/tests/test_build.py b/distutils/tests/test_build.py
index 712b0d53..80367607 100644
--- a/distutils/tests/test_build.py
+++ b/distutils/tests/test_build.py
@@ -1,5 +1,4 @@
"""Tests for distutils.command.build."""
-import unittest
import os
import sys
@@ -8,39 +7,39 @@ from distutils.tests import support
from sysconfig import get_platform
-class BuildTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase):
+class TestBuild(support.TempdirManager, support.LoggingSilencer):
def test_finalize_options(self):
pkg_dir, dist = self.create_dist()
cmd = build(dist)
cmd.finalize_options()
# if not specified, plat_name gets the current platform
- self.assertEqual(cmd.plat_name, get_platform())
+ assert cmd.plat_name == get_platform()
# build_purelib is build + lib
wanted = os.path.join(cmd.build_base, 'lib')
- self.assertEqual(cmd.build_purelib, wanted)
+ assert cmd.build_purelib == wanted
# build_platlib is 'build/lib.platform-cache_tag[-pydebug]'
# examples:
# build/lib.macosx-10.3-i386-cpython39
- plat_spec = '.%s-%s' % (cmd.plat_name, sys.implementation.cache_tag)
+ plat_spec = '.{}-{}'.format(cmd.plat_name, sys.implementation.cache_tag)
if hasattr(sys, 'gettotalrefcount'):
- self.assertTrue(cmd.build_platlib.endswith('-pydebug'))
+ assert cmd.build_platlib.endswith('-pydebug')
plat_spec += '-pydebug'
wanted = os.path.join(cmd.build_base, 'lib' + plat_spec)
- self.assertEqual(cmd.build_platlib, wanted)
+ assert cmd.build_platlib == wanted
# by default, build_lib = build_purelib
- self.assertEqual(cmd.build_lib, cmd.build_purelib)
+ assert cmd.build_lib == cmd.build_purelib
# build_temp is build/temp.<plat>
wanted = os.path.join(cmd.build_base, 'temp' + plat_spec)
- self.assertEqual(cmd.build_temp, wanted)
+ assert cmd.build_temp == wanted
# build_scripts is build/scripts-x.x
wanted = os.path.join(cmd.build_base, 'scripts-%d.%d' % sys.version_info[:2])
- self.assertEqual(cmd.build_scripts, wanted)
+ assert cmd.build_scripts == wanted
# executable is os.path.normpath(sys.executable)
- self.assertEqual(cmd.executable, os.path.normpath(sys.executable))
+ assert cmd.executable == os.path.normpath(sys.executable)
diff --git a/distutils/tests/test_build_clib.py b/distutils/tests/test_build_clib.py
index c8fbb5c2..c931c06e 100644
--- a/distutils/tests/test_build_clib.py
+++ b/distutils/tests/test_build_clib.py
@@ -1,47 +1,44 @@
"""Tests for distutils.command.build_clib."""
-import unittest
import os
-import sys
from test.support import missing_compiler_executable
+import pytest
+
from distutils.command.build_clib import build_clib
from distutils.errors import DistutilsSetupError
from distutils.tests import support
-class BuildCLibTestCase(
- support.TempdirManager, support.LoggingSilencer, unittest.TestCase
-):
+class TestBuildCLib(support.TempdirManager, support.LoggingSilencer):
def test_check_library_dist(self):
pkg_dir, dist = self.create_dist()
cmd = build_clib(dist)
# 'libraries' option must be a list
- self.assertRaises(DistutilsSetupError, cmd.check_library_list, 'foo')
+ with pytest.raises(DistutilsSetupError):
+ cmd.check_library_list('foo')
# each element of 'libraries' must a 2-tuple
- self.assertRaises(DistutilsSetupError, cmd.check_library_list, ['foo1', 'foo2'])
+ with pytest.raises(DistutilsSetupError):
+ cmd.check_library_list(['foo1', 'foo2'])
# first element of each tuple in 'libraries'
# must be a string (the library name)
- self.assertRaises(
- DistutilsSetupError, cmd.check_library_list, [(1, 'foo1'), ('name', 'foo2')]
- )
+ with pytest.raises(DistutilsSetupError):
+ cmd.check_library_list([(1, 'foo1'), ('name', 'foo2')])
# library name may not contain directory separators
- self.assertRaises(
- DistutilsSetupError,
- cmd.check_library_list,
- [('name', 'foo1'), ('another/name', 'foo2')],
- )
+ with pytest.raises(DistutilsSetupError):
+ cmd.check_library_list(
+ [('name', 'foo1'), ('another/name', 'foo2')],
+ )
# second element of each tuple must be a dictionary (build info)
- self.assertRaises(
- DistutilsSetupError,
- cmd.check_library_list,
- [('name', {}), ('another', 'foo2')],
- )
+ with pytest.raises(DistutilsSetupError):
+ cmd.check_library_list(
+ [('name', {}), ('another', 'foo2')],
+ )
# those work
libs = [('name', {}), ('name', {'ok': 'good'})]
@@ -54,22 +51,24 @@ class BuildCLibTestCase(
# "in 'libraries' option 'sources' must be present and must be
# a list of source filenames
cmd.libraries = [('name', {})]
- self.assertRaises(DistutilsSetupError, cmd.get_source_files)
+ with pytest.raises(DistutilsSetupError):
+ cmd.get_source_files()
cmd.libraries = [('name', {'sources': 1})]
- self.assertRaises(DistutilsSetupError, cmd.get_source_files)
+ with pytest.raises(DistutilsSetupError):
+ cmd.get_source_files()
cmd.libraries = [('name', {'sources': ['a', 'b']})]
- self.assertEqual(cmd.get_source_files(), ['a', 'b'])
+ assert cmd.get_source_files() == ['a', 'b']
cmd.libraries = [('name', {'sources': ('a', 'b')})]
- self.assertEqual(cmd.get_source_files(), ['a', 'b'])
+ assert cmd.get_source_files() == ['a', 'b']
cmd.libraries = [
('name', {'sources': ('a', 'b')}),
('name2', {'sources': ['c', 'd']}),
]
- self.assertEqual(cmd.get_source_files(), ['a', 'b', 'c', 'd'])
+ assert cmd.get_source_files() == ['a', 'b', 'c', 'd']
def test_build_libraries(self):
@@ -86,7 +85,8 @@ class BuildCLibTestCase(
# build_libraries is also doing a bit of typo checking
lib = [('name', {'sources': 'notvalid'})]
- self.assertRaises(DistutilsSetupError, cmd.build_libraries, lib)
+ with pytest.raises(DistutilsSetupError):
+ cmd.build_libraries(lib)
lib = [('name', {'sources': list()})]
cmd.build_libraries(lib)
@@ -100,16 +100,17 @@ class BuildCLibTestCase(
cmd.include_dirs = 'one-dir'
cmd.finalize_options()
- self.assertEqual(cmd.include_dirs, ['one-dir'])
+ assert cmd.include_dirs == ['one-dir']
cmd.include_dirs = None
cmd.finalize_options()
- self.assertEqual(cmd.include_dirs, [])
+ assert cmd.include_dirs == []
cmd.distribution.libraries = 'WONTWORK'
- self.assertRaises(DistutilsSetupError, cmd.finalize_options)
+ with pytest.raises(DistutilsSetupError):
+ cmd.finalize_options()
- @unittest.skipIf(sys.platform == 'win32', "can't test on Windows")
+ @pytest.mark.skipif('platform.system() == "Windows"')
def test_run(self):
pkg_dir, dist = self.create_dist()
cmd = build_clib(dist)
@@ -133,4 +134,4 @@ class BuildCLibTestCase(
cmd.run()
# let's check the result
- self.assertIn('libfoo.a', os.listdir(build_temp))
+ assert 'libfoo.a' in os.listdir(build_temp)
diff --git a/distutils/tests/test_build_ext.py b/distutils/tests/test_build_ext.py
index 39d7920a..e60814ff 100644
--- a/distutils/tests/test_build_ext.py
+++ b/distutils/tests/test_build_ext.py
@@ -2,6 +2,12 @@ import sys
import os
from io import StringIO
import textwrap
+import site
+import contextlib
+import platform
+import tempfile
+import importlib
+import shutil
from distutils.core import Distribution
from distutils.command.build_ext import build_ext
@@ -20,53 +26,70 @@ from distutils.errors import (
UnknownFileError,
)
-import unittest
from test import support
from . import py38compat as os_helper
-from test.support.script_helper import assert_python_ok
-
-# http://bugs.python.org/issue4373
-# Don't load the xx module more than once.
-ALREADY_TESTED = False
-
-
-class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
- def setUp(self):
- # Create a simple test environment
- super(BuildExtTestCase, self).setUp()
- self.tmp_dir = self.mkdtemp()
- import site
-
- self.old_user_base = site.USER_BASE
- site.USER_BASE = self.mkdtemp()
- from distutils.command import build_ext
-
- build_ext.USER_BASE = site.USER_BASE
-
- # bpo-30132: On Windows, a .pdb file may be created in the current
- # working directory. Create a temporary working directory to cleanup
- # everything at the end of the test.
- change_cwd = os_helper.change_cwd(self.tmp_dir)
- change_cwd.__enter__()
- self.addCleanup(change_cwd.__exit__, None, None, None)
-
- def tearDown(self):
- import site
-
- site.USER_BASE = self.old_user_base
- from distutils.command import build_ext
-
- build_ext.USER_BASE = self.old_user_base
- super(BuildExtTestCase, self).tearDown()
-
+from . import py38compat as import_helper
+import pytest
+import re
+
+
+@pytest.fixture()
+def user_site_dir(request):
+ self = request.instance
+ self.tmp_dir = self.mkdtemp()
+ from distutils.command import build_ext
+
+ orig_user_base = site.USER_BASE
+
+ site.USER_BASE = self.mkdtemp()
+ build_ext.USER_BASE = site.USER_BASE
+
+ # bpo-30132: On Windows, a .pdb file may be created in the current
+ # working directory. Create a temporary working directory to cleanup
+ # everything at the end of the test.
+ with os_helper.change_cwd(self.tmp_dir):
+ yield
+
+ site.USER_BASE = orig_user_base
+ build_ext.USER_BASE = orig_user_base
+
+
+@contextlib.contextmanager
+def safe_extension_import(name, path):
+ with import_helper.CleanImport(name):
+ with extension_redirect(name, path) as new_path:
+ with import_helper.DirsOnSysPath(new_path):
+ yield
+
+
+@contextlib.contextmanager
+def extension_redirect(mod, path):
+ """
+ Tests will fail to tear down an extension module if it's been imported.
+
+ Before importing, copy the file to a temporary directory that won't
+ be cleaned up. Yield the new path.
+ """
+ if platform.system() != "Windows" and sys.platform != "cygwin":
+ yield path
+ return
+ with import_helper.DirsOnSysPath(path):
+ spec = importlib.util.find_spec(mod)
+ filename = os.path.basename(spec.origin)
+ trash_dir = tempfile.mkdtemp(prefix='deleteme')
+ dest = os.path.join(trash_dir, os.path.basename(filename))
+ shutil.copy(spec.origin, dest)
+ yield trash_dir
+ # TODO: can the file be scheduled for deletion?
+
+
+@pytest.mark.usefixtures('user_site_dir')
+class TestBuildExt(TempdirManager, LoggingSilencer):
def build_ext(self, *args, **kwargs):
return build_ext(*args, **kwargs)
def test_build_ext(self):
cmd = support.missing_compiler_executable()
- if cmd is not None:
- self.skipTest('The %r command is not found' % cmd)
- global ALREADY_TESTED
copy_xxmodule_c(self.tmp_dir)
xx_c = os.path.join(self.tmp_dir, 'xxmodule.c')
xx_ext = Extension('xx', [xx_c])
@@ -87,43 +110,24 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
finally:
sys.stdout = old_stdout
- if ALREADY_TESTED:
- self.skipTest('Already tested in %s' % ALREADY_TESTED)
- else:
- ALREADY_TESTED = type(self).__name__
-
- code = textwrap.dedent(
- """
- tmp_dir = {self.tmp_dir!r}
-
- import sys
- import unittest
- from test import support
-
- sys.path.insert(0, tmp_dir)
- import xx
+ with safe_extension_import('xx', self.tmp_dir):
+ self._test_xx()
- class Tests(unittest.TestCase):
- def test_xx(self):
- for attr in ('error', 'foo', 'new', 'roj'):
- self.assertTrue(hasattr(xx, attr))
+ @staticmethod
+ def _test_xx():
+ import xx
- self.assertEqual(xx.foo(2, 5), 7)
- self.assertEqual(xx.foo(13,15), 28)
- self.assertEqual(xx.new().demo(), None)
- if support.HAVE_DOCSTRINGS:
- doc = 'This is a template module just for instruction.'
- self.assertEqual(xx.__doc__, doc)
- self.assertIsInstance(xx.Null(), xx.Null)
- self.assertIsInstance(xx.Str(), xx.Str)
+ for attr in ('error', 'foo', 'new', 'roj'):
+ assert hasattr(xx, attr)
-
- unittest.main()
- """.format(
- **locals()
- )
- )
- assert_python_ok('-c', code)
+ assert xx.foo(2, 5) == 7
+ assert xx.foo(13, 15) == 28
+ assert xx.new().demo() is None
+ if support.HAVE_DOCSTRINGS:
+ doc = 'This is a template module just for instruction.'
+ assert xx.__doc__ == doc
+ assert isinstance(xx.Null(), xx.Null)
+ assert isinstance(xx.Str(), xx.Str)
def test_solaris_enable_shared(self):
dist = Distribution({'name': 'xx'})
@@ -145,7 +149,7 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
_config_vars['Py_ENABLE_SHARED'] = old_var
# make sure we get some library dirs under solaris
- self.assertGreater(len(cmd.library_dirs), 0)
+ assert len(cmd.library_dirs) > 0
def test_user_site(self):
import site
@@ -155,7 +159,7 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
# making sure the user option is there
options = [name for name, short, lable in cmd.user_options]
- self.assertIn('user', options)
+ assert 'user' in options
# setting a value
cmd.user = 1
@@ -171,9 +175,9 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
# see if include_dirs and library_dirs
# were set
- self.assertIn(lib, cmd.library_dirs)
- self.assertIn(lib, cmd.rpath)
- self.assertIn(incl, cmd.include_dirs)
+ assert lib in cmd.library_dirs
+ assert lib in cmd.rpath
+ assert incl in cmd.include_dirs
def test_optional_extension(self):
@@ -183,9 +187,8 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
dist = Distribution({'name': 'xx', 'ext_modules': modules})
cmd = self.build_ext(dist)
cmd.ensure_finalized()
- self.assertRaises(
- (UnknownFileError, CompileError), cmd.run
- ) # should raise an error
+ with pytest.raises((UnknownFileError, CompileError)):
+ cmd.run() # should raise an error
modules = [Extension('foo', ['xxx'], optional=True)]
dist = Distribution({'name': 'xx', 'ext_modules': modules})
@@ -203,40 +206,40 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
py_include = sysconfig.get_python_inc()
for p in py_include.split(os.path.pathsep):
- self.assertIn(p, cmd.include_dirs)
+ assert p in cmd.include_dirs
plat_py_include = sysconfig.get_python_inc(plat_specific=1)
for p in plat_py_include.split(os.path.pathsep):
- self.assertIn(p, cmd.include_dirs)
+ assert p in cmd.include_dirs
# make sure cmd.libraries is turned into a list
# if it's a string
cmd = self.build_ext(dist)
cmd.libraries = 'my_lib, other_lib lastlib'
cmd.finalize_options()
- self.assertEqual(cmd.libraries, ['my_lib', 'other_lib', 'lastlib'])
+ assert cmd.libraries == ['my_lib', 'other_lib', 'lastlib']
# make sure cmd.library_dirs is turned into a list
# if it's a string
cmd = self.build_ext(dist)
cmd.library_dirs = 'my_lib_dir%sother_lib_dir' % os.pathsep
cmd.finalize_options()
- self.assertIn('my_lib_dir', cmd.library_dirs)
- self.assertIn('other_lib_dir', cmd.library_dirs)
+ assert 'my_lib_dir' in cmd.library_dirs
+ assert 'other_lib_dir' in cmd.library_dirs
# make sure rpath is turned into a list
# if it's a string
cmd = self.build_ext(dist)
cmd.rpath = 'one%stwo' % os.pathsep
cmd.finalize_options()
- self.assertEqual(cmd.rpath, ['one', 'two'])
+ assert cmd.rpath == ['one', 'two']
# make sure cmd.link_objects is turned into a list
# if it's a string
cmd = build_ext(dist)
cmd.link_objects = 'one two,three'
cmd.finalize_options()
- self.assertEqual(cmd.link_objects, ['one', 'two', 'three'])
+ assert cmd.link_objects == ['one', 'two', 'three']
# XXX more tests to perform for win32
@@ -245,25 +248,25 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
cmd = self.build_ext(dist)
cmd.define = 'one,two'
cmd.finalize_options()
- self.assertEqual(cmd.define, [('one', '1'), ('two', '1')])
+ assert cmd.define == [('one', '1'), ('two', '1')]
# make sure undef is turned into a list of
# strings if they are ','-separated strings
cmd = self.build_ext(dist)
cmd.undef = 'one,two'
cmd.finalize_options()
- self.assertEqual(cmd.undef, ['one', 'two'])
+ assert cmd.undef == ['one', 'two']
# make sure swig_opts is turned into a list
cmd = self.build_ext(dist)
cmd.swig_opts = None
cmd.finalize_options()
- self.assertEqual(cmd.swig_opts, [])
+ assert cmd.swig_opts == []
cmd = self.build_ext(dist)
cmd.swig_opts = '1 2'
cmd.finalize_options()
- self.assertEqual(cmd.swig_opts, ['1', '2'])
+ assert cmd.swig_opts == ['1', '2']
def test_check_extensions_list(self):
dist = Distribution()
@@ -271,35 +274,39 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
cmd.finalize_options()
# 'extensions' option must be a list of Extension instances
- self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, 'foo')
+ with pytest.raises(DistutilsSetupError):
+ cmd.check_extensions_list('foo')
# each element of 'ext_modules' option must be an
# Extension instance or 2-tuple
exts = [('bar', 'foo', 'bar'), 'foo']
- self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
+ with pytest.raises(DistutilsSetupError):
+ cmd.check_extensions_list(exts)
# first element of each tuple in 'ext_modules'
# must be the extension name (a string) and match
# a python dotted-separated name
exts = [('foo-bar', '')]
- self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
+ with pytest.raises(DistutilsSetupError):
+ cmd.check_extensions_list(exts)
# second element of each tuple in 'ext_modules'
# must be a dictionary (build info)
exts = [('foo.bar', '')]
- self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
+ with pytest.raises(DistutilsSetupError):
+ cmd.check_extensions_list(exts)
# ok this one should pass
exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', 'some': 'bar'})]
cmd.check_extensions_list(exts)
ext = exts[0]
- self.assertIsInstance(ext, Extension)
+ assert isinstance(ext, Extension)
# check_extensions_list adds in ext the values passed
# when they are in ('include_dirs', 'library_dirs', 'libraries'
# 'extra_objects', 'extra_compile_args', 'extra_link_args')
- self.assertEqual(ext.libraries, 'foo')
- self.assertFalse(hasattr(ext, 'some'))
+ assert ext.libraries == 'foo'
+ assert not hasattr(ext, 'some')
# 'macros' element of build info dict must be 1- or 2-tuple
exts = [
@@ -313,19 +320,20 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
},
)
]
- self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts)
+ with pytest.raises(DistutilsSetupError):
+ cmd.check_extensions_list(exts)
exts[0][1]['macros'] = [('1', '2'), ('3',)]
cmd.check_extensions_list(exts)
- self.assertEqual(exts[0].undef_macros, ['3'])
- self.assertEqual(exts[0].define_macros, [('1', '2')])
+ assert exts[0].undef_macros == ['3']
+ assert exts[0].define_macros == [('1', '2')]
def test_get_source_files(self):
modules = [Extension('foo', ['xxx'], optional=False)]
dist = Distribution({'name': 'xx', 'ext_modules': modules})
cmd = self.build_ext(dist)
cmd.ensure_finalized()
- self.assertEqual(cmd.get_source_files(), ['xxx'])
+ assert cmd.get_source_files() == ['xxx']
def test_unicode_module_names(self):
modules = [
@@ -335,10 +343,10 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
dist = Distribution({'name': 'xx', 'ext_modules': modules})
cmd = self.build_ext(dist)
cmd.ensure_finalized()
- self.assertRegex(cmd.get_ext_filename(modules[0].name), r'foo(_d)?\..*')
- self.assertRegex(cmd.get_ext_filename(modules[1].name), r'föö(_d)?\..*')
- self.assertEqual(cmd.get_export_symbols(modules[0]), ['PyInit_foo'])
- self.assertEqual(cmd.get_export_symbols(modules[1]), ['PyInitU_f_1gaa'])
+ assert re.search(r'foo(_d)?\..*', cmd.get_ext_filename(modules[0].name))
+ assert re.search(r'föö(_d)?\..*', cmd.get_ext_filename(modules[1].name))
+ assert cmd.get_export_symbols(modules[0]) == ['PyInit_foo']
+ assert cmd.get_export_symbols(modules[1]) == ['PyInitU_f_1gaa']
def test_compiler_option(self):
# cmd.compiler is an option and
@@ -349,12 +357,10 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
cmd.compiler = 'unix'
cmd.ensure_finalized()
cmd.run()
- self.assertEqual(cmd.compiler, 'unix')
+ assert cmd.compiler == 'unix'
def test_get_outputs(self):
cmd = support.missing_compiler_executable()
- if cmd is not None:
- self.skipTest('The %r command is not found' % cmd)
tmp_dir = self.mkdtemp()
c_file = os.path.join(tmp_dir, 'foo.c')
self.write_file(c_file, 'void PyInit_foo(void) {}\n')
@@ -363,7 +369,7 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
cmd = self.build_ext(dist)
fixup_build_ext(cmd)
cmd.ensure_finalized()
- self.assertEqual(len(cmd.get_outputs()), 1)
+ assert len(cmd.get_outputs()) == 1
cmd.build_lib = os.path.join(self.tmp_dir, 'build')
cmd.build_temp = os.path.join(self.tmp_dir, 'tempt')
@@ -379,20 +385,20 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
so_file = cmd.get_outputs()[0]
finally:
os.chdir(old_wd)
- self.assertTrue(os.path.exists(so_file))
+ assert os.path.exists(so_file)
ext_suffix = sysconfig.get_config_var('EXT_SUFFIX')
- self.assertTrue(so_file.endswith(ext_suffix))
+ assert so_file.endswith(ext_suffix)
so_dir = os.path.dirname(so_file)
- self.assertEqual(so_dir, other_tmp_dir)
+ assert so_dir == other_tmp_dir
cmd.inplace = 0
cmd.compiler = None
cmd.run()
so_file = cmd.get_outputs()[0]
- self.assertTrue(os.path.exists(so_file))
- self.assertTrue(so_file.endswith(ext_suffix))
+ assert os.path.exists(so_file)
+ assert so_file.endswith(ext_suffix)
so_dir = os.path.dirname(so_file)
- self.assertEqual(so_dir, cmd.build_lib)
+ assert so_dir == cmd.build_lib
# inplace = 0, cmd.package = 'bar'
build_py = cmd.get_finalized_command('build_py')
@@ -400,7 +406,7 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
path = cmd.get_ext_fullpath('foo')
# checking that the last directory is the build_dir
path = os.path.split(path)[0]
- self.assertEqual(path, cmd.build_lib)
+ assert path == cmd.build_lib
# inplace = 1, cmd.package = 'bar'
cmd.inplace = 1
@@ -414,7 +420,7 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
# checking that the last directory is bar
path = os.path.split(path)[0]
lastdir = os.path.split(path)[-1]
- self.assertEqual(lastdir, 'bar')
+ assert lastdir == 'bar'
def test_ext_fullpath(self):
ext = sysconfig.get_config_var('EXT_SUFFIX')
@@ -430,14 +436,14 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
curdir = os.getcwd()
wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext)
path = cmd.get_ext_fullpath('lxml.etree')
- self.assertEqual(wanted, path)
+ assert wanted == path
# building lxml.etree not inplace
cmd.inplace = 0
cmd.build_lib = os.path.join(curdir, 'tmpdir')
wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext)
path = cmd.get_ext_fullpath('lxml.etree')
- self.assertEqual(wanted, path)
+ assert wanted == path
# building twisted.runner.portmap not inplace
build_py = cmd.get_finalized_command('build_py')
@@ -445,30 +451,32 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
cmd.distribution.packages = ['twisted', 'twisted.runner.portmap']
path = cmd.get_ext_fullpath('twisted.runner.portmap')
wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', 'portmap' + ext)
- self.assertEqual(wanted, path)
+ assert wanted == path
# building twisted.runner.portmap inplace
cmd.inplace = 1
path = cmd.get_ext_fullpath('twisted.runner.portmap')
wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext)
- self.assertEqual(wanted, path)
+ assert wanted == path
- @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX')
+ @pytest.mark.skipif('platform.system() != "Darwin"')
+ @pytest.mark.usefixtures('save_env')
def test_deployment_target_default(self):
# Issue 9516: Test that, in the absence of the environment variable,
# an extension module is compiled with the same deployment target as
# the interpreter.
self._try_compile_deployment_target('==', None)
- @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX')
+ @pytest.mark.skipif('platform.system() != "Darwin"')
+ @pytest.mark.usefixtures('save_env')
def test_deployment_target_too_low(self):
# Issue 9516: Test that an extension module is not allowed to be
# compiled with a deployment target less than that of the interpreter.
- self.assertRaises(
- DistutilsPlatformError, self._try_compile_deployment_target, '>', '10.1'
- )
+ with pytest.raises(DistutilsPlatformError):
+ self._try_compile_deployment_target('>', '10.1')
- @unittest.skipUnless(sys.platform == 'darwin', 'test only relevant for MacOSX')
+ @pytest.mark.skipif('platform.system() != "Darwin"')
+ @pytest.mark.usefixtures('save_env')
def test_deployment_target_higher_ok(self):
# Issue 9516: Test that an extension module can be compiled with a
# deployment target higher than that of the interpreter: the ext
@@ -482,10 +490,6 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
self._try_compile_deployment_target('<', deptarget)
def _try_compile_deployment_target(self, operator, target):
- orig_environ = os.environ
- os.environ = orig_environ.copy()
- self.addCleanup(setattr, os, 'environ', orig_environ)
-
if target is None:
if os.environ.get('MACOSX_DEPLOYMENT_TARGET'):
del os.environ['MACOSX_DEPLOYMENT_TARGET']
@@ -531,7 +535,7 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
deptarget_ext = Extension(
'deptarget',
[deptarget_c],
- extra_compile_args=['-DTARGET=%s' % (target,)],
+ extra_compile_args=['-DTARGET={}'.format(target)],
)
dist = Distribution({'name': 'deptarget', 'ext_modules': [deptarget_ext]})
dist.package_dir = self.tmp_dir
@@ -554,7 +558,7 @@ class BuildExtTestCase(TempdirManager, LoggingSilencer, unittest.TestCase):
self.fail("Wrong deployment target during compilation")
-class ParallelBuildExtTestCase(BuildExtTestCase):
+class TestParallelBuildExt(TestBuildExt):
def build_ext(self, *args, **kwargs):
build_ext = super().build_ext(*args, **kwargs)
build_ext.parallel = True
diff --git a/distutils/tests/test_build_py.py b/distutils/tests/test_build_py.py
index 4a8582e4..63543dca 100644
--- a/distutils/tests/test_build_py.py
+++ b/distutils/tests/test_build_py.py
@@ -2,19 +2,19 @@
import os
import sys
-import unittest
+import unittest.mock as mock
+
+import pytest
from distutils.command.build_py import build_py
from distutils.core import Distribution
from distutils.errors import DistutilsFileError
-from unittest.mock import patch
from distutils.tests import support
-class BuildPyTestCase(
- support.TempdirManager, support.LoggingSilencer, unittest.TestCase
-):
+@support.combine_markers
+class TestBuildPy(support.TempdirManager, support.LoggingSilencer):
def test_package_data(self):
sources = self.mkdtemp()
f = open(os.path.join(sources, "__init__.py"), "w")
@@ -41,24 +41,24 @@ class BuildPyTestCase(
cmd = build_py(dist)
cmd.compile = 1
cmd.ensure_finalized()
- self.assertEqual(cmd.package_data, dist.package_data)
+ assert cmd.package_data == dist.package_data
cmd.run()
# This makes sure the list of outputs includes byte-compiled
# files for Python modules but not for package data files
# (there shouldn't *be* byte-code files for those!).
- self.assertEqual(len(cmd.get_outputs()), 3)
+ assert len(cmd.get_outputs()) == 3
pkgdest = os.path.join(destination, "pkg")
files = os.listdir(pkgdest)
pycache_dir = os.path.join(pkgdest, "__pycache__")
- self.assertIn("__init__.py", files)
- self.assertIn("README.txt", files)
+ assert "__init__.py" in files
+ assert "README.txt" in files
if sys.dont_write_bytecode:
- self.assertFalse(os.path.exists(pycache_dir))
+ assert not os.path.exists(pycache_dir)
else:
pyc_files = os.listdir(pycache_dir)
- self.assertIn("__init__.%s.pyc" % sys.implementation.cache_tag, pyc_files)
+ assert "__init__.%s.pyc" % sys.implementation.cache_tag in pyc_files
def test_empty_package_dir(self):
# See bugs #1668596/#1720897
@@ -87,7 +87,7 @@ class BuildPyTestCase(
except DistutilsFileError:
self.fail("failed package_data test when package_dir is ''")
- @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled')
+ @pytest.mark.skipif('sys.dont_write_bytecode')
def test_byte_compile(self):
project_dir, dist = self.create_dist(py_modules=['boiledeggs'])
os.chdir(project_dir)
@@ -99,11 +99,11 @@ class BuildPyTestCase(
cmd.run()
found = os.listdir(cmd.build_lib)
- self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py'])
+ assert sorted(found) == ['__pycache__', 'boiledeggs.py']
found = os.listdir(os.path.join(cmd.build_lib, '__pycache__'))
- self.assertEqual(found, ['boiledeggs.%s.pyc' % sys.implementation.cache_tag])
+ assert found == ['boiledeggs.%s.pyc' % sys.implementation.cache_tag]
- @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled')
+ @pytest.mark.skipif('sys.dont_write_bytecode')
def test_byte_compile_optimized(self):
project_dir, dist = self.create_dist(py_modules=['boiledeggs'])
os.chdir(project_dir)
@@ -116,10 +116,10 @@ class BuildPyTestCase(
cmd.run()
found = os.listdir(cmd.build_lib)
- self.assertEqual(sorted(found), ['__pycache__', 'boiledeggs.py'])
+ assert sorted(found) == ['__pycache__', 'boiledeggs.py']
found = os.listdir(os.path.join(cmd.build_lib, '__pycache__'))
- expect = 'boiledeggs.{}.opt-1.pyc'.format(sys.implementation.cache_tag)
- self.assertEqual(sorted(found), [expect])
+ expect = f'boiledeggs.{sys.implementation.cache_tag}.opt-1.pyc'
+ assert sorted(found) == [expect]
def test_dir_in_package_data(self):
"""
@@ -165,9 +165,9 @@ class BuildPyTestCase(
finally:
sys.dont_write_bytecode = old_dont_write_bytecode
- self.assertIn('byte-compiling is disabled', self.logs[0][1] % self.logs[0][2])
+ assert 'byte-compiling is disabled' in self.logs[0][1] % self.logs[0][2]
- @patch("distutils.command.build_py.log.warn")
+ @mock.patch("distutils.command.build_py.log.warn")
def test_namespace_package_does_not_warn(self, log_warn):
"""
Originally distutils implementation did not account for PEP 420
diff --git a/distutils/tests/test_build_scripts.py b/distutils/tests/test_build_scripts.py
index 6ef9dd61..00d7fc59 100644
--- a/distutils/tests/test_build_scripts.py
+++ b/distutils/tests/test_build_scripts.py
@@ -1,7 +1,6 @@
"""Tests for distutils.command.build_scripts."""
import os
-import unittest
from distutils.command.build_scripts import build_scripts
from distutils.core import Distribution
@@ -10,18 +9,16 @@ from distutils import sysconfig
from distutils.tests import support
-class BuildScriptsTestCase(
- support.TempdirManager, support.LoggingSilencer, unittest.TestCase
-):
+class TestBuildScripts(support.TempdirManager, support.LoggingSilencer):
def test_default_settings(self):
cmd = self.get_build_scripts_cmd("/foo/bar", [])
- self.assertFalse(cmd.force)
- self.assertIsNone(cmd.build_dir)
+ assert not cmd.force
+ assert cmd.build_dir is None
cmd.finalize_options()
- self.assertTrue(cmd.force)
- self.assertEqual(cmd.build_dir, "/foo/bar")
+ assert cmd.force
+ assert cmd.build_dir == "/foo/bar"
def test_build(self):
source = self.mkdtemp()
@@ -36,7 +33,7 @@ class BuildScriptsTestCase(
built = os.listdir(target)
for name in expected:
- self.assertIn(name, built)
+ assert name in built
def get_build_scripts_cmd(self, target, scripts):
import sys
@@ -106,4 +103,4 @@ class BuildScriptsTestCase(
built = os.listdir(target)
for name in expected:
- self.assertIn(name, built)
+ assert name in built
diff --git a/distutils/tests/test_ccompiler.py b/distutils/tests/test_ccompiler.py
new file mode 100644
index 00000000..da1879f2
--- /dev/null
+++ b/distutils/tests/test_ccompiler.py
@@ -0,0 +1,55 @@
+import os
+import sys
+import platform
+import textwrap
+import sysconfig
+
+import pytest
+
+from distutils import ccompiler
+
+
+def _make_strs(paths):
+ """
+ Convert paths to strings for legacy compatibility.
+ """
+ if sys.version_info > (3, 8) and platform.system() != "Windows":
+ return paths
+ return list(map(os.fspath, paths))
+
+
+@pytest.fixture
+def c_file(tmp_path):
+ c_file = tmp_path / 'foo.c'
+ gen_headers = ('Python.h',)
+ is_windows = platform.system() == "Windows"
+ plat_headers = ('windows.h',) * is_windows
+ all_headers = gen_headers + plat_headers
+ headers = '\n'.join(f'#include <{header}>\n' for header in all_headers)
+ payload = (
+ textwrap.dedent(
+ """
+ #headers
+ void PyInit_foo(void) {}
+ """
+ )
+ .lstrip()
+ .replace('#headers', headers)
+ )
+ c_file.write_text(payload)
+ return c_file
+
+
+def test_set_include_dirs(c_file):
+ """
+ Extensions should build even if set_include_dirs is invoked.
+ In particular, compiler-specific paths should not be overridden.
+ """
+ compiler = ccompiler.new_compiler()
+ python = sysconfig.get_paths()['include']
+ compiler.set_include_dirs([python])
+ compiler.compile(_make_strs([c_file]))
+
+ # do it again, setting include dirs after any initialization
+ compiler.set_include_dirs([python])
+ compiler.compile(_make_strs([c_file]))
diff --git a/distutils/tests/test_check.py b/distutils/tests/test_check.py
index 21035f5d..3e5f6034 100644
--- a/distutils/tests/test_check.py
+++ b/distutils/tests/test_check.py
@@ -1,9 +1,10 @@
"""Tests for distutils.command.check."""
import os
import textwrap
-import unittest
-from distutils.command.check import check, HAS_DOCUTILS
+import pytest
+
+from distutils.command.check import check
from distutils.tests import support
from distutils.errors import DistutilsSetupError
@@ -16,7 +17,8 @@ except ImportError:
HERE = os.path.dirname(__file__)
-class CheckTestCase(support.LoggingSilencer, support.TempdirManager, unittest.TestCase):
+@support.combine_markers
+class TestCheck(support.LoggingSilencer, support.TempdirManager):
def _run(self, metadata=None, cwd=None, **options):
if metadata is None:
metadata = {}
@@ -39,7 +41,7 @@ class CheckTestCase(support.LoggingSilencer, support.TempdirManager, unittest.Te
# by default, check is checking the metadata
# should have some warnings
cmd = self._run()
- self.assertEqual(cmd._warnings, 1)
+ assert cmd._warnings == 1
# now let's add the required fields
# and run it again, to make sure we don't get
@@ -52,15 +54,16 @@ class CheckTestCase(support.LoggingSilencer, support.TempdirManager, unittest.Te
'version': 'xxx',
}
cmd = self._run(metadata)
- self.assertEqual(cmd._warnings, 0)
+ assert cmd._warnings == 0
# now with the strict mode, we should
# get an error if there are missing metadata
- self.assertRaises(DistutilsSetupError, self._run, {}, **{'strict': 1})
+ with pytest.raises(DistutilsSetupError):
+ self._run({}, **{'strict': 1})
# and of course, no error when all metadata are present
cmd = self._run(metadata, strict=1)
- self.assertEqual(cmd._warnings, 0)
+ assert cmd._warnings == 0
# now a test with non-ASCII characters
metadata = {
@@ -73,7 +76,7 @@ class CheckTestCase(support.LoggingSilencer, support.TempdirManager, unittest.Te
'long_description': 'More things about esszet \u00df',
}
cmd = self._run(metadata)
- self.assertEqual(cmd._warnings, 0)
+ assert cmd._warnings == 0
def test_check_author_maintainer(self):
for kind in ("author", "maintainer"):
@@ -86,42 +89,42 @@ class CheckTestCase(support.LoggingSilencer, support.TempdirManager, unittest.Te
'version': 'xxx',
}
cmd = self._run(metadata)
- self.assertEqual(cmd._warnings, 0)
+ assert cmd._warnings == 0
# the check should not warn if only email is given
metadata[kind + '_email'] = 'name@email.com'
cmd = self._run(metadata)
- self.assertEqual(cmd._warnings, 0)
+ assert cmd._warnings == 0
# the check should not warn if only the name is given
metadata[kind] = "Name"
del metadata[kind + '_email']
cmd = self._run(metadata)
- self.assertEqual(cmd._warnings, 0)
+ assert cmd._warnings == 0
- @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils")
def test_check_document(self):
+ pytest.importorskip('docutils')
pkg_info, dist = self.create_dist()
cmd = check(dist)
# let's see if it detects broken rest
broken_rest = 'title\n===\n\ntest'
msgs = cmd._check_rst_data(broken_rest)
- self.assertEqual(len(msgs), 1)
+ assert len(msgs) == 1
# and non-broken rest
rest = 'title\n=====\n\ntest'
msgs = cmd._check_rst_data(rest)
- self.assertEqual(len(msgs), 0)
+ assert len(msgs) == 0
- @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils")
def test_check_restructuredtext(self):
+ pytest.importorskip('docutils')
# let's see if it detects broken rest in long_description
broken_rest = 'title\n===\n\ntest'
pkg_info, dist = self.create_dist(long_description=broken_rest)
cmd = check(dist)
cmd.check_restructuredtext()
- self.assertEqual(cmd._warnings, 1)
+ assert cmd._warnings == 1
# let's see if we have an error with strict=1
metadata = {
@@ -132,25 +135,21 @@ class CheckTestCase(support.LoggingSilencer, support.TempdirManager, unittest.Te
'version': 'xxx',
'long_description': broken_rest,
}
- self.assertRaises(
- DistutilsSetupError,
- self._run,
- metadata,
- **{'strict': 1, 'restructuredtext': 1}
- )
+ with pytest.raises(DistutilsSetupError):
+ self._run(metadata, **{'strict': 1, 'restructuredtext': 1})
# and non-broken rest, including a non-ASCII character to test #12114
metadata['long_description'] = 'title\n=====\n\ntest \u00df'
cmd = self._run(metadata, strict=1, restructuredtext=1)
- self.assertEqual(cmd._warnings, 0)
+ assert cmd._warnings == 0
# check that includes work to test #31292
metadata['long_description'] = 'title\n=====\n\n.. include:: includetest.rst'
cmd = self._run(metadata, cwd=HERE, strict=1, restructuredtext=1)
- self.assertEqual(cmd._warnings, 0)
+ assert cmd._warnings == 0
- @unittest.skipUnless(HAS_DOCUTILS, "won't test without docutils")
def test_check_restructuredtext_with_syntax_highlight(self):
+ pytest.importorskip('docutils')
# Don't fail if there is a `code` or `code-block` directive
example_rst_docs = []
@@ -185,14 +184,14 @@ class CheckTestCase(support.LoggingSilencer, support.TempdirManager, unittest.Te
cmd.check_restructuredtext()
msgs = cmd._check_rst_data(rest_with_code)
if pygments is not None:
- self.assertEqual(len(msgs), 0)
+ assert len(msgs) == 0
else:
- self.assertEqual(len(msgs), 1)
- self.assertEqual(
- str(msgs[0][1]), 'Cannot analyze code. Pygments package not found.'
+ assert len(msgs) == 1
+ assert (
+ str(msgs[0][1])
+ == 'Cannot analyze code. Pygments package not found.'
)
def test_check_all(self):
- self.assertRaises(
- DistutilsSetupError, self._run, {}, **{'strict': 1, 'restructuredtext': 1}
- )
+ with pytest.raises(DistutilsSetupError):
+ self._run({}, **{'strict': 1, 'restructuredtext': 1})
diff --git a/distutils/tests/test_clean.py b/distutils/tests/test_clean.py
index 796ca0fc..4166bb7e 100644
--- a/distutils/tests/test_clean.py
+++ b/distutils/tests/test_clean.py
@@ -1,12 +1,11 @@
"""Tests for distutils.command.clean."""
import os
-import unittest
from distutils.command.clean import clean
from distutils.tests import support
-class cleanTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase):
+class TestClean(support.TempdirManager, support.LoggingSilencer):
def test_simple_run(self):
pkg_dir, dist = self.create_dist()
cmd = clean(dist)
@@ -38,7 +37,7 @@ class cleanTestCase(support.TempdirManager, support.LoggingSilencer, unittest.Te
# make sure the files where removed
for name, path in dirs:
- self.assertFalse(os.path.exists(path), '%s was not removed' % path)
+ assert not os.path.exists(path), '%s was not removed' % path
# let's run the command again (should spit warnings but succeed)
cmd.all = 1
diff --git a/distutils/tests/test_cmd.py b/distutils/tests/test_cmd.py
index 6a771a11..e4d5bf3c 100644
--- a/distutils/tests/test_cmd.py
+++ b/distutils/tests/test_cmd.py
@@ -1,5 +1,4 @@
"""Tests for distutils.cmd."""
-import unittest
import os
from test.support import captured_stdout
@@ -7,6 +6,7 @@ from distutils.cmd import Command
from distutils.dist import Distribution
from distutils.errors import DistutilsOptionError
from distutils import debug
+import pytest
class MyCmd(Command):
@@ -14,14 +14,13 @@ class MyCmd(Command):
pass
-class CommandTestCase(unittest.TestCase):
- def setUp(self):
- dist = Distribution()
- self.cmd = MyCmd(dist)
+@pytest.fixture
+def cmd(request):
+ return MyCmd(Distribution())
- def test_ensure_string_list(self):
- cmd = self.cmd
+class TestCommand:
+ def test_ensure_string_list(self, cmd):
cmd.not_string_list = ['one', 2, 'three']
cmd.yes_string_list = ['one', 'two', 'three']
cmd.not_string_list2 = object()
@@ -29,49 +28,43 @@ class CommandTestCase(unittest.TestCase):
cmd.ensure_string_list('yes_string_list')
cmd.ensure_string_list('yes_string_list2')
- self.assertRaises(
- DistutilsOptionError, cmd.ensure_string_list, 'not_string_list'
- )
+ with pytest.raises(DistutilsOptionError):
+ cmd.ensure_string_list('not_string_list')
- self.assertRaises(
- DistutilsOptionError, cmd.ensure_string_list, 'not_string_list2'
- )
+ with pytest.raises(DistutilsOptionError):
+ cmd.ensure_string_list('not_string_list2')
cmd.option1 = 'ok,dok'
cmd.ensure_string_list('option1')
- self.assertEqual(cmd.option1, ['ok', 'dok'])
+ assert cmd.option1 == ['ok', 'dok']
cmd.option2 = ['xxx', 'www']
cmd.ensure_string_list('option2')
cmd.option3 = ['ok', 2]
- self.assertRaises(DistutilsOptionError, cmd.ensure_string_list, 'option3')
-
- def test_make_file(self):
-
- cmd = self.cmd
+ with pytest.raises(DistutilsOptionError):
+ cmd.ensure_string_list('option3')
+ def test_make_file(self, cmd):
# making sure it raises when infiles is not a string or a list/tuple
- self.assertRaises(
- TypeError, cmd.make_file, infiles=1, outfile='', func='func', args=()
- )
+ with pytest.raises(TypeError):
+ cmd.make_file(infiles=1, outfile='', func='func', args=())
# making sure execute gets called properly
def _execute(func, args, exec_msg, level):
- self.assertEqual(exec_msg, 'generating out from in')
+ assert exec_msg == 'generating out from in'
cmd.force = True
cmd.execute = _execute
cmd.make_file(infiles='in', outfile='out', func='func', args=())
- def test_dump_options(self):
+ def test_dump_options(self, cmd):
msgs = []
def _announce(msg, level):
msgs.append(msg)
- cmd = self.cmd
cmd.announce = _announce
cmd.option1 = 1
cmd.option2 = 1
@@ -79,46 +72,45 @@ class CommandTestCase(unittest.TestCase):
cmd.dump_options()
wanted = ["command options for 'MyCmd':", ' option1 = 1', ' option2 = 1']
- self.assertEqual(msgs, wanted)
+ assert msgs == wanted
- def test_ensure_string(self):
- cmd = self.cmd
+ def test_ensure_string(self, cmd):
cmd.option1 = 'ok'
cmd.ensure_string('option1')
cmd.option2 = None
cmd.ensure_string('option2', 'xxx')
- self.assertTrue(hasattr(cmd, 'option2'))
+ assert hasattr(cmd, 'option2')
cmd.option3 = 1
- self.assertRaises(DistutilsOptionError, cmd.ensure_string, 'option3')
+ with pytest.raises(DistutilsOptionError):
+ cmd.ensure_string('option3')
- def test_ensure_filename(self):
- cmd = self.cmd
+ def test_ensure_filename(self, cmd):
cmd.option1 = __file__
cmd.ensure_filename('option1')
cmd.option2 = 'xxx'
- self.assertRaises(DistutilsOptionError, cmd.ensure_filename, 'option2')
+ with pytest.raises(DistutilsOptionError):
+ cmd.ensure_filename('option2')
- def test_ensure_dirname(self):
- cmd = self.cmd
+ def test_ensure_dirname(self, cmd):
cmd.option1 = os.path.dirname(__file__) or os.curdir
cmd.ensure_dirname('option1')
cmd.option2 = 'xxx'
- self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2')
+ with pytest.raises(DistutilsOptionError):
+ cmd.ensure_dirname('option2')
- def test_debug_print(self):
- cmd = self.cmd
+ def test_debug_print(self, cmd):
with captured_stdout() as stdout:
cmd.debug_print('xxx')
stdout.seek(0)
- self.assertEqual(stdout.read(), '')
+ assert stdout.read() == ''
debug.DEBUG = True
try:
with captured_stdout() as stdout:
cmd.debug_print('xxx')
stdout.seek(0)
- self.assertEqual(stdout.read(), 'xxx\n')
+ assert stdout.read() == 'xxx\n'
finally:
debug.DEBUG = False
diff --git a/distutils/tests/test_config.py b/distutils/tests/test_config.py
index 5bca4da8..43ba6766 100644
--- a/distutils/tests/test_config.py
+++ b/distutils/tests/test_config.py
@@ -1,14 +1,8 @@
"""Tests for distutils.pypirc.pypirc."""
import os
-import unittest
import pytest
-from distutils.core import PyPIRCCommand
-from distutils.core import Distribution
-from distutils.log import set_threshold
-from distutils.log import WARN
-
from distutils.tests import support
PYPIRC = """\
@@ -51,37 +45,14 @@ password:xxx
"""
-@pytest.mark.usefixtures('save_env')
+@support.combine_markers
+@pytest.mark.usefixtures('threshold_warn')
+@pytest.mark.usefixtures('pypirc')
class BasePyPIRCCommandTestCase(
support.TempdirManager,
support.LoggingSilencer,
- unittest.TestCase,
):
- def setUp(self):
- """Patches the environment."""
- super(BasePyPIRCCommandTestCase, self).setUp()
- self.tmp_dir = self.mkdtemp()
- os.environ['HOME'] = self.tmp_dir
- os.environ['USERPROFILE'] = self.tmp_dir
- self.rc = os.path.join(self.tmp_dir, '.pypirc')
- self.dist = Distribution()
-
- class command(PyPIRCCommand):
- def __init__(self, dist):
- super().__init__(dist)
-
- def initialize_options(self):
- pass
-
- finalize_options = initialize_options
-
- self._cmd = command
- self.old_threshold = set_threshold(WARN)
-
- def tearDown(self):
- """Removes the patch."""
- set_threshold(self.old_threshold)
- super(BasePyPIRCCommandTestCase, self).tearDown()
+ pass
class PyPIRCCommandTestCase(BasePyPIRCCommandTestCase):
@@ -103,7 +74,7 @@ class PyPIRCCommandTestCase(BasePyPIRCCommandTestCase):
('server', 'server1'),
('username', 'me'),
]
- self.assertEqual(config, waited)
+ assert config == waited
# old format
self.write_file(self.rc, PYPIRC_OLD)
@@ -116,18 +87,18 @@ class PyPIRCCommandTestCase(BasePyPIRCCommandTestCase):
('server', 'server-login'),
('username', 'tarek'),
]
- self.assertEqual(config, waited)
+ assert config == waited
def test_server_empty_registration(self):
cmd = self._cmd(self.dist)
rc = cmd._get_rc_file()
- self.assertFalse(os.path.exists(rc))
+ assert not os.path.exists(rc)
cmd._store_pypirc('tarek', 'xxx')
- self.assertTrue(os.path.exists(rc))
+ assert os.path.exists(rc)
f = open(rc)
try:
content = f.read()
- self.assertEqual(content, WANTED)
+ assert content == WANTED
finally:
f.close()
@@ -146,4 +117,4 @@ class PyPIRCCommandTestCase(BasePyPIRCCommandTestCase):
('server', 'server3'),
('username', 'cbiggles'),
]
- self.assertEqual(config, waited)
+ assert config == waited
diff --git a/distutils/tests/test_config_cmd.py b/distutils/tests/test_config_cmd.py
index 3c0879b5..65c60f64 100644
--- a/distutils/tests/test_config_cmd.py
+++ b/distutils/tests/test_config_cmd.py
@@ -1,31 +1,28 @@
"""Tests for distutils.command.config."""
-import unittest
import os
import sys
from test.support import missing_compiler_executable
+import pytest
+
from distutils.command.config import dump_file, config
from distutils.tests import support
from distutils import log
-class ConfigTestCase(
- support.LoggingSilencer, support.TempdirManager, unittest.TestCase
-):
+@pytest.fixture(autouse=True)
+def info_log(request, monkeypatch):
+ self = request.instance
+ self._logs = []
+ monkeypatch.setattr(log, 'info', self._info)
+
+
+@support.combine_markers
+class TestConfig(support.LoggingSilencer, support.TempdirManager):
def _info(self, msg, *args):
for line in msg.splitlines():
self._logs.append(line)
- def setUp(self):
- super(ConfigTestCase, self).setUp()
- self._logs = []
- self.old_log = log.info
- log.info = self._info
-
- def tearDown(self):
- log.info = self.old_log
- super(ConfigTestCase, self).tearDown()
-
def test_dump_file(self):
this_file = os.path.splitext(__file__)[0] + '.py'
f = open(this_file)
@@ -35,9 +32,9 @@ class ConfigTestCase(
f.close()
dump_file(this_file, 'I am the header')
- self.assertEqual(len(self._logs), numlines + 1)
+ assert len(self._logs) == numlines + 1
- @unittest.skipIf(sys.platform == 'win32', "can't test on Windows")
+ @pytest.mark.skipif('platform.system() == "Windows"')
def test_search_cpp(self):
cmd = missing_compiler_executable(['preprocessor'])
if cmd is not None:
@@ -53,10 +50,10 @@ class ConfigTestCase(
# simple pattern searches
match = cmd.search_cpp(pattern='xxx', body='/* xxx */')
- self.assertEqual(match, 0)
+ assert match == 0
match = cmd.search_cpp(pattern='_configtest', body='/* xxx */')
- self.assertEqual(match, 1)
+ assert match == 1
def test_finalize_options(self):
# finalize_options does a bit of transformation
@@ -68,9 +65,9 @@ class ConfigTestCase(
cmd.library_dirs = 'three%sfour' % os.pathsep
cmd.ensure_finalized()
- self.assertEqual(cmd.include_dirs, ['one', 'two'])
- self.assertEqual(cmd.libraries, ['one'])
- self.assertEqual(cmd.library_dirs, ['three', 'four'])
+ assert cmd.include_dirs == ['one', 'two']
+ assert cmd.libraries == ['one']
+ assert cmd.library_dirs == ['three', 'four']
def test_clean(self):
# _clean removes files
@@ -82,11 +79,11 @@ class ConfigTestCase(
self.write_file(f2, 'xxx')
for f in (f1, f2):
- self.assertTrue(os.path.exists(f))
+ assert os.path.exists(f)
pkg_dir, dist = self.create_dist()
cmd = config(dist)
cmd._clean(f1, f2)
for f in (f1, f2):
- self.assertFalse(os.path.exists(f))
+ assert not os.path.exists(f)
diff --git a/distutils/tests/test_core.py b/distutils/tests/test_core.py
index c3943866..86b0040f 100644
--- a/distutils/tests/test_core.py
+++ b/distutils/tests/test_core.py
@@ -3,15 +3,12 @@
import io
import distutils.core
import os
-import shutil
import sys
from test.support import captured_stdout
import pytest
from . import py38compat as os_helper
-import unittest
-from distutils import log
from distutils.dist import Distribution
# setup script that uses __file__
@@ -59,29 +56,15 @@ if __name__ == "__main__":
"""
-@pytest.mark.usefixtures('save_env')
-class CoreTestCase(unittest.TestCase):
- def setUp(self):
- super(CoreTestCase, self).setUp()
- self.old_stdout = sys.stdout
- self.cleanup_testfn()
- self.old_argv = sys.argv, sys.argv[:]
- self.addCleanup(log.set_threshold, log._global_log.threshold)
-
- def tearDown(self):
- sys.stdout = self.old_stdout
- self.cleanup_testfn()
- sys.argv = self.old_argv[0]
- sys.argv[:] = self.old_argv[1]
- super(CoreTestCase, self).tearDown()
-
- def cleanup_testfn(self):
- path = os_helper.TESTFN
- if os.path.isfile(path):
- os.remove(path)
- elif os.path.isdir(path):
- shutil.rmtree(path)
+@pytest.fixture(autouse=True)
+def save_stdout(monkeypatch):
+ monkeypatch.setattr(sys, 'stdout', sys.stdout)
+
+@pytest.mark.usefixtures('save_env')
+@pytest.mark.usefixtures('save_argv')
+@pytest.mark.usefixtures('cleanup_testfn')
+class TestCore:
def write_setup(self, text, path=os_helper.TESTFN):
f = open(path, "w")
try:
@@ -99,14 +82,14 @@ class CoreTestCase(unittest.TestCase):
# Make sure run_setup does not clobber sys.argv
argv_copy = sys.argv.copy()
distutils.core.run_setup(self.write_setup(setup_does_nothing))
- self.assertEqual(sys.argv, argv_copy)
+ assert sys.argv == argv_copy
def test_run_setup_defines_subclass(self):
# Make sure the script can use __file__; if that's missing, the test
# setup.py script will raise NameError.
dist = distutils.core.run_setup(self.write_setup(setup_defines_subclass))
install = dist.get_command_obj('install')
- self.assertIn('cmd', install.sub_commands)
+ assert 'cmd' in install.sub_commands
def test_run_setup_uses_current_dir(self):
# This tests that the setup script is run with the current directory
@@ -123,23 +106,23 @@ class CoreTestCase(unittest.TestCase):
output = sys.stdout.getvalue()
if output.endswith("\n"):
output = output[:-1]
- self.assertEqual(cwd, output)
+ assert cwd == output
def test_run_setup_within_if_main(self):
dist = distutils.core.run_setup(
self.write_setup(setup_within_if_main), stop_after="config"
)
- self.assertIsInstance(dist, Distribution)
- self.assertEqual(dist.get_name(), "setup_within_if_main")
+ assert isinstance(dist, Distribution)
+ assert dist.get_name() == "setup_within_if_main"
def test_run_commands(self):
sys.argv = ['setup.py', 'build']
dist = distutils.core.run_setup(
self.write_setup(setup_within_if_main), stop_after="commandline"
)
- self.assertNotIn('build', dist.have_run)
+ assert 'build' not in dist.have_run
distutils.core.run_commands(dist)
- self.assertIn('build', dist.have_run)
+ assert 'build' in dist.have_run
def test_debug_mode(self):
# this covers the code called when DEBUG is set
@@ -147,7 +130,7 @@ class CoreTestCase(unittest.TestCase):
with captured_stdout() as stdout:
distutils.core.setup(name='bar')
stdout.seek(0)
- self.assertEqual(stdout.read(), 'bar\n')
+ assert stdout.read() == 'bar\n'
distutils.core.DEBUG = True
try:
@@ -157,4 +140,4 @@ class CoreTestCase(unittest.TestCase):
distutils.core.DEBUG = False
stdout.seek(0)
wanted = "options (after parsing config files):\n"
- self.assertEqual(stdout.readlines()[0], wanted)
+ assert stdout.readlines()[0] == wanted
diff --git a/distutils/tests/test_cygwinccompiler.py b/distutils/tests/test_cygwinccompiler.py
index da73adc6..ef01ae21 100644
--- a/distutils/tests/test_cygwinccompiler.py
+++ b/distutils/tests/test_cygwinccompiler.py
@@ -1,8 +1,9 @@
"""Tests for distutils.cygwinccompiler."""
-import unittest
import sys
import os
+import pytest
+
from distutils.cygwinccompiler import (
check_config_h,
CONFIG_H_OK,
@@ -11,48 +12,39 @@ from distutils.cygwinccompiler import (
get_msvcr,
)
from distutils.tests import support
+from distutils import sysconfig
-class CygwinCCompilerTestCase(support.TempdirManager, unittest.TestCase):
- def setUp(self):
- super(CygwinCCompilerTestCase, self).setUp()
- self.version = sys.version
- self.python_h = os.path.join(self.mkdtemp(), 'python.h')
- from distutils import sysconfig
-
- self.old_get_config_h_filename = sysconfig.get_config_h_filename
- sysconfig.get_config_h_filename = self._get_config_h_filename
-
- def tearDown(self):
- sys.version = self.version
- from distutils import sysconfig
+@pytest.fixture(autouse=True)
+def stuff(request, monkeypatch, distutils_managed_tempdir):
+ self = request.instance
+ self.python_h = os.path.join(self.mkdtemp(), 'python.h')
+ monkeypatch.setattr(sysconfig, 'get_config_h_filename', self._get_config_h_filename)
+ monkeypatch.setattr(sys, 'version', sys.version)
- sysconfig.get_config_h_filename = self.old_get_config_h_filename
- super(CygwinCCompilerTestCase, self).tearDown()
+class TestCygwinCCompiler(support.TempdirManager):
def _get_config_h_filename(self):
return self.python_h
- @unittest.skipIf(sys.platform != "cygwin", "Not running on Cygwin")
- @unittest.skipIf(
- not os.path.exists("/usr/lib/libbash.dll.a"), "Don't know a linkable library"
- )
+ @pytest.mark.skipif('sys.platform != "cygwin"')
+ @pytest.mark.skipif('not os.path.exists("/usr/lib/libbash.dll.a")')
def test_find_library_file(self):
from distutils.cygwinccompiler import CygwinCCompiler
compiler = CygwinCCompiler()
link_name = "bash"
linkable_file = compiler.find_library_file(["/usr/lib"], link_name)
- self.assertIsNotNone(linkable_file)
- self.assertTrue(os.path.exists(linkable_file))
- self.assertEquals(linkable_file, "/usr/lib/lib{:s}.dll.a".format(link_name))
+ assert linkable_file is not None
+ assert os.path.exists(linkable_file)
+ assert linkable_file == f"/usr/lib/lib{link_name:s}.dll.a"
- @unittest.skipIf(sys.platform != "cygwin", "Not running on Cygwin")
+ @pytest.mark.skipif('sys.platform != "cygwin"')
def test_runtime_library_dir_option(self):
from distutils.cygwinccompiler import CygwinCCompiler
compiler = CygwinCCompiler()
- self.assertEqual(compiler.runtime_library_dir_option('/foo'), [])
+ assert compiler.runtime_library_dir_option('/foo') == []
def test_check_config_h(self):
@@ -63,21 +55,21 @@ class CygwinCCompilerTestCase(support.TempdirManager, unittest.TestCase):
'4.0.1 (Apple Computer, Inc. build 5370)]'
)
- self.assertEqual(check_config_h()[0], CONFIG_H_OK)
+ assert check_config_h()[0] == CONFIG_H_OK
# then it tries to see if it can find "__GNUC__" in pyconfig.h
sys.version = 'something without the *CC word'
# if the file doesn't exist it returns CONFIG_H_UNCERTAIN
- self.assertEqual(check_config_h()[0], CONFIG_H_UNCERTAIN)
+ assert check_config_h()[0] == CONFIG_H_UNCERTAIN
# if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK
self.write_file(self.python_h, 'xxx')
- self.assertEqual(check_config_h()[0], CONFIG_H_NOTOK)
+ assert check_config_h()[0] == CONFIG_H_NOTOK
# and CONFIG_H_OK if __GNUC__ is found
self.write_file(self.python_h, 'xxx __GNUC__ xxx')
- self.assertEqual(check_config_h()[0], CONFIG_H_OK)
+ assert check_config_h()[0] == CONFIG_H_OK
def test_get_msvcr(self):
@@ -86,40 +78,41 @@ class CygwinCCompilerTestCase(support.TempdirManager, unittest.TestCase):
'2.6.1 (r261:67515, Dec 6 2008, 16:42:21) '
'\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]'
)
- self.assertEqual(get_msvcr(), None)
+ assert get_msvcr() is None
# MSVC 7.0
sys.version = (
'2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1300 32 bits (Intel)]'
)
- self.assertEqual(get_msvcr(), ['msvcr70'])
+ assert get_msvcr() == ['msvcr70']
# MSVC 7.1
sys.version = (
'2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1310 32 bits (Intel)]'
)
- self.assertEqual(get_msvcr(), ['msvcr71'])
+ assert get_msvcr() == ['msvcr71']
# VS2005 / MSVC 8.0
sys.version = (
'2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1400 32 bits (Intel)]'
)
- self.assertEqual(get_msvcr(), ['msvcr80'])
+ assert get_msvcr() == ['msvcr80']
# VS2008 / MSVC 9.0
sys.version = (
'2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1500 32 bits (Intel)]'
)
- self.assertEqual(get_msvcr(), ['msvcr90'])
+ assert get_msvcr() == ['msvcr90']
sys.version = (
'3.10.0 (tags/v3.10.0:b494f59, Oct 4 2021, 18:46:30) '
'[MSC v.1929 32 bit (Intel)]'
)
- self.assertEqual(get_msvcr(), ['ucrt', 'vcruntime140'])
+ assert get_msvcr() == ['ucrt', 'vcruntime140']
# unknown
sys.version = (
'2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.2000 32 bits (Intel)]'
)
- self.assertRaises(ValueError, get_msvcr)
+ with pytest.raises(ValueError):
+ get_msvcr()
diff --git a/distutils/tests/test_dep_util.py b/distutils/tests/test_dep_util.py
index fb170c6f..2dcce1dd 100644
--- a/distutils/tests/test_dep_util.py
+++ b/distutils/tests/test_dep_util.py
@@ -1,13 +1,13 @@
"""Tests for distutils.dep_util."""
-import unittest
import os
from distutils.dep_util import newer, newer_pairwise, newer_group
from distutils.errors import DistutilsFileError
from distutils.tests import support
+import pytest
-class DepUtilTestCase(support.TempdirManager, unittest.TestCase):
+class TestDepUtil(support.TempdirManager):
def test_newer(self):
tmpdir = self.mkdtemp()
@@ -15,17 +15,18 @@ class DepUtilTestCase(support.TempdirManager, unittest.TestCase):
old_file = os.path.abspath(__file__)
# Raise DistutilsFileError if 'new_file' does not exist.
- self.assertRaises(DistutilsFileError, newer, new_file, old_file)
+ with pytest.raises(DistutilsFileError):
+ newer(new_file, old_file)
# Return true if 'new_file' exists and is more recently modified than
# 'old_file', or if 'new_file' exists and 'old_file' doesn't.
self.write_file(new_file)
- self.assertTrue(newer(new_file, 'I_dont_exist'))
- self.assertTrue(newer(new_file, old_file))
+ assert newer(new_file, 'I_dont_exist')
+ assert newer(new_file, old_file)
# Return false if both exist and 'old_file' is the same age or younger
# than 'new_file'.
- self.assertFalse(newer(old_file, new_file))
+ assert not newer(old_file, new_file)
def test_newer_pairwise(self):
tmpdir = self.mkdtemp()
@@ -41,7 +42,7 @@ class DepUtilTestCase(support.TempdirManager, unittest.TestCase):
self.write_file(two)
self.write_file(four)
- self.assertEqual(newer_pairwise([one, two], [three, four]), ([one], [three]))
+ assert newer_pairwise([one, two], [three, four]) == ([one], [three])
def test_newer_group(self):
tmpdir = self.mkdtemp()
@@ -57,13 +58,14 @@ class DepUtilTestCase(support.TempdirManager, unittest.TestCase):
self.write_file(one)
self.write_file(two)
self.write_file(three)
- self.assertTrue(newer_group([one, two, three], old_file))
- self.assertFalse(newer_group([one, two, old_file], three))
+ assert newer_group([one, two, three], old_file)
+ assert not newer_group([one, two, old_file], three)
# missing handling
os.remove(one)
- self.assertRaises(OSError, newer_group, [one, two, old_file], three)
+ with pytest.raises(OSError):
+ newer_group([one, two, old_file], three)
- self.assertFalse(newer_group([one, two, old_file], three, missing='ignore'))
+ assert not newer_group([one, two, old_file], three, missing='ignore')
- self.assertTrue(newer_group([one, two, old_file], three, missing='newer'))
+ assert newer_group([one, two, old_file], three, missing='newer')
diff --git a/distutils/tests/test_dir_util.py b/distutils/tests/test_dir_util.py
index 8231df69..cd7e018f 100644
--- a/distutils/tests/test_dir_util.py
+++ b/distutils/tests/test_dir_util.py
@@ -1,9 +1,7 @@
"""Tests for distutils.dir_util."""
-import unittest
import os
import stat
-import sys
-from unittest.mock import patch
+import unittest.mock as mock
from distutils import dir_util, errors
from distutils.dir_util import (
@@ -16,67 +14,62 @@ from distutils.dir_util import (
from distutils import log
from distutils.tests import support
+import pytest
-class DirUtilTestCase(support.TempdirManager, unittest.TestCase):
+@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 setUp(self):
- super(DirUtilTestCase, self).setUp()
- 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')
- self.old_log = log.info
- log.info = self._log
-
- def tearDown(self):
- log.info = self.old_log
- super(DirUtilTestCase, self).tearDown()
-
def test_mkpath_remove_tree_verbosity(self):
mkpath(self.target, verbose=0)
wanted = []
- self.assertEqual(self._logs, wanted)
+ assert self._logs == wanted
remove_tree(self.root_target, verbose=0)
mkpath(self.target, verbose=1)
wanted = ['creating %s' % self.root_target, 'creating %s' % self.target]
- self.assertEqual(self._logs, wanted)
+ assert self._logs == wanted
self._logs = []
remove_tree(self.root_target, verbose=1)
wanted = ["removing '%s' (and everything under it)" % self.root_target]
- self.assertEqual(self._logs, wanted)
+ assert self._logs == wanted
- @unittest.skipIf(
- sys.platform.startswith('win'),
- "This test is only appropriate for POSIX-like systems.",
- )
+ @pytest.mark.skipif("platform.system() == 'Windows'")
def test_mkpath_with_custom_mode(self):
# Get and set the current umask value for testing mode bits.
umask = os.umask(0o002)
os.umask(umask)
mkpath(self.target, 0o700)
- self.assertEqual(stat.S_IMODE(os.stat(self.target).st_mode), 0o700 & ~umask)
+ assert stat.S_IMODE(os.stat(self.target).st_mode) == 0o700 & ~umask
mkpath(self.target2, 0o555)
- self.assertEqual(stat.S_IMODE(os.stat(self.target2).st_mode), 0o555 & ~umask)
+ assert stat.S_IMODE(os.stat(self.target2).st_mode) == 0o555 & ~umask
def test_create_tree_verbosity(self):
create_tree(self.root_target, ['one', 'two', 'three'], verbose=0)
- self.assertEqual(self._logs, [])
+ assert self._logs == []
remove_tree(self.root_target, verbose=0)
wanted = ['creating %s' % self.root_target]
create_tree(self.root_target, ['one', 'two', 'three'], verbose=1)
- self.assertEqual(self._logs, wanted)
+ assert self._logs == wanted
remove_tree(self.root_target, verbose=0)
@@ -85,7 +78,7 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase):
mkpath(self.target, verbose=0)
copy_tree(self.target, self.target2, verbose=0)
- self.assertEqual(self._logs, [])
+ assert self._logs == []
remove_tree(self.root_target, verbose=0)
@@ -94,9 +87,9 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase):
with open(a_file, 'w') as f:
f.write('some content')
- wanted = ['copying %s -> %s' % (a_file, self.target2)]
+ wanted = ['copying {} -> {}'.format(a_file, self.target2)]
copy_tree(self.target, self.target2, verbose=1)
- self.assertEqual(self._logs, wanted)
+ assert self._logs == wanted
remove_tree(self.root_target, verbose=0)
remove_tree(self.target2, verbose=0)
@@ -111,24 +104,24 @@ class DirUtilTestCase(support.TempdirManager, unittest.TestCase):
fh.write('some content')
copy_tree(self.target, self.target2)
- self.assertEqual(os.listdir(self.target2), ['ok.txt'])
+ assert os.listdir(self.target2) == ['ok.txt']
remove_tree(self.root_target, verbose=0)
remove_tree(self.target2, verbose=0)
def test_ensure_relative(self):
if os.sep == '/':
- self.assertEqual(ensure_relative('/home/foo'), 'home/foo')
- self.assertEqual(ensure_relative('some/path'), 'some/path')
+ assert ensure_relative('/home/foo') == 'home/foo'
+ assert ensure_relative('some/path') == 'some/path'
else: # \\
- self.assertEqual(ensure_relative('c:\\home\\foo'), 'c:home\\foo')
- self.assertEqual(ensure_relative('home\\foo'), 'home\\foo')
+ assert ensure_relative('c:\\home\\foo') == 'c:home\\foo'
+ assert ensure_relative('home\\foo') == 'home\\foo'
def test_copy_tree_exception_in_listdir(self):
"""
An exception in listdir should raise a DistutilsFileError
"""
- with patch("os.listdir", side_effect=OSError()), self.assertRaises(
+ with mock.patch("os.listdir", side_effect=OSError()), pytest.raises(
errors.DistutilsFileError
):
src = self.tempdirs[-1]
diff --git a/distutils/tests/test_dist.py b/distutils/tests/test_dist.py
index 59d165a4..ddfaf921 100644
--- a/distutils/tests/test_dist.py
+++ b/distutils/tests/test_dist.py
@@ -2,11 +2,10 @@
import os
import io
import sys
-import unittest
import warnings
import textwrap
-
-from unittest import mock
+import functools
+import unittest.mock as mock
import pytest
@@ -42,22 +41,18 @@ class TestDistribution(Distribution):
return self._config_files
+@pytest.fixture
+def clear_argv():
+ del sys.argv[1:]
+
+
+@support.combine_markers
@pytest.mark.usefixtures('save_env')
-class DistributionTestCase(
+@pytest.mark.usefixtures('save_argv')
+class TestDistributionBehavior(
support.LoggingSilencer,
support.TempdirManager,
- unittest.TestCase,
):
- def setUp(self):
- super(DistributionTestCase, self).setUp()
- self.argv = sys.argv, sys.argv[:]
- del sys.argv[1:]
-
- def tearDown(self):
- sys.argv = self.argv[0]
- sys.argv[:] = self.argv[1]
- super(DistributionTestCase, self).tearDown()
-
def create_distribution(self, configfiles=()):
d = TestDistribution()
d._config_files = configfiles
@@ -65,12 +60,12 @@ class DistributionTestCase(
d.parse_command_line()
return d
- def test_command_packages_unspecified(self):
+ def test_command_packages_unspecified(self, clear_argv):
sys.argv.append("build")
d = self.create_distribution()
- self.assertEqual(d.get_command_packages(), ["distutils.command"])
+ assert d.get_command_packages() == ["distutils.command"]
- def test_command_packages_cmdline(self):
+ def test_command_packages_cmdline(self, clear_argv):
from distutils.tests.test_dist import test_dist
sys.argv.extend(
@@ -83,21 +78,22 @@ class DistributionTestCase(
)
d = self.create_distribution()
# let's actually try to load our test command:
- self.assertEqual(
- d.get_command_packages(),
- ["distutils.command", "foo.bar", "distutils.tests"],
- )
+ assert d.get_command_packages() == [
+ "distutils.command",
+ "foo.bar",
+ "distutils.tests",
+ ]
cmd = d.get_command_obj("test_dist")
- self.assertIsInstance(cmd, test_dist)
- self.assertEqual(cmd.sample_option, "sometext")
+ assert isinstance(cmd, test_dist)
+ assert cmd.sample_option == "sometext"
- @unittest.skipIf(
+ @pytest.mark.skipif(
'distutils' not in Distribution.parse_config_files.__module__,
- 'Cannot test when virtualenv has monkey-patched Distribution.',
+ reason='Cannot test when virtualenv has monkey-patched Distribution',
)
- def test_venv_install_options(self):
+ def test_venv_install_options(self, request):
sys.argv.append("install")
- self.addCleanup(os.unlink, TESTFN)
+ request.addfinalizer(functools.partial(os.unlink, TESTFN))
fakepath = '/somedir'
@@ -144,23 +140,23 @@ class DistributionTestCase(
'root': option_tuple,
}
- self.assertEqual(
- sorted(d.command_options.get('install').keys()), sorted(result_dict.keys())
+ assert sorted(d.command_options.get('install').keys()) == sorted(
+ result_dict.keys()
)
for (key, value) in d.command_options.get('install').items():
- self.assertEqual(value, result_dict[key])
+ assert value == result_dict[key]
# Test case: In a Virtual Environment
with mock.patch.multiple(sys, prefix='/a', base_prefix='/b'):
d = self.create_distribution([TESTFN])
for key in result_dict.keys():
- self.assertNotIn(key, d.command_options.get('install', {}))
+ assert key not in d.command_options.get('install', {})
- def test_command_packages_configfile(self):
+ def test_command_packages_configfile(self, request, clear_argv):
sys.argv.append("build")
- self.addCleanup(os.unlink, TESTFN)
+ request.addfinalizer(functools.partial(os.unlink, TESTFN))
f = open(TESTFN, "w")
try:
print("[global]", file=f)
@@ -169,22 +165,20 @@ class DistributionTestCase(
f.close()
d = self.create_distribution([TESTFN])
- self.assertEqual(
- d.get_command_packages(), ["distutils.command", "foo.bar", "splat"]
- )
+ assert d.get_command_packages() == ["distutils.command", "foo.bar", "splat"]
# ensure command line overrides config:
sys.argv[1:] = ["--command-packages", "spork", "build"]
d = self.create_distribution([TESTFN])
- self.assertEqual(d.get_command_packages(), ["distutils.command", "spork"])
+ assert d.get_command_packages() == ["distutils.command", "spork"]
# Setting --command-packages to '' should cause the default to
# be used even if a config file specified something else:
sys.argv[1:] = ["--command-packages", "", "build"]
d = self.create_distribution([TESTFN])
- self.assertEqual(d.get_command_packages(), ["distutils.command"])
+ assert d.get_command_packages() == ["distutils.command"]
- def test_empty_options(self):
+ def test_empty_options(self, request):
# an empty options dictionary should not stay in the
# list of attributes
@@ -194,7 +188,9 @@ class DistributionTestCase(
def _warn(msg):
warns.append(msg)
- self.addCleanup(setattr, warnings, 'warn', warnings.warn)
+ request.addfinalizer(
+ functools.partial(setattr, warnings, 'warn', warnings.warn)
+ )
warnings.warn = _warn
dist = Distribution(
attrs={
@@ -206,8 +202,8 @@ class DistributionTestCase(
}
)
- self.assertEqual(len(warns), 0)
- self.assertNotIn('options', dir(dist))
+ assert len(warns) == 0
+ assert 'options' not in dir(dist)
def test_finalize_options(self):
attrs = {'keywords': 'one,two', 'platforms': 'one,two'}
@@ -216,32 +212,33 @@ class DistributionTestCase(
dist.finalize_options()
# finalize_option splits platforms and keywords
- self.assertEqual(dist.metadata.platforms, ['one', 'two'])
- self.assertEqual(dist.metadata.keywords, ['one', 'two'])
+ assert dist.metadata.platforms == ['one', 'two']
+ assert dist.metadata.keywords == ['one', 'two']
attrs = {'keywords': 'foo bar', 'platforms': 'foo bar'}
dist = Distribution(attrs=attrs)
dist.finalize_options()
- self.assertEqual(dist.metadata.platforms, ['foo bar'])
- self.assertEqual(dist.metadata.keywords, ['foo bar'])
+ assert dist.metadata.platforms == ['foo bar']
+ assert dist.metadata.keywords == ['foo bar']
def test_get_command_packages(self):
dist = Distribution()
- self.assertEqual(dist.command_packages, None)
+ assert dist.command_packages is None
cmds = dist.get_command_packages()
- self.assertEqual(cmds, ['distutils.command'])
- self.assertEqual(dist.command_packages, ['distutils.command'])
+ assert cmds == ['distutils.command']
+ assert dist.command_packages == ['distutils.command']
dist.command_packages = 'one,two'
cmds = dist.get_command_packages()
- self.assertEqual(cmds, ['distutils.command', 'one', 'two'])
+ assert cmds == ['distutils.command', 'one', 'two']
def test_announce(self):
# make sure the level is known
dist = Distribution()
args = ('ok',)
kwargs = {'level': 'ok2'}
- self.assertRaises(ValueError, dist.announce, args, kwargs)
+ with pytest.raises(ValueError):
+ dist.announce(args, kwargs)
def test_find_config_files_disable(self):
# Ticket #1180: Allow user to disable their home config file.
@@ -269,20 +266,12 @@ class DistributionTestCase(
os.path.expanduser = old_expander
# make sure --no-user-cfg disables the user cfg file
- self.assertEqual(len(all_files) - 1, len(files))
+ assert len(all_files) - 1 == len(files)
@pytest.mark.usefixtures('save_env')
-class MetadataTestCase(support.TempdirManager, unittest.TestCase):
- def setUp(self):
- super(MetadataTestCase, self).setUp()
- self.argv = sys.argv, sys.argv[:]
-
- def tearDown(self):
- sys.argv = self.argv[0]
- sys.argv[:] = self.argv[1]
- super(MetadataTestCase, self).tearDown()
-
+@pytest.mark.usefixtures('save_argv')
+class MetadataTestCase(support.TempdirManager):
def format_metadata(self, dist):
sio = io.StringIO()
dist.metadata.write_pkg_file(sio)
@@ -292,10 +281,10 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase):
attrs = {"name": "package", "version": "1.0"}
dist = Distribution(attrs)
meta = self.format_metadata(dist)
- self.assertIn("Metadata-Version: 1.0", meta)
- self.assertNotIn("provides:", meta.lower())
- self.assertNotIn("requires:", meta.lower())
- self.assertNotIn("obsoletes:", meta.lower())
+ assert "Metadata-Version: 1.0" in meta
+ assert "provides:" not in meta.lower()
+ assert "requires:" not in meta.lower()
+ assert "obsoletes:" not in meta.lower()
def test_provides(self):
attrs = {
@@ -304,19 +293,18 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase):
"provides": ["package", "package.sub"],
}
dist = Distribution(attrs)
- self.assertEqual(dist.metadata.get_provides(), ["package", "package.sub"])
- self.assertEqual(dist.get_provides(), ["package", "package.sub"])
+ assert dist.metadata.get_provides() == ["package", "package.sub"]
+ assert dist.get_provides() == ["package", "package.sub"]
meta = self.format_metadata(dist)
- self.assertIn("Metadata-Version: 1.1", meta)
- self.assertNotIn("requires:", meta.lower())
- self.assertNotIn("obsoletes:", meta.lower())
+ assert "Metadata-Version: 1.1" in meta
+ assert "requires:" not in meta.lower()
+ assert "obsoletes:" not in meta.lower()
def test_provides_illegal(self):
- self.assertRaises(
- ValueError,
- Distribution,
- {"name": "package", "version": "1.0", "provides": ["my.pkg (splat)"]},
- )
+ with pytest.raises(ValueError):
+ Distribution(
+ {"name": "package", "version": "1.0", "provides": ["my.pkg (splat)"]},
+ )
def test_requires(self):
attrs = {
@@ -325,26 +313,25 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase):
"requires": ["other", "another (==1.0)"],
}
dist = Distribution(attrs)
- self.assertEqual(dist.metadata.get_requires(), ["other", "another (==1.0)"])
- self.assertEqual(dist.get_requires(), ["other", "another (==1.0)"])
+ assert dist.metadata.get_requires() == ["other", "another (==1.0)"]
+ assert dist.get_requires() == ["other", "another (==1.0)"]
meta = self.format_metadata(dist)
- self.assertIn("Metadata-Version: 1.1", meta)
- self.assertNotIn("provides:", meta.lower())
- self.assertIn("Requires: other", meta)
- self.assertIn("Requires: another (==1.0)", meta)
- self.assertNotIn("obsoletes:", meta.lower())
+ assert "Metadata-Version: 1.1" in meta
+ assert "provides:" not in meta.lower()
+ assert "Requires: other" in meta
+ assert "Requires: another (==1.0)" in meta
+ assert "obsoletes:" not in meta.lower()
def test_requires_illegal(self):
- self.assertRaises(
- ValueError,
- Distribution,
- {"name": "package", "version": "1.0", "requires": ["my.pkg (splat)"]},
- )
+ with pytest.raises(ValueError):
+ Distribution(
+ {"name": "package", "version": "1.0", "requires": ["my.pkg (splat)"]},
+ )
def test_requires_to_list(self):
attrs = {"name": "package", "requires": iter(["other"])}
dist = Distribution(attrs)
- self.assertIsInstance(dist.metadata.requires, list)
+ assert isinstance(dist.metadata.requires, list)
def test_obsoletes(self):
attrs = {
@@ -353,26 +340,25 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase):
"obsoletes": ["other", "another (<1.0)"],
}
dist = Distribution(attrs)
- self.assertEqual(dist.metadata.get_obsoletes(), ["other", "another (<1.0)"])
- self.assertEqual(dist.get_obsoletes(), ["other", "another (<1.0)"])
+ assert dist.metadata.get_obsoletes() == ["other", "another (<1.0)"]
+ assert dist.get_obsoletes() == ["other", "another (<1.0)"]
meta = self.format_metadata(dist)
- self.assertIn("Metadata-Version: 1.1", meta)
- self.assertNotIn("provides:", meta.lower())
- self.assertNotIn("requires:", meta.lower())
- self.assertIn("Obsoletes: other", meta)
- self.assertIn("Obsoletes: another (<1.0)", meta)
+ assert "Metadata-Version: 1.1" in meta
+ assert "provides:" not in meta.lower()
+ assert "requires:" not in meta.lower()
+ assert "Obsoletes: other" in meta
+ assert "Obsoletes: another (<1.0)" in meta
def test_obsoletes_illegal(self):
- self.assertRaises(
- ValueError,
- Distribution,
- {"name": "package", "version": "1.0", "obsoletes": ["my.pkg (splat)"]},
- )
+ with pytest.raises(ValueError):
+ Distribution(
+ {"name": "package", "version": "1.0", "obsoletes": ["my.pkg (splat)"]},
+ )
def test_obsoletes_to_list(self):
attrs = {"name": "package", "obsoletes": iter(["other"])}
dist = Distribution(attrs)
- self.assertIsInstance(dist.metadata.obsoletes, list)
+ assert isinstance(dist.metadata.obsoletes, list)
def test_classifier(self):
attrs = {
@@ -381,11 +367,9 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase):
'classifiers': ['Programming Language :: Python :: 3'],
}
dist = Distribution(attrs)
- self.assertEqual(
- dist.get_classifiers(), ['Programming Language :: Python :: 3']
- )
+ assert dist.get_classifiers() == ['Programming Language :: Python :: 3']
meta = self.format_metadata(dist)
- self.assertIn('Metadata-Version: 1.1', meta)
+ assert 'Metadata-Version: 1.1' in meta
def test_classifier_invalid_type(self):
attrs = {
@@ -396,10 +380,10 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase):
with captured_stderr() as error:
d = Distribution(attrs)
# should have warning about passing a non-list
- self.assertIn('should be a list', error.getvalue())
+ assert 'should be a list' in error.getvalue()
# should be converted to a list
- self.assertIsInstance(d.metadata.classifiers, list)
- self.assertEqual(d.metadata.classifiers, list(attrs['classifiers']))
+ assert isinstance(d.metadata.classifiers, list)
+ assert d.metadata.classifiers == list(attrs['classifiers'])
def test_keywords(self):
attrs = {
@@ -408,7 +392,7 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase):
'keywords': ['spam', 'eggs', 'life of brian'],
}
dist = Distribution(attrs)
- self.assertEqual(dist.get_keywords(), ['spam', 'eggs', 'life of brian'])
+ assert dist.get_keywords() == ['spam', 'eggs', 'life of brian']
def test_keywords_invalid_type(self):
attrs = {
@@ -419,10 +403,10 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase):
with captured_stderr() as error:
d = Distribution(attrs)
# should have warning about passing a non-list
- self.assertIn('should be a list', error.getvalue())
+ assert 'should be a list' in error.getvalue()
# should be converted to a list
- self.assertIsInstance(d.metadata.keywords, list)
- self.assertEqual(d.metadata.keywords, list(attrs['keywords']))
+ assert isinstance(d.metadata.keywords, list)
+ assert d.metadata.keywords == list(attrs['keywords'])
def test_platforms(self):
attrs = {
@@ -431,7 +415,7 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase):
'platforms': ['GNU/Linux', 'Some Evil Platform'],
}
dist = Distribution(attrs)
- self.assertEqual(dist.get_platforms(), ['GNU/Linux', 'Some Evil Platform'])
+ assert dist.get_platforms() == ['GNU/Linux', 'Some Evil Platform']
def test_platforms_invalid_types(self):
attrs = {
@@ -442,10 +426,10 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase):
with captured_stderr() as error:
d = Distribution(attrs)
# should have warning about passing a non-list
- self.assertIn('should be a list', error.getvalue())
+ assert 'should be a list' in error.getvalue()
# should be converted to a list
- self.assertIsInstance(d.metadata.platforms, list)
- self.assertEqual(d.metadata.platforms, list(attrs['platforms']))
+ assert isinstance(d.metadata.platforms, list)
+ assert d.metadata.platforms == list(attrs['platforms'])
def test_download_url(self):
attrs = {
@@ -455,7 +439,7 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase):
}
dist = Distribution(attrs)
meta = self.format_metadata(dist)
- self.assertIn('Metadata-Version: 1.1', meta)
+ assert 'Metadata-Version: 1.1' in meta
def test_long_description(self):
long_desc = textwrap.dedent(
@@ -470,7 +454,7 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase):
dist = Distribution(attrs)
meta = self.format_metadata(dist)
meta = meta.replace('\n' + 8 * ' ', '\n')
- self.assertIn(long_desc, meta)
+ assert long_desc in meta
def test_custom_pydistutils(self):
# fixes #2166
@@ -495,15 +479,15 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase):
if sys.platform in ('linux', 'darwin'):
os.environ['HOME'] = temp_dir
files = dist.find_config_files()
- self.assertIn(user_filename, files)
+ assert user_filename in files
# win32-style
if sys.platform == 'win32':
# home drive should be found
os.environ['USERPROFILE'] = temp_dir
files = dist.find_config_files()
- self.assertIn(
- user_filename, files, '%r not found in %r' % (user_filename, files)
+ assert user_filename in files, '{!r} not found in {!r}'.format(
+ user_filename, files
)
finally:
os.remove(user_filename)
@@ -511,8 +495,8 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase):
def test_fix_help_options(self):
help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)]
fancy_options = fix_help_options(help_tuples)
- self.assertEqual(fancy_options[0], ('a', 'b', 'c'))
- self.assertEqual(fancy_options[1], (1, 2, 3))
+ assert fancy_options[0] == ('a', 'b', 'c')
+ assert fancy_options[1] == (1, 2, 3)
def test_show_help(self):
# smoke test, just makes sure some help is displayed
@@ -525,7 +509,7 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase):
dist.parse_command_line()
output = [line for line in s.getvalue().split('\n') if line.strip() != '']
- self.assertTrue(output)
+ assert output
def test_read_metadata(self):
attrs = {
@@ -547,11 +531,11 @@ class MetadataTestCase(support.TempdirManager, unittest.TestCase):
PKG_INFO.seek(0)
metadata.read_pkg_file(PKG_INFO)
- self.assertEqual(metadata.name, "package")
- self.assertEqual(metadata.version, "1.0")
- self.assertEqual(metadata.description, "xxx")
- self.assertEqual(metadata.download_url, 'http://example.com')
- self.assertEqual(metadata.keywords, ['one', 'two'])
- self.assertEqual(metadata.platforms, None)
- self.assertEqual(metadata.obsoletes, None)
- self.assertEqual(metadata.requires, ['foo'])
+ assert metadata.name == "package"
+ assert metadata.version == "1.0"
+ assert metadata.description == "xxx"
+ assert metadata.download_url == 'http://example.com'
+ assert metadata.keywords == ['one', 'two']
+ assert metadata.platforms is None
+ assert metadata.obsoletes is None
+ assert metadata.requires == ['foo']
diff --git a/distutils/tests/test_extension.py b/distutils/tests/test_extension.py
index bf573930..f86af073 100644
--- a/distutils/tests/test_extension.py
+++ b/distutils/tests/test_extension.py
@@ -1,14 +1,14 @@
"""Tests for distutils.extension."""
-import unittest
import os
import warnings
from distutils.extension import read_setup_file, Extension
from .py38compat import check_warnings
+import pytest
-class ExtensionTestCase(unittest.TestCase):
+class TestExtension:
def test_read_setup_file(self):
# trying to read a Setup file
# (sample extracted from the PyGame project)
@@ -57,20 +57,23 @@ class ExtensionTestCase(unittest.TestCase):
'transform',
]
- self.assertEqual(names, wanted)
+ assert names == wanted
def test_extension_init(self):
# the first argument, which is the name, must be a string
- self.assertRaises(AssertionError, Extension, 1, [])
+ with pytest.raises(AssertionError):
+ Extension(1, [])
ext = Extension('name', [])
- self.assertEqual(ext.name, 'name')
+ assert ext.name == 'name'
# the second argument, which is the list of files, must
# be a list of strings
- self.assertRaises(AssertionError, Extension, 'name', 'file')
- self.assertRaises(AssertionError, Extension, 'name', ['file', 1])
+ with pytest.raises(AssertionError):
+ Extension('name', 'file')
+ with pytest.raises(AssertionError):
+ Extension('name', ['file', 1])
ext = Extension('name', ['file1', 'file2'])
- self.assertEqual(ext.sources, ['file1', 'file2'])
+ assert ext.sources == ['file1', 'file2']
# others arguments have defaults
for attr in (
@@ -87,17 +90,15 @@ class ExtensionTestCase(unittest.TestCase):
'swig_opts',
'depends',
):
- self.assertEqual(getattr(ext, attr), [])
+ assert getattr(ext, attr) == []
- self.assertEqual(ext.language, None)
- self.assertEqual(ext.optional, None)
+ assert ext.language is None
+ assert ext.optional is None
# if there are unknown keyword options, warn about them
with check_warnings() as w:
warnings.simplefilter('always')
ext = Extension('name', ['file1', 'file2'], chic=True)
- self.assertEqual(len(w.warnings), 1)
- self.assertEqual(
- str(w.warnings[0].message), "Unknown Extension options: 'chic'"
- )
+ assert len(w.warnings) == 1
+ assert str(w.warnings[0].message) == "Unknown Extension options: 'chic'"
diff --git a/distutils/tests/test_file_util.py b/distutils/tests/test_file_util.py
index 6b333d5e..b2e83c52 100644
--- a/distutils/tests/test_file_util.py
+++ b/distutils/tests/test_file_util.py
@@ -1,37 +1,34 @@
"""Tests for distutils.file_util."""
-import unittest
import os
import errno
-from unittest.mock import patch
+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
+import pytest
-class FileUtilTestCase(support.TempdirManager, unittest.TestCase):
+@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 setUp(self):
- super(FileUtilTestCase, self).setUp()
- self._logs = []
- self.old_log = log.info
- log.info = self._log
- 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')
-
- def tearDown(self):
- log.info = self.old_log
- super(FileUtilTestCase, self).tearDown()
-
def test_move_file_verbosity(self):
f = open(self.source, 'w')
try:
@@ -41,14 +38,14 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase):
move_file(self.source, self.target, verbose=0)
wanted = []
- self.assertEqual(self._logs, wanted)
+ assert self._logs == wanted
# back to original state
move_file(self.target, self.source, verbose=0)
move_file(self.source, self.target, verbose=1)
- wanted = ['moving %s -> %s' % (self.source, self.target)]
- self.assertEqual(self._logs, wanted)
+ wanted = ['moving {} -> {}'.format(self.source, self.target)]
+ assert self._logs == wanted
# back to original state
move_file(self.target, self.source, verbose=0)
@@ -57,12 +54,12 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase):
# now the target is a dir
os.mkdir(self.target_dir)
move_file(self.source, self.target_dir, verbose=1)
- wanted = ['moving %s -> %s' % (self.source, self.target_dir)]
- self.assertEqual(self._logs, wanted)
+ wanted = ['moving {} -> {}'.format(self.source, self.target_dir)]
+ assert self._logs == wanted
def test_move_file_exception_unpacking_rename(self):
# see issue 22182
- with patch("os.rename", side_effect=OSError("wrong", 1)), self.assertRaises(
+ with mock.patch("os.rename", side_effect=OSError("wrong", 1)), pytest.raises(
DistutilsFileError
):
with open(self.source, 'w') as fobj:
@@ -71,9 +68,11 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase):
def test_move_file_exception_unpacking_unlink(self):
# see issue 22182
- with patch("os.rename", side_effect=OSError(errno.EXDEV, "wrong")), patch(
- "os.unlink", side_effect=OSError("wrong", 1)
- ), self.assertRaises(DistutilsFileError):
+ with mock.patch(
+ "os.rename", side_effect=OSError(errno.EXDEV, "wrong")
+ ), mock.patch("os.unlink", side_effect=OSError("wrong", 1)), pytest.raises(
+ DistutilsFileError
+ ):
with open(self.source, 'w') as fobj:
fobj.write('spam eggs')
move_file(self.source, self.target, verbose=0)
@@ -93,10 +92,10 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase):
copy_file(self.source, self.target, link='hard')
st2 = os.stat(self.source)
st3 = os.stat(self.target)
- self.assertTrue(os.path.samestat(st, st2), (st, st2))
- self.assertTrue(os.path.samestat(st2, st3), (st2, st3))
- with open(self.source, 'r') as f:
- self.assertEqual(f.read(), 'some content')
+ assert os.path.samestat(st, st2), (st, st2)
+ assert os.path.samestat(st2, st3), (st2, st3)
+ with open(self.source) as f:
+ assert f.read() == 'some content'
def test_copy_file_hard_link_failure(self):
# If hard linking fails, copy_file() falls back on copying file
@@ -105,12 +104,12 @@ class FileUtilTestCase(support.TempdirManager, unittest.TestCase):
with open(self.source, 'w') as f:
f.write('some content')
st = os.stat(self.source)
- with patch("os.link", side_effect=OSError(0, "linking unsupported")):
+ with mock.patch("os.link", side_effect=OSError(0, "linking unsupported")):
copy_file(self.source, self.target, link='hard')
st2 = os.stat(self.source)
st3 = os.stat(self.target)
- self.assertTrue(os.path.samestat(st, st2), (st, st2))
- self.assertFalse(os.path.samestat(st2, st3), (st2, st3))
+ assert os.path.samestat(st, st2), (st, st2)
+ assert not os.path.samestat(st2, st3), (st2, st3)
for fn in (self.source, self.target):
- with open(fn, 'r') as f:
- self.assertEqual(f.read(), 'some content')
+ with open(fn) as f:
+ assert f.read() == 'some content'
diff --git a/distutils/tests/test_filelist.py b/distutils/tests/test_filelist.py
index 0673139e..26071820 100644
--- a/distutils/tests/test_filelist.py
+++ b/distutils/tests/test_filelist.py
@@ -1,7 +1,6 @@
"""Tests for distutils.filelist."""
import os
import re
-import unittest
from distutils import debug
from distutils.log import WARN
from distutils.errors import DistutilsTemplateError
@@ -12,6 +11,7 @@ from test.support import captured_stdout
from distutils.tests import support
from . import py38compat as os_helper
+import pytest
MANIFEST_IN = """\
@@ -35,13 +35,13 @@ def make_local_path(s):
return s.replace('/', os.sep)
-class FileListTestCase(support.LoggingSilencer, unittest.TestCase):
+class TestFileList(support.LoggingSilencer):
def assertNoWarnings(self):
- self.assertEqual(self.get_logs(WARN), [])
+ assert self.get_logs(WARN) == []
self.clear_logs()
def assertWarnings(self):
- self.assertGreater(len(self.get_logs(WARN)), 0)
+ assert len(self.get_logs(WARN)) > 0
self.clear_logs()
def test_glob_to_re(self):
@@ -61,7 +61,7 @@ class FileListTestCase(support.LoggingSilencer, unittest.TestCase):
(r'foo\\??', r'(?s:foo\\\\[^%(sep)s][^%(sep)s])\Z'),
):
regex = regex % {'sep': sep}
- self.assertEqual(glob_to_re(glob), regex)
+ assert glob_to_re(glob) == regex
def test_process_template_line(self):
# testing all MANIFEST.in template patterns
@@ -106,19 +106,19 @@ class FileListTestCase(support.LoggingSilencer, unittest.TestCase):
mlp('dir/dir2/graft2'),
]
- self.assertEqual(file_list.files, wanted)
+ assert file_list.files == wanted
def test_debug_print(self):
file_list = FileList()
with captured_stdout() as stdout:
file_list.debug_print('xxx')
- self.assertEqual(stdout.getvalue(), '')
+ assert stdout.getvalue() == ''
debug.DEBUG = True
try:
with captured_stdout() as stdout:
file_list.debug_print('xxx')
- self.assertEqual(stdout.getvalue(), 'xxx\n')
+ assert stdout.getvalue() == 'xxx\n'
finally:
debug.DEBUG = False
@@ -126,7 +126,7 @@ class FileListTestCase(support.LoggingSilencer, unittest.TestCase):
file_list = FileList()
files = ['a', 'b', 'c']
file_list.set_allfiles(files)
- self.assertEqual(file_list.allfiles, files)
+ assert file_list.allfiles == files
def test_remove_duplicates(self):
file_list = FileList()
@@ -134,61 +134,57 @@ class FileListTestCase(support.LoggingSilencer, unittest.TestCase):
# files must be sorted beforehand (sdist does it)
file_list.sort()
file_list.remove_duplicates()
- self.assertEqual(file_list.files, ['a', 'b', 'c', 'g'])
+ assert file_list.files == ['a', 'b', 'c', 'g']
def test_translate_pattern(self):
# not regex
- self.assertTrue(
- hasattr(translate_pattern('a', anchor=True, is_regex=False), 'search')
- )
+ assert hasattr(translate_pattern('a', anchor=True, is_regex=False), 'search')
# is a regex
regex = re.compile('a')
- self.assertEqual(translate_pattern(regex, anchor=True, is_regex=True), regex)
+ assert translate_pattern(regex, anchor=True, is_regex=True) == regex
# plain string flagged as regex
- self.assertTrue(
- hasattr(translate_pattern('a', anchor=True, is_regex=True), 'search')
- )
+ assert hasattr(translate_pattern('a', anchor=True, is_regex=True), 'search')
# glob support
- self.assertTrue(
- translate_pattern('*.py', anchor=True, is_regex=False).search('filelist.py')
+ assert translate_pattern('*.py', anchor=True, is_regex=False).search(
+ 'filelist.py'
)
def test_exclude_pattern(self):
# return False if no match
file_list = FileList()
- self.assertFalse(file_list.exclude_pattern('*.py'))
+ assert not file_list.exclude_pattern('*.py')
# return True if files match
file_list = FileList()
file_list.files = ['a.py', 'b.py']
- self.assertTrue(file_list.exclude_pattern('*.py'))
+ assert file_list.exclude_pattern('*.py')
# test excludes
file_list = FileList()
file_list.files = ['a.py', 'a.txt']
file_list.exclude_pattern('*.py')
- self.assertEqual(file_list.files, ['a.txt'])
+ assert file_list.files == ['a.txt']
def test_include_pattern(self):
# return False if no match
file_list = FileList()
file_list.set_allfiles([])
- self.assertFalse(file_list.include_pattern('*.py'))
+ assert not file_list.include_pattern('*.py')
# return True if files match
file_list = FileList()
file_list.set_allfiles(['a.py', 'b.txt'])
- self.assertTrue(file_list.include_pattern('*.py'))
+ assert file_list.include_pattern('*.py')
# test * matches all files
file_list = FileList()
- self.assertIsNone(file_list.allfiles)
+ assert file_list.allfiles is None
file_list.set_allfiles(['a.py', 'b.txt'])
file_list.include_pattern('*')
- self.assertEqual(file_list.allfiles, ['a.py', 'b.txt'])
+ assert file_list.allfiles == ['a.py', 'b.txt']
def test_process_template(self):
mlp = make_local_path
@@ -205,20 +201,19 @@ class FileListTestCase(support.LoggingSilencer, unittest.TestCase):
'prune',
'blarg',
):
- self.assertRaises(
- DistutilsTemplateError, file_list.process_template_line, action
- )
+ with pytest.raises(DistutilsTemplateError):
+ file_list.process_template_line(action)
# include
file_list = FileList()
file_list.set_allfiles(['a.py', 'b.txt', mlp('d/c.py')])
file_list.process_template_line('include *.py')
- self.assertEqual(file_list.files, ['a.py'])
+ assert file_list.files == ['a.py']
self.assertNoWarnings()
file_list.process_template_line('include *.rb')
- self.assertEqual(file_list.files, ['a.py'])
+ assert file_list.files == ['a.py']
self.assertWarnings()
# exclude
@@ -226,11 +221,11 @@ class FileListTestCase(support.LoggingSilencer, unittest.TestCase):
file_list.files = ['a.py', 'b.txt', mlp('d/c.py')]
file_list.process_template_line('exclude *.py')
- self.assertEqual(file_list.files, ['b.txt', mlp('d/c.py')])
+ assert file_list.files == ['b.txt', mlp('d/c.py')]
self.assertNoWarnings()
file_list.process_template_line('exclude *.rb')
- self.assertEqual(file_list.files, ['b.txt', mlp('d/c.py')])
+ assert file_list.files == ['b.txt', mlp('d/c.py')]
self.assertWarnings()
# global-include
@@ -238,11 +233,11 @@ class FileListTestCase(support.LoggingSilencer, unittest.TestCase):
file_list.set_allfiles(['a.py', 'b.txt', mlp('d/c.py')])
file_list.process_template_line('global-include *.py')
- self.assertEqual(file_list.files, ['a.py', mlp('d/c.py')])
+ assert file_list.files == ['a.py', mlp('d/c.py')]
self.assertNoWarnings()
file_list.process_template_line('global-include *.rb')
- self.assertEqual(file_list.files, ['a.py', mlp('d/c.py')])
+ assert file_list.files == ['a.py', mlp('d/c.py')]
self.assertWarnings()
# global-exclude
@@ -250,11 +245,11 @@ class FileListTestCase(support.LoggingSilencer, unittest.TestCase):
file_list.files = ['a.py', 'b.txt', mlp('d/c.py')]
file_list.process_template_line('global-exclude *.py')
- self.assertEqual(file_list.files, ['b.txt'])
+ assert file_list.files == ['b.txt']
self.assertNoWarnings()
file_list.process_template_line('global-exclude *.rb')
- self.assertEqual(file_list.files, ['b.txt'])
+ assert file_list.files == ['b.txt']
self.assertWarnings()
# recursive-include
@@ -262,11 +257,11 @@ class FileListTestCase(support.LoggingSilencer, unittest.TestCase):
file_list.set_allfiles(['a.py', mlp('d/b.py'), mlp('d/c.txt'), mlp('d/d/e.py')])
file_list.process_template_line('recursive-include d *.py')
- self.assertEqual(file_list.files, [mlp('d/b.py'), mlp('d/d/e.py')])
+ assert file_list.files == [mlp('d/b.py'), mlp('d/d/e.py')]
self.assertNoWarnings()
file_list.process_template_line('recursive-include e *.py')
- self.assertEqual(file_list.files, [mlp('d/b.py'), mlp('d/d/e.py')])
+ assert file_list.files == [mlp('d/b.py'), mlp('d/d/e.py')]
self.assertWarnings()
# recursive-exclude
@@ -274,11 +269,11 @@ class FileListTestCase(support.LoggingSilencer, unittest.TestCase):
file_list.files = ['a.py', mlp('d/b.py'), mlp('d/c.txt'), mlp('d/d/e.py')]
file_list.process_template_line('recursive-exclude d *.py')
- self.assertEqual(file_list.files, ['a.py', mlp('d/c.txt')])
+ assert file_list.files == ['a.py', mlp('d/c.txt')]
self.assertNoWarnings()
file_list.process_template_line('recursive-exclude e *.py')
- self.assertEqual(file_list.files, ['a.py', mlp('d/c.txt')])
+ assert file_list.files == ['a.py', mlp('d/c.txt')]
self.assertWarnings()
# graft
@@ -286,11 +281,11 @@ class FileListTestCase(support.LoggingSilencer, unittest.TestCase):
file_list.set_allfiles(['a.py', mlp('d/b.py'), mlp('d/d/e.py'), mlp('f/f.py')])
file_list.process_template_line('graft d')
- self.assertEqual(file_list.files, [mlp('d/b.py'), mlp('d/d/e.py')])
+ assert file_list.files == [mlp('d/b.py'), mlp('d/d/e.py')]
self.assertNoWarnings()
file_list.process_template_line('graft e')
- self.assertEqual(file_list.files, [mlp('d/b.py'), mlp('d/d/e.py')])
+ assert file_list.files == [mlp('d/b.py'), mlp('d/d/e.py')]
self.assertWarnings()
# prune
@@ -298,20 +293,20 @@ class FileListTestCase(support.LoggingSilencer, unittest.TestCase):
file_list.files = ['a.py', mlp('d/b.py'), mlp('d/d/e.py'), mlp('f/f.py')]
file_list.process_template_line('prune d')
- self.assertEqual(file_list.files, ['a.py', mlp('f/f.py')])
+ assert file_list.files == ['a.py', mlp('f/f.py')]
self.assertNoWarnings()
file_list.process_template_line('prune e')
- self.assertEqual(file_list.files, ['a.py', mlp('f/f.py')])
+ assert file_list.files == ['a.py', mlp('f/f.py')]
self.assertWarnings()
-class FindAllTestCase(unittest.TestCase):
+class TestFindAll:
@os_helper.skip_unless_symlink
def test_missing_symlink(self):
with os_helper.temp_cwd():
os.symlink('foo', 'bar')
- self.assertEqual(filelist.findall(), [])
+ assert filelist.findall() == []
def test_basic_discovery(self):
"""
@@ -327,7 +322,7 @@ class FindAllTestCase(unittest.TestCase):
file2 = os.path.join('bar', 'file2.txt')
os_helper.create_empty_file(file2)
expected = [file2, file1]
- self.assertEqual(sorted(filelist.findall()), expected)
+ assert sorted(filelist.findall()) == expected
def test_non_local_discovery(self):
"""
@@ -338,7 +333,7 @@ class FindAllTestCase(unittest.TestCase):
file1 = os.path.join(temp_dir, 'file1.txt')
os_helper.create_empty_file(file1)
expected = [file1]
- self.assertEqual(filelist.findall(temp_dir), expected)
+ assert filelist.findall(temp_dir) == expected
@os_helper.skip_unless_symlink
def test_symlink_loop(self):
diff --git a/distutils/tests/test_install.py b/distutils/tests/test_install.py
index 32ee02c7..32a18b2f 100644
--- a/distutils/tests/test_install.py
+++ b/distutils/tests/test_install.py
@@ -2,8 +2,8 @@
import os
import sys
-import unittest
import site
+import pathlib
from test.support import captured_stdout
@@ -26,11 +26,11 @@ def _make_ext_name(modname):
return modname + sysconfig.get_config_var('EXT_SUFFIX')
+@support.combine_markers
@pytest.mark.usefixtures('save_env')
-class InstallTestCase(
+class TestInstall(
support.TempdirManager,
support.LoggingSilencer,
- unittest.TestCase,
):
@pytest.mark.xfail(
'platform.system() == "Windows" and sys.version_info > (3, 11)',
@@ -55,13 +55,13 @@ class InstallTestCase(
cmd.home = destination
cmd.ensure_finalized()
- self.assertEqual(cmd.install_base, destination)
- self.assertEqual(cmd.install_platbase, destination)
+ assert cmd.install_base == destination
+ assert cmd.install_platbase == destination
def check_path(got, expected):
got = os.path.normpath(got)
expected = os.path.normpath(expected)
- self.assertEqual(got, expected)
+ assert got == expected
impl_name = sys.implementation.name.replace("cpython", "python")
libdir = os.path.join(destination, "lib", impl_name)
@@ -77,76 +77,60 @@ class InstallTestCase(
check_path(cmd.install_scripts, os.path.join(destination, "bin"))
check_path(cmd.install_data, destination)
- def test_user_site(self):
+ def test_user_site(self, monkeypatch):
# test install with --user
# preparing the environment for the test
- self.old_user_base = site.USER_BASE
- self.old_user_site = site.USER_SITE
self.tmpdir = self.mkdtemp()
- self.user_base = os.path.join(self.tmpdir, 'B')
- self.user_site = os.path.join(self.tmpdir, 'S')
- site.USER_BASE = self.user_base
- site.USER_SITE = self.user_site
- install_module.USER_BASE = self.user_base
- install_module.USER_SITE = self.user_site
+ orig_site = site.USER_SITE
+ orig_base = site.USER_BASE
+ monkeypatch.setattr(site, 'USER_BASE', os.path.join(self.tmpdir, 'B'))
+ monkeypatch.setattr(site, 'USER_SITE', os.path.join(self.tmpdir, 'S'))
+ monkeypatch.setattr(install_module, 'USER_BASE', site.USER_BASE)
+ monkeypatch.setattr(install_module, 'USER_SITE', site.USER_SITE)
def _expanduser(path):
if path.startswith('~'):
return os.path.normpath(self.tmpdir + path[1:])
return path
- self.old_expand = os.path.expanduser
- os.path.expanduser = _expanduser
-
- def cleanup():
- site.USER_BASE = self.old_user_base
- site.USER_SITE = self.old_user_site
- install_module.USER_BASE = self.old_user_base
- install_module.USER_SITE = self.old_user_site
- os.path.expanduser = self.old_expand
-
- self.addCleanup(cleanup)
+ monkeypatch.setattr(os.path, 'expanduser', _expanduser)
for key in ('nt_user', 'posix_user'):
- self.assertIn(key, INSTALL_SCHEMES)
+ assert key in INSTALL_SCHEMES
dist = Distribution({'name': 'xx'})
cmd = install(dist)
# making sure the user option is there
options = [name for name, short, lable in cmd.user_options]
- self.assertIn('user', options)
+ assert 'user' in options
# setting a value
cmd.user = 1
# user base and site shouldn't be created yet
- self.assertFalse(os.path.exists(self.user_base))
- self.assertFalse(os.path.exists(self.user_site))
+ assert not os.path.exists(site.USER_BASE)
+ assert not os.path.exists(site.USER_SITE)
# let's run finalize
cmd.ensure_finalized()
# now they should
- self.assertTrue(os.path.exists(self.user_base))
- self.assertTrue(os.path.exists(self.user_site))
+ assert os.path.exists(site.USER_BASE)
+ assert os.path.exists(site.USER_SITE)
- self.assertIn('userbase', cmd.config_vars)
- self.assertIn('usersite', cmd.config_vars)
+ assert 'userbase' in cmd.config_vars
+ assert 'usersite' in cmd.config_vars
- actual_headers = os.path.relpath(cmd.install_headers, self.user_base)
+ actual_headers = os.path.relpath(cmd.install_headers, site.USER_BASE)
if os.name == 'nt':
- site_path = os.path.relpath(
- os.path.dirname(self.old_user_site), self.old_user_base
- )
+ site_path = os.path.relpath(os.path.dirname(orig_site), orig_base)
include = os.path.join(site_path, 'Include')
else:
include = sysconfig.get_python_inc(0, '')
expect_headers = os.path.join(include, 'xx')
- self.assertEqual(
- os.path.normcase(actual_headers), os.path.normcase(expect_headers)
- )
+ assert os.path.normcase(actual_headers) == os.path.normcase(expect_headers)
def test_handle_extra_path(self):
dist = Distribution({'name': 'xx', 'extra_path': 'path,dirs'})
@@ -154,27 +138,28 @@ class InstallTestCase(
# two elements
cmd.handle_extra_path()
- self.assertEqual(cmd.extra_path, ['path', 'dirs'])
- self.assertEqual(cmd.extra_dirs, 'dirs')
- self.assertEqual(cmd.path_file, 'path')
+ assert cmd.extra_path == ['path', 'dirs']
+ assert cmd.extra_dirs == 'dirs'
+ assert cmd.path_file == 'path'
# one element
cmd.extra_path = ['path']
cmd.handle_extra_path()
- self.assertEqual(cmd.extra_path, ['path'])
- self.assertEqual(cmd.extra_dirs, 'path')
- self.assertEqual(cmd.path_file, 'path')
+ assert cmd.extra_path == ['path']
+ assert cmd.extra_dirs == 'path'
+ assert cmd.path_file == 'path'
# none
dist.extra_path = cmd.extra_path = None
cmd.handle_extra_path()
- self.assertEqual(cmd.extra_path, None)
- self.assertEqual(cmd.extra_dirs, '')
- self.assertEqual(cmd.path_file, None)
+ assert cmd.extra_path is None
+ assert cmd.extra_dirs == ''
+ assert cmd.path_file is None
# three elements (no way !)
cmd.extra_path = 'path,dirs,again'
- self.assertRaises(DistutilsOptionError, cmd.handle_extra_path)
+ with pytest.raises(DistutilsOptionError):
+ cmd.handle_extra_path()
def test_finalize_options(self):
dist = Distribution({'name': 'xx'})
@@ -184,18 +169,21 @@ class InstallTestCase(
# install-base/install-platbase -- not both
cmd.prefix = 'prefix'
cmd.install_base = 'base'
- self.assertRaises(DistutilsOptionError, cmd.finalize_options)
+ with pytest.raises(DistutilsOptionError):
+ cmd.finalize_options()
# must supply either home or prefix/exec-prefix -- not both
cmd.install_base = None
cmd.home = 'home'
- self.assertRaises(DistutilsOptionError, cmd.finalize_options)
+ with pytest.raises(DistutilsOptionError):
+ cmd.finalize_options()
# can't combine user with prefix/exec_prefix/home or
# install_(plat)base
cmd.prefix = None
cmd.user = 'user'
- self.assertRaises(DistutilsOptionError, cmd.finalize_options)
+ with pytest.raises(DistutilsOptionError):
+ cmd.finalize_options()
def test_record(self):
install_dir = self.mkdtemp()
@@ -224,12 +212,12 @@ class InstallTestCase(
'sayhi',
'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2],
]
- self.assertEqual(found, expected)
+ assert found == expected
def test_record_extensions(self):
cmd = test_support.missing_compiler_executable()
if cmd is not None:
- self.skipTest('The %r command is not found' % cmd)
+ pytest.skip('The %r command is not found' % cmd)
install_dir = self.mkdtemp()
project_dir, dist = self.create_dist(
ext_modules=[Extension('xx', ['xxmodule.c'])]
@@ -249,18 +237,14 @@ class InstallTestCase(
cmd.ensure_finalized()
cmd.run()
- f = open(cmd.record)
- try:
- content = f.read()
- finally:
- f.close()
+ content = pathlib.Path(cmd.record).read_text()
found = [os.path.basename(line) for line in content.splitlines()]
expected = [
_make_ext_name('xx'),
'UNKNOWN-0.0.0-py%s.%s.egg-info' % sys.version_info[:2],
]
- self.assertEqual(found, expected)
+ assert found == expected
def test_debug_mode(self):
# this covers the code called when DEBUG is set
@@ -271,4 +255,4 @@ class InstallTestCase(
self.test_record()
finally:
install_module.DEBUG = False
- self.assertGreater(len(self.logs), old_logs_len)
+ assert len(self.logs) > old_logs_len
diff --git a/distutils/tests/test_install_data.py b/distutils/tests/test_install_data.py
index a66e5d40..f77c790f 100644
--- a/distutils/tests/test_install_data.py
+++ b/distutils/tests/test_install_data.py
@@ -1,6 +1,5 @@
"""Tests for distutils.command.install_data."""
import os
-import unittest
import pytest
@@ -9,10 +8,9 @@ from distutils.tests import support
@pytest.mark.usefixtures('save_env')
-class InstallDataTestCase(
+class TestInstallData(
support.TempdirManager,
support.LoggingSilencer,
- unittest.TestCase,
):
def test_simple_run(self):
pkg_dir, dist = self.create_dist()
@@ -29,18 +27,18 @@ class InstallDataTestCase(
self.write_file(two, 'xxx')
cmd.data_files = [one, (inst2, [two])]
- self.assertEqual(cmd.get_inputs(), [one, (inst2, [two])])
+ assert cmd.get_inputs() == [one, (inst2, [two])]
# let's run the command
cmd.ensure_finalized()
cmd.run()
# let's check the result
- self.assertEqual(len(cmd.get_outputs()), 2)
+ assert len(cmd.get_outputs()) == 2
rtwo = os.path.split(two)[-1]
- self.assertTrue(os.path.exists(os.path.join(inst2, rtwo)))
+ assert os.path.exists(os.path.join(inst2, rtwo))
rone = os.path.split(one)[-1]
- self.assertTrue(os.path.exists(os.path.join(inst, rone)))
+ assert os.path.exists(os.path.join(inst, rone))
cmd.outfiles = []
# let's try with warn_dir one
@@ -49,9 +47,9 @@ class InstallDataTestCase(
cmd.run()
# let's check the result
- self.assertEqual(len(cmd.get_outputs()), 2)
- self.assertTrue(os.path.exists(os.path.join(inst2, rtwo)))
- self.assertTrue(os.path.exists(os.path.join(inst, rone)))
+ assert len(cmd.get_outputs()) == 2
+ assert os.path.exists(os.path.join(inst2, rtwo))
+ assert os.path.exists(os.path.join(inst, rone))
cmd.outfiles = []
# now using root and empty dir
@@ -64,6 +62,6 @@ class InstallDataTestCase(
cmd.run()
# let's check the result
- self.assertEqual(len(cmd.get_outputs()), 4)
- self.assertTrue(os.path.exists(os.path.join(inst2, rtwo)))
- self.assertTrue(os.path.exists(os.path.join(inst, rone)))
+ assert len(cmd.get_outputs()) == 4
+ assert os.path.exists(os.path.join(inst2, rtwo))
+ assert os.path.exists(os.path.join(inst, rone))
diff --git a/distutils/tests/test_install_headers.py b/distutils/tests/test_install_headers.py
index 9029f7e4..7594f5af 100644
--- a/distutils/tests/test_install_headers.py
+++ b/distutils/tests/test_install_headers.py
@@ -1,6 +1,5 @@
"""Tests for distutils.command.install_headers."""
import os
-import unittest
import pytest
@@ -9,10 +8,9 @@ from distutils.tests import support
@pytest.mark.usefixtures('save_env')
-class InstallHeadersTestCase(
+class TestInstallHeaders(
support.TempdirManager,
support.LoggingSilencer,
- unittest.TestCase,
):
def test_simple_run(self):
# we have two headers
@@ -25,7 +23,7 @@ class InstallHeadersTestCase(
pkg_dir, dist = self.create_dist(headers=headers)
cmd = install_headers(dist)
- self.assertEqual(cmd.get_inputs(), headers)
+ assert cmd.get_inputs() == headers
# let's run the command
cmd.install_dir = os.path.join(pkg_dir, 'inst')
@@ -33,4 +31,4 @@ class InstallHeadersTestCase(
cmd.run()
# let's check the results
- self.assertEqual(len(cmd.get_outputs()), 2)
+ assert len(cmd.get_outputs()) == 2
diff --git a/distutils/tests/test_install_lib.py b/distutils/tests/test_install_lib.py
index cebc88e7..a654a66a 100644
--- a/distutils/tests/test_install_lib.py
+++ b/distutils/tests/test_install_lib.py
@@ -2,7 +2,6 @@
import sys
import os
import importlib.util
-import unittest
import pytest
@@ -12,31 +11,33 @@ from distutils.tests import support
from distutils.errors import DistutilsOptionError
+@support.combine_markers
@pytest.mark.usefixtures('save_env')
-class InstallLibTestCase(
+class TestInstallLib(
support.TempdirManager,
support.LoggingSilencer,
- unittest.TestCase,
):
def test_finalize_options(self):
dist = self.create_dist()[1]
cmd = install_lib(dist)
cmd.finalize_options()
- self.assertEqual(cmd.compile, 1)
- self.assertEqual(cmd.optimize, 0)
+ assert cmd.compile == 1
+ assert cmd.optimize == 0
# optimize must be 0, 1, or 2
cmd.optimize = 'foo'
- self.assertRaises(DistutilsOptionError, cmd.finalize_options)
+ with pytest.raises(DistutilsOptionError):
+ cmd.finalize_options()
cmd.optimize = '4'
- self.assertRaises(DistutilsOptionError, cmd.finalize_options)
+ with pytest.raises(DistutilsOptionError):
+ cmd.finalize_options()
cmd.optimize = '2'
cmd.finalize_options()
- self.assertEqual(cmd.optimize, 2)
+ assert cmd.optimize == 2
- @unittest.skipIf(sys.dont_write_bytecode, 'byte-compile disabled')
+ @pytest.mark.skipif('sys.dont_write_bytecode')
def test_byte_compile(self):
project_dir, dist = self.create_dist()
os.chdir(project_dir)
@@ -50,8 +51,8 @@ class InstallLibTestCase(
pyc_opt_file = importlib.util.cache_from_source(
'foo.py', optimization=cmd.optimize
)
- self.assertTrue(os.path.exists(pyc_file))
- self.assertTrue(os.path.exists(pyc_opt_file))
+ assert os.path.exists(pyc_file)
+ assert os.path.exists(pyc_opt_file)
def test_get_outputs(self):
project_dir, dist = self.create_dist()
@@ -71,7 +72,7 @@ class InstallLibTestCase(
# get_outputs should return 4 elements: spam/__init__.py and .pyc,
# foo.import-tag-abiflags.so / foo.pyd
outputs = cmd.get_outputs()
- self.assertEqual(len(outputs), 4, outputs)
+ assert len(outputs) == 4, outputs
def test_get_inputs(self):
project_dir, dist = self.create_dist()
@@ -91,7 +92,7 @@ class InstallLibTestCase(
# get_inputs should return 2 elements: spam/__init__.py and
# foo.import-tag-abiflags.so / foo.pyd
inputs = cmd.get_inputs()
- self.assertEqual(len(inputs), 2, inputs)
+ assert len(inputs) == 2, inputs
def test_dont_write_bytecode(self):
# makes sure byte_compile is not used
@@ -107,4 +108,4 @@ class InstallLibTestCase(
finally:
sys.dont_write_bytecode = old_dont_write_bytecode
- self.assertIn('byte-compiling is disabled', self.logs[0][1] % self.logs[0][2])
+ assert 'byte-compiling is disabled' in self.logs[0][1] % self.logs[0][2]
diff --git a/distutils/tests/test_install_scripts.py b/distutils/tests/test_install_scripts.py
index 2b19d736..0d17f11b 100644
--- a/distutils/tests/test_install_scripts.py
+++ b/distutils/tests/test_install_scripts.py
@@ -1,7 +1,6 @@
"""Tests for distutils.command.install_scripts."""
import os
-import unittest
from distutils.command.install_scripts import install_scripts
from distutils.core import Distribution
@@ -9,9 +8,7 @@ from distutils.core import Distribution
from distutils.tests import support
-class InstallScriptsTestCase(
- support.TempdirManager, support.LoggingSilencer, unittest.TestCase
-):
+class TestInstallScripts(support.TempdirManager, support.LoggingSilencer):
def test_default_settings(self):
dist = Distribution()
dist.command_obj["build"] = support.DummyCommand(build_scripts="/foo/bar")
@@ -21,17 +18,17 @@ class InstallScriptsTestCase(
skip_build=1,
)
cmd = install_scripts(dist)
- self.assertFalse(cmd.force)
- self.assertFalse(cmd.skip_build)
- self.assertIsNone(cmd.build_dir)
- self.assertIsNone(cmd.install_dir)
+ assert not cmd.force
+ assert not cmd.skip_build
+ assert cmd.build_dir is None
+ assert cmd.install_dir is None
cmd.finalize_options()
- self.assertTrue(cmd.force)
- self.assertTrue(cmd.skip_build)
- self.assertEqual(cmd.build_dir, "/foo/bar")
- self.assertEqual(cmd.install_dir, "/splat/funk")
+ assert cmd.force
+ assert cmd.skip_build
+ assert cmd.build_dir == "/foo/bar"
+ assert cmd.install_dir == "/splat/funk"
def test_installation(self):
source = self.mkdtemp()
@@ -75,4 +72,4 @@ class InstallScriptsTestCase(
installed = os.listdir(target)
for name in expected:
- self.assertIn(name, installed)
+ assert name in installed
diff --git a/distutils/tests/test_log.py b/distutils/tests/test_log.py
index 4a5c5a05..7aeee405 100644
--- a/distutils/tests/test_log.py
+++ b/distutils/tests/test_log.py
@@ -2,52 +2,51 @@
import io
import sys
-import unittest
from test.support import swap_attr
+import pytest
+
from distutils import log
-class TestLog(unittest.TestCase):
- def test_non_ascii(self):
- # Issues #8663, #34421: test that non-encodable text is escaped with
- # backslashreplace error handler and encodable non-ASCII text is
- # output as is.
- for errors in (
+class TestLog:
+ @pytest.mark.parametrize(
+ 'errors',
+ (
'strict',
'backslashreplace',
'surrogateescape',
'replace',
'ignore',
- ):
- with self.subTest(errors=errors):
- 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)
+ ),
+ )
+ 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)
- self.assertEqual(
- 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)
- self.assertEqual(
- stderr.read().rstrip(),
- 'Fαtal\t?rr?r'
- if errors == 'replace'
- else 'Fαtal\trrr'
- if errors == 'ignore'
- else 'Fαtal\t\\xc8rr\\u014dr',
- )
+ 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'
+ )
diff --git a/distutils/tests/test_msvc9compiler.py b/distutils/tests/test_msvc9compiler.py
deleted file mode 100644
index 11a45557..00000000
--- a/distutils/tests/test_msvc9compiler.py
+++ /dev/null
@@ -1,183 +0,0 @@
-"""Tests for distutils.msvc9compiler."""
-import sys
-import unittest
-import os
-
-from distutils.errors import DistutilsPlatformError
-from distutils.tests import support
-
-# A manifest with the only assembly reference being the msvcrt assembly, so
-# should have the assembly completely stripped. Note that although the
-# assembly has a <security> reference the assembly is removed - that is
-# currently a "feature", not a bug :)
-_MANIFEST_WITH_ONLY_MSVC_REFERENCE = """\
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
- manifestVersion="1.0">
- <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
- <security>
- <requestedPrivileges>
- <requestedExecutionLevel level="asInvoker" uiAccess="false">
- </requestedExecutionLevel>
- </requestedPrivileges>
- </security>
- </trustInfo>
- <dependency>
- <dependentAssembly>
- <assemblyIdentity type="win32" name="Microsoft.VC90.CRT"
- version="9.0.21022.8" processorArchitecture="x86"
- publicKeyToken="XXXX">
- </assemblyIdentity>
- </dependentAssembly>
- </dependency>
-</assembly>
-"""
-
-# A manifest with references to assemblies other than msvcrt. When processed,
-# this assembly should be returned with just the msvcrt part removed.
-_MANIFEST_WITH_MULTIPLE_REFERENCES = """\
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
- manifestVersion="1.0">
- <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
- <security>
- <requestedPrivileges>
- <requestedExecutionLevel level="asInvoker" uiAccess="false">
- </requestedExecutionLevel>
- </requestedPrivileges>
- </security>
- </trustInfo>
- <dependency>
- <dependentAssembly>
- <assemblyIdentity type="win32" name="Microsoft.VC90.CRT"
- version="9.0.21022.8" processorArchitecture="x86"
- publicKeyToken="XXXX">
- </assemblyIdentity>
- </dependentAssembly>
- </dependency>
- <dependency>
- <dependentAssembly>
- <assemblyIdentity type="win32" name="Microsoft.VC90.MFC"
- version="9.0.21022.8" processorArchitecture="x86"
- publicKeyToken="XXXX"></assemblyIdentity>
- </dependentAssembly>
- </dependency>
-</assembly>
-"""
-
-_CLEANED_MANIFEST = """\
-<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<assembly xmlns="urn:schemas-microsoft-com:asm.v1"
- manifestVersion="1.0">
- <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
- <security>
- <requestedPrivileges>
- <requestedExecutionLevel level="asInvoker" uiAccess="false">
- </requestedExecutionLevel>
- </requestedPrivileges>
- </security>
- </trustInfo>
- <dependency>
-
- </dependency>
- <dependency>
- <dependentAssembly>
- <assemblyIdentity type="win32" name="Microsoft.VC90.MFC"
- version="9.0.21022.8" processorArchitecture="x86"
- publicKeyToken="XXXX"></assemblyIdentity>
- </dependentAssembly>
- </dependency>
-</assembly>"""
-
-if sys.platform == "win32":
- from distutils.msvccompiler import get_build_version
-
- if get_build_version() >= 8.0:
- SKIP_MESSAGE = None
- else:
- SKIP_MESSAGE = "These tests are only for MSVC8.0 or above"
-else:
- SKIP_MESSAGE = "These tests are only for win32"
-
-
-@unittest.skipUnless(SKIP_MESSAGE is None, SKIP_MESSAGE)
-class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase):
- def test_no_compiler(self):
- # makes sure query_vcvarsall raises
- # a DistutilsPlatformError if the compiler
- # is not found
- from distutils.msvc9compiler import query_vcvarsall
-
- def _find_vcvarsall(version):
- return None
-
- from distutils import msvc9compiler
-
- old_find_vcvarsall = msvc9compiler.find_vcvarsall
- msvc9compiler.find_vcvarsall = _find_vcvarsall
- try:
- self.assertRaises(
- DistutilsPlatformError, query_vcvarsall, 'wont find this version'
- )
- finally:
- msvc9compiler.find_vcvarsall = old_find_vcvarsall
-
- def test_reg_class(self):
- from distutils.msvc9compiler import Reg
-
- self.assertRaises(KeyError, Reg.get_value, 'xxx', 'xxx')
-
- # looking for values that should exist on all
- # windows registry versions.
- path = r'Control Panel\Desktop'
- v = Reg.get_value(path, 'dragfullwindows')
- self.assertIn(v, ('0', '1', '2'))
-
- import winreg
-
- HKCU = winreg.HKEY_CURRENT_USER
- keys = Reg.read_keys(HKCU, 'xxxx')
- self.assertEqual(keys, None)
-
- keys = Reg.read_keys(HKCU, r'Control Panel')
- self.assertIn('Desktop', keys)
-
- def test_remove_visual_c_ref(self):
- from distutils.msvc9compiler import MSVCCompiler
-
- tempdir = self.mkdtemp()
- manifest = os.path.join(tempdir, 'manifest')
- f = open(manifest, 'w')
- try:
- f.write(_MANIFEST_WITH_MULTIPLE_REFERENCES)
- finally:
- f.close()
-
- compiler = MSVCCompiler()
- compiler._remove_visual_c_ref(manifest)
-
- # see what we got
- f = open(manifest)
- try:
- # removing trailing spaces
- content = '\n'.join([line.rstrip() for line in f.readlines()])
- finally:
- f.close()
-
- # makes sure the manifest was properly cleaned
- self.assertEqual(content, _CLEANED_MANIFEST)
-
- def test_remove_entire_manifest(self):
- from distutils.msvc9compiler import MSVCCompiler
-
- tempdir = self.mkdtemp()
- manifest = os.path.join(tempdir, 'manifest')
- f = open(manifest, 'w')
- try:
- f.write(_MANIFEST_WITH_ONLY_MSVC_REFERENCE)
- finally:
- f.close()
-
- compiler = MSVCCompiler()
- got = compiler._remove_visual_c_ref(manifest)
- self.assertIsNone(got)
diff --git a/distutils/tests/test_msvccompiler.py b/distutils/tests/test_msvccompiler.py
index 9357a223..f63537b8 100644
--- a/distutils/tests/test_msvccompiler.py
+++ b/distutils/tests/test_msvccompiler.py
@@ -1,8 +1,8 @@
"""Tests for distutils._msvccompiler."""
import sys
-import unittest
import os
import threading
+import unittest.mock as mock
import pytest
@@ -14,7 +14,7 @@ from distutils import _msvccompiler
needs_winreg = pytest.mark.skipif('not hasattr(_msvccompiler, "winreg")')
-class msvccompilerTestCase(support.TempdirManager, unittest.TestCase):
+class Testmsvccompiler(support.TempdirManager):
def test_no_compiler(self):
# makes sure query_vcvarsall raises
# a DistutilsPlatformError if the compiler
@@ -25,11 +25,10 @@ class msvccompilerTestCase(support.TempdirManager, unittest.TestCase):
old_find_vcvarsall = _msvccompiler._find_vcvarsall
_msvccompiler._find_vcvarsall = _find_vcvarsall
try:
- self.assertRaises(
- DistutilsPlatformError,
- _msvccompiler._get_vc_env,
- 'wont find this version',
- )
+ with pytest.raises(DistutilsPlatformError):
+ _msvccompiler._get_vc_env(
+ 'wont find this version',
+ )
finally:
_msvccompiler._find_vcvarsall = old_find_vcvarsall
@@ -43,34 +42,25 @@ class msvccompilerTestCase(support.TempdirManager, unittest.TestCase):
os.environ[test_var] = test_value
try:
env = _msvccompiler._get_vc_env('x86')
- self.assertIn(test_var.lower(), env)
- self.assertEqual(test_value, env[test_var.lower()])
+ assert test_var.lower() in env
+ assert test_value == env[test_var.lower()]
finally:
os.environ.pop(test_var)
if old_distutils_use_sdk:
os.environ['DISTUTILS_USE_SDK'] = old_distutils_use_sdk
@needs_winreg
- def test_get_vc2017(self):
- # This function cannot be mocked, so pass it if we find VS 2017
- # and mark it skipped if we do not.
- version, path = _msvccompiler._find_vc2017()
- if version:
- self.assertGreaterEqual(version, 15)
- self.assertTrue(os.path.isdir(path))
- else:
- raise unittest.SkipTest("VS 2017 is not installed")
-
- @needs_winreg
- def test_get_vc2015(self):
- # This function cannot be mocked, so pass it if we find VS 2015
- # and mark it skipped if we do not.
- version, path = _msvccompiler._find_vc2015()
- if version:
- self.assertGreaterEqual(version, 14)
- self.assertTrue(os.path.isdir(path))
- else:
- raise unittest.SkipTest("VS 2015 is not installed")
+ @pytest.mark.parametrize('ver', (2015, 2017))
+ def test_get_vc(self, ver):
+ # This function cannot be mocked, so pass if VC is found
+ # and skip otherwise.
+ lookup = getattr(_msvccompiler, f'_find_vc{ver}')
+ expected_version = {2015: 14, 2017: 15}[ver]
+ version, path = lookup()
+ if not version:
+ pytest.skip(f"VS {ver} is not installed")
+ assert version >= expected_version
+ assert os.path.isdir(path)
class CheckThread(threading.Thread):
@@ -86,7 +76,7 @@ class CheckThread(threading.Thread):
return not self.exc_info
-class TestSpawn(unittest.TestCase):
+class TestSpawn:
def test_concurrent_safe(self):
"""
Concurrent calls to spawn should have consistent results.
@@ -119,7 +109,7 @@ class TestSpawn(unittest.TestCase):
"A spawn without an env argument."
assert os.environ["PATH"] == "expected"
- with unittest.mock.patch.object(ccompiler.CCompiler, 'spawn', CCompiler_spawn):
+ with mock.patch.object(ccompiler.CCompiler, 'spawn', CCompiler_spawn):
compiler.spawn(["n/a"])
assert os.environ.get("PATH") != "expected"
diff --git a/distutils/tests/test_register.py b/distutils/tests/test_register.py
index 76fec685..0a5765f1 100644
--- a/distutils/tests/test_register.py
+++ b/distutils/tests/test_register.py
@@ -1,12 +1,7 @@
"""Tests for distutils.command.register."""
import os
-import unittest
import getpass
import urllib
-import warnings
-
-
-from .py38compat import check_warnings
from distutils.command import register as register_module
from distutils.command.register import register
@@ -14,6 +9,7 @@ from distutils.errors import DistutilsSetupError
from distutils.log import INFO
from distutils.tests.test_config import BasePyPIRCCommandTestCase
+import pytest
try:
import docutils
@@ -41,7 +37,7 @@ password:password
"""
-class Inputs(object):
+class Inputs:
"""Fakes user inputs."""
def __init__(self, *answers):
@@ -55,7 +51,7 @@ class Inputs(object):
self.index += 1
-class FakeOpener(object):
+class FakeOpener:
"""Fakes a PyPI server"""
def __init__(self):
@@ -77,26 +73,20 @@ class FakeOpener(object):
}.get(name.lower(), default)
-class RegisterTestCase(BasePyPIRCCommandTestCase):
- def setUp(self):
- super(RegisterTestCase, self).setUp()
- # patching the password prompt
- self._old_getpass = getpass.getpass
+@pytest.fixture(autouse=True)
+def autopass(monkeypatch):
+ monkeypatch.setattr(getpass, 'getpass', lambda prompt: 'password')
- def _getpass(prompt):
- return 'password'
- getpass.getpass = _getpass
- urllib.request._opener = None
- self.old_opener = urllib.request.build_opener
- self.conn = urllib.request.build_opener = FakeOpener()
+@pytest.fixture(autouse=True)
+def fake_opener(monkeypatch, request):
+ opener = FakeOpener()
+ monkeypatch.setattr(urllib.request, 'build_opener', opener)
+ monkeypatch.setattr(urllib.request, '_opener', None)
+ request.instance.conn = opener
- def tearDown(self):
- getpass.getpass = self._old_getpass
- urllib.request._opener = None
- urllib.request.build_opener = self.old_opener
- super(RegisterTestCase, self).tearDown()
+class TestRegister(BasePyPIRCCommandTestCase):
def _get_cmd(self, metadata=None):
if metadata is None:
metadata = {
@@ -105,6 +95,7 @@ class RegisterTestCase(BasePyPIRCCommandTestCase):
'author_email': 'xxx',
'name': 'xxx',
'version': 'xxx',
+ 'long_description': 'xxx',
}
pkg_info, dist = self.create_dist(**metadata)
return register(dist)
@@ -117,7 +108,7 @@ class RegisterTestCase(BasePyPIRCCommandTestCase):
cmd = self._get_cmd()
# we shouldn't have a .pypirc file yet
- self.assertFalse(os.path.exists(self.rc))
+ assert not os.path.exists(self.rc)
# patching input and getpass.getpass
# so register gets happy
@@ -136,13 +127,13 @@ class RegisterTestCase(BasePyPIRCCommandTestCase):
del register_module.input
# we should have a brand new .pypirc file
- self.assertTrue(os.path.exists(self.rc))
+ assert os.path.exists(self.rc)
# with the content similar to WANTED_PYPIRC
f = open(self.rc)
try:
content = f.read()
- self.assertEqual(content, WANTED_PYPIRC)
+ assert content == WANTED_PYPIRC
finally:
f.close()
@@ -159,13 +150,13 @@ class RegisterTestCase(BasePyPIRCCommandTestCase):
# let's see what the server received : we should
# have 2 similar requests
- self.assertEqual(len(self.conn.reqs), 2)
+ assert len(self.conn.reqs) == 2
req1 = dict(self.conn.reqs[0].headers)
req2 = dict(self.conn.reqs[1].headers)
- self.assertEqual(req1['Content-length'], '1359')
- self.assertEqual(req2['Content-length'], '1359')
- self.assertIn(b'xxx', self.conn.reqs[1].data)
+ assert req1['Content-length'] == '1358'
+ assert req2['Content-length'] == '1358'
+ assert b'xxx' in self.conn.reqs[1].data
def test_password_not_in_file(self):
@@ -177,7 +168,7 @@ class RegisterTestCase(BasePyPIRCCommandTestCase):
# dist.password should be set
# therefore used afterwards by other commands
- self.assertEqual(cmd.distribution.password, 'password')
+ assert cmd.distribution.password == 'password'
def test_registering(self):
# this test runs choice 2
@@ -191,11 +182,11 @@ class RegisterTestCase(BasePyPIRCCommandTestCase):
del register_module.input
# we should have send a request
- self.assertEqual(len(self.conn.reqs), 1)
+ assert len(self.conn.reqs) == 1
req = self.conn.reqs[0]
headers = dict(req.headers)
- self.assertEqual(headers['Content-length'], '608')
- self.assertIn(b'tarek', req.data)
+ assert headers['Content-length'] == '608'
+ assert b'tarek' in req.data
def test_password_reset(self):
# this test runs choice 3
@@ -209,24 +200,26 @@ class RegisterTestCase(BasePyPIRCCommandTestCase):
del register_module.input
# we should have send a request
- self.assertEqual(len(self.conn.reqs), 1)
+ assert len(self.conn.reqs) == 1
req = self.conn.reqs[0]
headers = dict(req.headers)
- self.assertEqual(headers['Content-length'], '290')
- self.assertIn(b'tarek', req.data)
+ assert headers['Content-length'] == '290'
+ assert b'tarek' in req.data
- @unittest.skipUnless(docutils is not None, 'needs docutils')
def test_strict(self):
- # testing the script option
+ # testing the strict option
# when on, the register command stops if
# the metadata is incomplete or if
# long_description is not reSt compliant
+ pytest.importorskip('docutils')
+
# empty metadata
cmd = self._get_cmd({})
cmd.ensure_finalized()
cmd.strict = 1
- self.assertRaises(DistutilsSetupError, cmd.run)
+ with pytest.raises(DistutilsSetupError):
+ cmd.run()
# metadata are OK but long_description is broken
metadata = {
@@ -241,7 +234,8 @@ class RegisterTestCase(BasePyPIRCCommandTestCase):
cmd = self._get_cmd(metadata)
cmd.ensure_finalized()
cmd.strict = 1
- self.assertRaises(DistutilsSetupError, cmd.run)
+ with pytest.raises(DistutilsSetupError):
+ cmd.run()
# now something that works
metadata['long_description'] = 'title\n=====\n\ntext'
@@ -289,8 +283,8 @@ class RegisterTestCase(BasePyPIRCCommandTestCase):
finally:
del register_module.input
- @unittest.skipUnless(docutils is not None, 'needs docutils')
- def test_register_invalid_long_description(self):
+ def test_register_invalid_long_description(self, monkeypatch):
+ pytest.importorskip('docutils')
description = ':funkie:`str`' # mimic Sphinx-specific markup
metadata = {
'url': 'xxx',
@@ -304,25 +298,17 @@ class RegisterTestCase(BasePyPIRCCommandTestCase):
cmd.ensure_finalized()
cmd.strict = True
inputs = Inputs('2', 'tarek', 'tarek@ziade.org')
- register_module.input = inputs
- self.addCleanup(delattr, register_module, 'input')
-
- self.assertRaises(DistutilsSetupError, cmd.run)
+ monkeypatch.setattr(register_module, 'input', inputs, raising=False)
- def test_check_metadata_deprecated(self):
- # makes sure make_metadata is deprecated
- cmd = self._get_cmd()
- with check_warnings() as w:
- warnings.simplefilter("always")
- cmd.check_metadata()
- self.assertEqual(len(w.warnings), 1)
+ with pytest.raises(DistutilsSetupError):
+ cmd.run()
def test_list_classifiers(self):
cmd = self._get_cmd()
cmd.list_classifiers = 1
cmd.run()
results = self.get_logs(INFO)
- self.assertEqual(results, ['running check', 'xxx'])
+ assert results == ['running check', 'xxx']
def test_show_response(self):
# test that the --show-response option return a well formatted response
@@ -336,4 +322,4 @@ class RegisterTestCase(BasePyPIRCCommandTestCase):
del register_module.input
results = self.get_logs(INFO)
- self.assertEqual(results[3], 75 * '-' + '\nxxx\n' + 75 * '-')
+ assert results[3] == 75 * '-' + '\nxxx\n' + 75 * '-'
diff --git a/distutils/tests/test_sdist.py b/distutils/tests/test_sdist.py
index f0ca8295..b11fe7c4 100644
--- a/distutils/tests/test_sdist.py
+++ b/distutils/tests/test_sdist.py
@@ -1,7 +1,6 @@
"""Tests for distutils.command.sdist."""
import os
import tarfile
-import unittest
import warnings
import zipfile
from os.path import join
@@ -10,6 +9,8 @@ from test.support import captured_stdout
from .unix_compat import require_unix_id, require_uid_0, pwd, grp
import pytest
+import path
+import jaraco.path
from .py38compat import check_warnings
@@ -17,7 +18,7 @@ from distutils.command.sdist import sdist, show_formats
from distutils.core import Distribution
from distutils.tests.test_config import BasePyPIRCCommandTestCase
from distutils.errors import DistutilsOptionError
-from distutils.spawn import find_executable
+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
@@ -45,26 +46,24 @@ somecode%(sep)sdoc.txt
"""
-class SDistTestCase(BasePyPIRCCommandTestCase):
- def setUp(self):
- # PyPIRCCommandTestCase creates a temp dir already
- # and put it in self.tmp_dir
- super(SDistTestCase, self).setUp()
- # setting up an environment
- self.old_path = os.getcwd()
- os.mkdir(join(self.tmp_dir, 'somecode'))
- os.mkdir(join(self.tmp_dir, 'dist'))
- # a package, and a README
- self.write_file((self.tmp_dir, 'README'), 'xxx')
- self.write_file((self.tmp_dir, 'somecode', '__init__.py'), '#')
- self.write_file((self.tmp_dir, 'setup.py'), SETUP_PY)
- os.chdir(self.tmp_dir)
-
- def tearDown(self):
- # back to normal
- os.chdir(self.old_path)
- super(SDistTestCase, self).tearDown()
-
+@pytest.fixture(autouse=True)
+def project_dir(request, pypirc):
+ self = request.instance
+ jaraco.path.build(
+ {
+ 'somecode': {
+ '__init__.py': '#',
+ },
+ 'README': 'xxx',
+ 'setup.py': SETUP_PY,
+ },
+ self.tmp_dir,
+ )
+ with path.Path(self.tmp_dir):
+ yield
+
+
+class TestSDist(BasePyPIRCCommandTestCase):
def get_cmd(self, metadata=None):
"""Returns a cmd"""
if metadata is None:
@@ -113,7 +112,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
# now let's check what we have
dist_folder = join(self.tmp_dir, 'dist')
files = os.listdir(dist_folder)
- self.assertEqual(files, ['fake-1.0.zip'])
+ assert files == ['fake-1.0.zip']
zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip'))
try:
@@ -130,11 +129,11 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
'somecode/',
'somecode/__init__.py',
]
- self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected])
+ assert sorted(content) == ['fake-1.0/' + x for x in expected]
@pytest.mark.usefixtures('needs_zlib')
- @unittest.skipIf(find_executable('tar') is None, "The tar command is not found")
- @unittest.skipIf(find_executable('gzip') is None, "The gzip command is not found")
+ @pytest.mark.skipif("not find_executable('tar')")
+ @pytest.mark.skipif("not find_executable('gzip')")
def test_make_distribution(self):
# now building a sdist
dist, cmd = self.get_cmd()
@@ -148,7 +147,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
dist_folder = join(self.tmp_dir, 'dist')
result = os.listdir(dist_folder)
result.sort()
- self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'])
+ assert result == ['fake-1.0.tar', 'fake-1.0.tar.gz']
os.remove(join(dist_folder, 'fake-1.0.tar'))
os.remove(join(dist_folder, 'fake-1.0.tar.gz'))
@@ -161,7 +160,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
result = os.listdir(dist_folder)
result.sort()
- self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'])
+ assert result == ['fake-1.0.tar', 'fake-1.0.tar.gz']
@pytest.mark.usefixtures('needs_zlib')
def test_add_defaults(self):
@@ -215,7 +214,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
# now let's check what we have
dist_folder = join(self.tmp_dir, 'dist')
files = os.listdir(dist_folder)
- self.assertEqual(files, ['fake-1.0.zip'])
+ assert files == ['fake-1.0.zip']
zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip'))
try:
@@ -243,7 +242,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
'somecode/doc.dat',
'somecode/doc.txt',
]
- self.assertEqual(sorted(content), ['fake-1.0/' + x for x in expected])
+ assert sorted(content) == ['fake-1.0/' + x for x in expected]
# checking the MANIFEST
f = open(join(self.tmp_dir, 'MANIFEST'))
@@ -251,7 +250,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
manifest = f.read()
finally:
f.close()
- self.assertEqual(manifest, MANIFEST % {'sep': os.sep})
+ assert manifest == MANIFEST % {'sep': os.sep}
@pytest.mark.usefixtures('needs_zlib')
def test_metadata_check_option(self):
@@ -265,7 +264,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
warnings = [
msg for msg in self.get_logs(WARN) if msg.startswith('warning: check:')
]
- self.assertEqual(len(warnings), 1)
+ assert len(warnings) == 1
# trying with a complete set of metadata
self.clear_logs()
@@ -276,7 +275,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
warnings = [
msg for msg in self.get_logs(WARN) if msg.startswith('warning: check:')
]
- self.assertEqual(len(warnings), 0)
+ assert len(warnings) == 0
def test_check_metadata_deprecated(self):
# makes sure make_metadata is deprecated
@@ -284,7 +283,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
with check_warnings() as w:
warnings.simplefilter("always")
cmd.check_metadata()
- self.assertEqual(len(w.warnings), 1)
+ assert len(w.warnings) == 1
def test_show_formats(self):
with captured_stdout() as stdout:
@@ -297,27 +296,29 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
for line in stdout.getvalue().split('\n')
if line.strip().startswith('--formats=')
]
- self.assertEqual(len(output), num_formats)
+ assert len(output) == num_formats
def test_finalize_options(self):
dist, cmd = self.get_cmd()
cmd.finalize_options()
# default options set by finalize
- self.assertEqual(cmd.manifest, 'MANIFEST')
- self.assertEqual(cmd.template, 'MANIFEST.in')
- self.assertEqual(cmd.dist_dir, 'dist')
+ assert cmd.manifest == 'MANIFEST'
+ assert cmd.template == 'MANIFEST.in'
+ assert cmd.dist_dir == 'dist'
# formats has to be a string splitable on (' ', ',') or
# a stringlist
cmd.formats = 1
- self.assertRaises(DistutilsOptionError, cmd.finalize_options)
+ with pytest.raises(DistutilsOptionError):
+ cmd.finalize_options()
cmd.formats = ['zip']
cmd.finalize_options()
# formats has to be known
cmd.formats = 'supazipa'
- self.assertRaises(DistutilsOptionError, cmd.finalize_options)
+ with pytest.raises(DistutilsOptionError):
+ cmd.finalize_options()
# the following tests make sure there is a nice error message instead
# of a traceback when parsing an invalid manifest template
@@ -330,7 +331,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
cmd.filelist = FileList()
cmd.read_template()
warnings = self.get_logs(WARN)
- self.assertEqual(len(warnings), 1)
+ assert len(warnings) == 1
def test_invalid_template_unknown_command(self):
self._check_template('taunt knights *')
@@ -339,7 +340,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
# this manifest command takes one argument
self._check_template('prune')
- @unittest.skipIf(os.name != 'nt', 'test relevant for Windows only')
+ @pytest.mark.skipif("platform.system() != 'Windows'")
def test_invalid_template_wrong_path(self):
# on Windows, trailing slashes are not allowed
# this used to crash instead of raising a warning: #8286
@@ -365,7 +366,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
finally:
f.close()
- self.assertEqual(len(manifest), 5)
+ assert len(manifest) == 5
# adding a file
self.write_file((self.tmp_dir, 'somecode', 'doc2.txt'), '#')
@@ -386,8 +387,8 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
f.close()
# do we have the new file in MANIFEST ?
- self.assertEqual(len(manifest2), 6)
- self.assertIn('doc2.txt', manifest2[-1])
+ assert len(manifest2) == 6
+ assert 'doc2.txt' in manifest2[-1]
@pytest.mark.usefixtures('needs_zlib')
def test_manifest_marker(self):
@@ -404,7 +405,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
finally:
f.close()
- self.assertEqual(manifest[0], '# file GENERATED by distutils, do NOT edit')
+ assert manifest[0] == '# file GENERATED by distutils, do NOT edit'
@pytest.mark.usefixtures('needs_zlib')
def test_manifest_comments(self):
@@ -423,7 +424,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
self.write_file((self.tmp_dir, 'bad.py'), "# don't pick me!")
self.write_file((self.tmp_dir, '#bad.py'), "# don't pick me!")
cmd.run()
- self.assertEqual(cmd.filelist.files, ['good.py'])
+ assert cmd.filelist.files == ['good.py']
@pytest.mark.usefixtures('needs_zlib')
def test_manual_manifest(self):
@@ -437,7 +438,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
'This project maintains its MANIFEST file itself.',
)
cmd.run()
- self.assertEqual(cmd.filelist.files, ['README.manual'])
+ assert cmd.filelist.files == ['README.manual']
f = open(cmd.manifest)
try:
@@ -447,7 +448,7 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
finally:
f.close()
- self.assertEqual(manifest, ['README.manual'])
+ assert manifest == ['README.manual']
archive_name = join(self.tmp_dir, 'dist', 'fake-1.0.tar.gz')
archive = tarfile.open(archive_name)
@@ -455,16 +456,17 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
filenames = [tarinfo.name for tarinfo in archive]
finally:
archive.close()
- self.assertEqual(
- sorted(filenames),
- ['fake-1.0', 'fake-1.0/PKG-INFO', 'fake-1.0/README.manual'],
- )
+ assert sorted(filenames) == [
+ 'fake-1.0',
+ 'fake-1.0/PKG-INFO',
+ 'fake-1.0/README.manual',
+ ]
@pytest.mark.usefixtures('needs_zlib')
@require_unix_id
@require_uid_0
- @unittest.skipIf(find_executable('tar') is None, "The tar command is not found")
- @unittest.skipIf(find_executable('gzip') is None, "The gzip command is not found")
+ @pytest.mark.skipif("not find_executable('tar')")
+ @pytest.mark.skipif("not find_executable('gzip')")
def test_make_distribution_owner_group(self):
# now building a sdist
dist, cmd = self.get_cmd()
@@ -481,8 +483,8 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
archive = tarfile.open(archive_name)
try:
for member in archive.getmembers():
- self.assertEqual(member.uid, 0)
- self.assertEqual(member.gid, 0)
+ assert member.uid == 0
+ assert member.gid == 0
finally:
archive.close()
@@ -503,6 +505,6 @@ class SDistTestCase(BasePyPIRCCommandTestCase):
# rights (see #7408)
try:
for member in archive.getmembers():
- self.assertEqual(member.uid, os.getuid())
+ assert member.uid == os.getuid()
finally:
archive.close()
diff --git a/distutils/tests/test_spawn.py b/distutils/tests/test_spawn.py
index b86c157f..d2a898ed 100644
--- a/distutils/tests/test_spawn.py
+++ b/distutils/tests/test_spawn.py
@@ -2,7 +2,8 @@
import os
import stat
import sys
-import unittest.mock
+import unittest.mock as mock
+
from test.support import unix_shell
from . import py38compat as os_helper
@@ -11,10 +12,11 @@ from distutils.spawn import find_executable
from distutils.spawn import spawn
from distutils.errors import DistutilsExecError
from distutils.tests import support
+import pytest
-class SpawnTestCase(support.TempdirManager, support.LoggingSilencer, unittest.TestCase):
- @unittest.skipUnless(os.name in ('nt', 'posix'), 'Runs only under posix or nt')
+class TestSpawn(support.TempdirManager, support.LoggingSilencer):
+ @pytest.mark.skipif("os.name not in ('nt', 'posix')")
def test_spawn(self):
tmpdir = self.mkdtemp()
@@ -28,7 +30,8 @@ class SpawnTestCase(support.TempdirManager, support.LoggingSilencer, unittest.Te
self.write_file(exe, 'exit 1')
os.chmod(exe, 0o777)
- self.assertRaises(DistutilsExecError, spawn, [exe])
+ with pytest.raises(DistutilsExecError):
+ spawn([exe])
# now something that works
if sys.platform != 'win32':
@@ -56,70 +59,70 @@ class SpawnTestCase(support.TempdirManager, support.LoggingSilencer, unittest.Te
# test path parameter
rv = find_executable(program, path=tmp_dir)
- self.assertEqual(rv, filename)
+ assert rv == filename
if sys.platform == 'win32':
# test without ".exe" extension
rv = find_executable(program_noeext, path=tmp_dir)
- self.assertEqual(rv, filename)
+ assert rv == filename
# test find in the current directory
with os_helper.change_cwd(tmp_dir):
rv = find_executable(program)
- self.assertEqual(rv, program)
+ assert rv == program
# test non-existent program
dont_exist_program = "dontexist_" + program
rv = find_executable(dont_exist_program, path=tmp_dir)
- self.assertIsNone(rv)
+ assert rv is None
# PATH='': no match, except in the current directory
with os_helper.EnvironmentVarGuard() as env:
env['PATH'] = ''
- with unittest.mock.patch(
+ with mock.patch(
'distutils.spawn.os.confstr', return_value=tmp_dir, create=True
- ), unittest.mock.patch('distutils.spawn.os.defpath', tmp_dir):
+ ), mock.patch('distutils.spawn.os.defpath', tmp_dir):
rv = find_executable(program)
- self.assertIsNone(rv)
+ assert rv is None
# look in current directory
with os_helper.change_cwd(tmp_dir):
rv = find_executable(program)
- self.assertEqual(rv, program)
+ assert rv == program
# PATH=':': explicitly looks in the current directory
with os_helper.EnvironmentVarGuard() as env:
env['PATH'] = os.pathsep
- with unittest.mock.patch(
+ with mock.patch(
'distutils.spawn.os.confstr', return_value='', create=True
- ), unittest.mock.patch('distutils.spawn.os.defpath', ''):
+ ), mock.patch('distutils.spawn.os.defpath', ''):
rv = find_executable(program)
- self.assertIsNone(rv)
+ assert rv is None
# look in current directory
with os_helper.change_cwd(tmp_dir):
rv = find_executable(program)
- self.assertEqual(rv, program)
+ assert rv == program
# missing PATH: test os.confstr("CS_PATH") and os.defpath
with os_helper.EnvironmentVarGuard() as env:
env.pop('PATH', None)
# without confstr
- with unittest.mock.patch(
+ with mock.patch(
'distutils.spawn.os.confstr', side_effect=ValueError, create=True
- ), unittest.mock.patch('distutils.spawn.os.defpath', tmp_dir):
+ ), mock.patch('distutils.spawn.os.defpath', tmp_dir):
rv = find_executable(program)
- self.assertEqual(rv, filename)
+ assert rv == filename
# with confstr
- with unittest.mock.patch(
+ with mock.patch(
'distutils.spawn.os.confstr', return_value=tmp_dir, create=True
- ), unittest.mock.patch('distutils.spawn.os.defpath', ''):
+ ), mock.patch('distutils.spawn.os.defpath', ''):
rv = find_executable(program)
- self.assertEqual(rv, filename)
+ assert rv == filename
def test_spawn_missing_exe(self):
- with self.assertRaises(DistutilsExecError) as ctx:
+ with pytest.raises(DistutilsExecError) as ctx:
spawn(['does-not-exist'])
- self.assertIn("command 'does-not-exist' failed", str(ctx.exception))
+ assert "command 'does-not-exist' failed" in str(ctx.value)
diff --git a/distutils/tests/test_sysconfig.py b/distutils/tests/test_sysconfig.py
index 2d68b5a3..f1759839 100644
--- a/distutils/tests/test_sysconfig.py
+++ b/distutils/tests/test_sysconfig.py
@@ -5,14 +5,13 @@ import shutil
import subprocess
import sys
import textwrap
-import unittest
import pytest
import jaraco.envs
import distutils
from distutils import sysconfig
-from distutils.ccompiler import get_default_compiler
+from distutils.ccompiler import get_default_compiler # noqa: F401
from distutils.unixccompiler import UnixCCompiler
from test.support import swap_item
@@ -20,17 +19,8 @@ from .py38compat import TESTFN
@pytest.mark.usefixtures('save_env')
-class SysconfigTestCase(unittest.TestCase):
- def setUp(self):
- super(SysconfigTestCase, self).setUp()
- self.makefile = None
-
- def tearDown(self):
- if self.makefile is not None:
- os.unlink(self.makefile)
- self.cleanup_testfn()
- super(SysconfigTestCase, self).tearDown()
-
+@pytest.mark.usefixtures('cleanup_testfn')
+class TestSysconfig:
def cleanup_testfn(self):
if os.path.isfile(TESTFN):
os.remove(TESTFN)
@@ -39,47 +29,42 @@ class SysconfigTestCase(unittest.TestCase):
def test_get_config_h_filename(self):
config_h = sysconfig.get_config_h_filename()
- self.assertTrue(os.path.isfile(config_h), config_h)
+ assert os.path.isfile(config_h), config_h
- @unittest.skipIf(
- sys.platform == 'win32', 'Makefile only exists on Unix like systems'
- )
- @unittest.skipIf(
- sys.implementation.name != 'cpython', 'Makefile only exists in CPython'
- )
+ @pytest.mark.skipif("platform.system() == 'Windows'")
+ @pytest.mark.skipif("sys.implementation.name != 'cpython'")
def test_get_makefile_filename(self):
makefile = sysconfig.get_makefile_filename()
- self.assertTrue(os.path.isfile(makefile), makefile)
+ assert os.path.isfile(makefile), makefile
def test_get_python_lib(self):
# XXX doesn't work on Linux when Python was never installed before
# self.assertTrue(os.path.isdir(lib_dir), lib_dir)
# test for pythonxx.lib?
- self.assertNotEqual(
- sysconfig.get_python_lib(), sysconfig.get_python_lib(prefix=TESTFN)
- )
+ assert sysconfig.get_python_lib() != sysconfig.get_python_lib(prefix=TESTFN)
def test_get_config_vars(self):
cvars = sysconfig.get_config_vars()
- self.assertIsInstance(cvars, dict)
- self.assertTrue(cvars)
+ assert isinstance(cvars, dict)
+ assert cvars
- @unittest.skip('sysconfig.IS_PYPY')
+ @pytest.mark.skipif('sysconfig.IS_PYPY')
+ @pytest.mark.xfail(reason="broken")
def test_srcdir(self):
# See Issues #15322, #15364.
srcdir = sysconfig.get_config_var('srcdir')
- self.assertTrue(os.path.isabs(srcdir), srcdir)
- self.assertTrue(os.path.isdir(srcdir), srcdir)
+ assert os.path.isabs(srcdir), srcdir
+ assert os.path.isdir(srcdir), srcdir
if sysconfig.python_build:
# The python executable has not been installed so srcdir
# should be a full source checkout.
Python_h = os.path.join(srcdir, 'Include', 'Python.h')
- self.assertTrue(os.path.exists(Python_h), Python_h)
- self.assertTrue(sysconfig._is_python_source_dir(srcdir))
+ assert os.path.exists(Python_h), Python_h
+ assert sysconfig._is_python_source_dir(srcdir)
elif os.name == 'posix':
- self.assertEqual(os.path.dirname(sysconfig.get_makefile_filename()), srcdir)
+ assert os.path.dirname(sysconfig.get_makefile_filename()) == srcdir
def test_srcdir_independent_of_cwd(self):
# srcdir should be independent of the current working directory
@@ -91,7 +76,7 @@ class SysconfigTestCase(unittest.TestCase):
srcdir2 = sysconfig.get_config_var('srcdir')
finally:
os.chdir(cwd)
- self.assertEqual(srcdir, srcdir2)
+ assert srcdir == srcdir2
def customize_compiler(self):
# make sure AR gets caught
@@ -127,9 +112,7 @@ class SysconfigTestCase(unittest.TestCase):
return comp
- @unittest.skipUnless(
- get_default_compiler() == 'unix', 'not testing if default compiler is not unix'
- )
+ @pytest.mark.skipif("get_default_compiler() != 'unix'")
def test_customize_compiler(self):
# Make sure that sysconfig._config_vars is initialized
sysconfig.get_config_vars()
@@ -146,27 +129,23 @@ class SysconfigTestCase(unittest.TestCase):
os.environ['RANLIB'] = 'env_ranlib'
comp = self.customize_compiler()
- self.assertEqual(comp.exes['archiver'], 'env_ar --env-arflags')
- self.assertEqual(comp.exes['preprocessor'], 'env_cpp --env-cppflags')
- self.assertEqual(
- comp.exes['compiler'], 'env_cc --sc-cflags --env-cflags --env-cppflags'
+ assert comp.exes['archiver'] == 'env_ar --env-arflags'
+ assert comp.exes['preprocessor'] == 'env_cpp --env-cppflags'
+ assert comp.exes['compiler'] == 'env_cc --sc-cflags --env-cflags --env-cppflags'
+ assert comp.exes['compiler_so'] == (
+ 'env_cc --sc-cflags ' '--env-cflags ' '--env-cppflags --sc-ccshared'
)
- self.assertEqual(
- comp.exes['compiler_so'],
- ('env_cc --sc-cflags ' '--env-cflags ' '--env-cppflags --sc-ccshared'),
+ assert comp.exes['compiler_cxx'] == 'env_cxx --env-cxx-flags'
+ assert comp.exes['linker_exe'] == 'env_cc'
+ assert comp.exes['linker_so'] == (
+ 'env_ldshared --env-ldflags --env-cflags' ' --env-cppflags'
)
- self.assertEqual(comp.exes['compiler_cxx'], 'env_cxx --env-cxx-flags')
- self.assertEqual(comp.exes['linker_exe'], 'env_cc')
- self.assertEqual(
- comp.exes['linker_so'],
- ('env_ldshared --env-ldflags --env-cflags' ' --env-cppflags'),
- )
- self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix')
+ assert comp.shared_lib_extension == 'sc_shutil_suffix'
if sys.platform == "darwin":
- self.assertEqual(comp.exes['ranlib'], 'env_ranlib')
+ assert comp.exes['ranlib'] == 'env_ranlib'
else:
- self.assertTrue('ranlib' not in comp.exes)
+ assert 'ranlib' not in comp.exes
del os.environ['AR']
del os.environ['CC']
@@ -180,15 +159,15 @@ class SysconfigTestCase(unittest.TestCase):
del os.environ['RANLIB']
comp = self.customize_compiler()
- self.assertEqual(comp.exes['archiver'], 'sc_ar --sc-arflags')
- self.assertEqual(comp.exes['preprocessor'], 'sc_cc -E')
- self.assertEqual(comp.exes['compiler'], 'sc_cc --sc-cflags')
- self.assertEqual(comp.exes['compiler_so'], 'sc_cc --sc-cflags --sc-ccshared')
- self.assertEqual(comp.exes['compiler_cxx'], 'sc_cxx')
- self.assertEqual(comp.exes['linker_exe'], 'sc_cc')
- self.assertEqual(comp.exes['linker_so'], 'sc_ldshared')
- self.assertEqual(comp.shared_lib_extension, 'sc_shutil_suffix')
- self.assertTrue('ranlib' not in comp.exes)
+ assert comp.exes['archiver'] == 'sc_ar --sc-arflags'
+ assert comp.exes['preprocessor'] == 'sc_cc -E'
+ assert comp.exes['compiler'] == 'sc_cc --sc-cflags'
+ assert comp.exes['compiler_so'] == 'sc_cc --sc-cflags --sc-ccshared'
+ assert comp.exes['compiler_cxx'] == 'sc_cxx'
+ assert comp.exes['linker_exe'] == 'sc_cc'
+ assert comp.exes['linker_so'] == 'sc_ldshared'
+ assert comp.shared_lib_extension == 'sc_shutil_suffix'
+ assert 'ranlib' not in comp.exes
def test_parse_makefile_base(self):
self.makefile = TESTFN
@@ -199,9 +178,7 @@ class SysconfigTestCase(unittest.TestCase):
finally:
fd.close()
d = sysconfig.parse_makefile(self.makefile)
- self.assertEqual(
- d, {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", 'OTHER': 'foo'}
- )
+ assert d == {'CONFIG_ARGS': "'--arg1=optarg1' 'ENV=LIB'", 'OTHER': 'foo'}
def test_parse_makefile_literal_dollar(self):
self.makefile = TESTFN
@@ -212,25 +189,19 @@ class SysconfigTestCase(unittest.TestCase):
finally:
fd.close()
d = sysconfig.parse_makefile(self.makefile)
- self.assertEqual(
- d, {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", 'OTHER': 'foo'}
- )
+ assert d == {'CONFIG_ARGS': r"'--arg1=optarg1' 'ENV=\$LIB'", 'OTHER': 'foo'}
def test_sysconfig_module(self):
import sysconfig as global_sysconfig
- self.assertEqual(
- global_sysconfig.get_config_var('CFLAGS'),
- sysconfig.get_config_var('CFLAGS'),
+ assert global_sysconfig.get_config_var('CFLAGS') == sysconfig.get_config_var(
+ 'CFLAGS'
)
- self.assertEqual(
- global_sysconfig.get_config_var('LDFLAGS'),
- sysconfig.get_config_var('LDFLAGS'),
+ assert global_sysconfig.get_config_var('LDFLAGS') == sysconfig.get_config_var(
+ 'LDFLAGS'
)
- @unittest.skipIf(
- sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'), 'compiler flags customized'
- )
+ @pytest.mark.skipif("sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER')")
def test_sysconfig_compiler_vars(self):
# On OS X, binary installers support extension module building on
# various levels of the operating system with differing Xcode
@@ -249,21 +220,16 @@ class SysconfigTestCase(unittest.TestCase):
import sysconfig as global_sysconfig
if sysconfig.get_config_var('CUSTOMIZED_OSX_COMPILER'):
- self.skipTest('compiler flags customized')
- self.assertEqual(
- global_sysconfig.get_config_var('LDSHARED'),
- sysconfig.get_config_var('LDSHARED'),
- )
- self.assertEqual(
- global_sysconfig.get_config_var('CC'), sysconfig.get_config_var('CC')
+ pytest.skip('compiler flags customized')
+ assert global_sysconfig.get_config_var('LDSHARED') == sysconfig.get_config_var(
+ 'LDSHARED'
)
+ assert global_sysconfig.get_config_var('CC') == sysconfig.get_config_var('CC')
- @unittest.skipIf(
- sysconfig.get_config_var('EXT_SUFFIX') is None,
- 'EXT_SUFFIX required for this test',
- )
+ @pytest.mark.skipif("not sysconfig.get_config_var('EXT_SUFFIX')")
def test_SO_deprecation(self):
- self.assertWarns(DeprecationWarning, sysconfig.get_config_var, 'SO')
+ with pytest.warns(DeprecationWarning):
+ sysconfig.get_config_var('SO')
def test_customize_compiler_before_get_config_vars(self):
# Issue #21923: test that a Distribution compiler
@@ -288,33 +254,29 @@ class SysconfigTestCase(unittest.TestCase):
universal_newlines=True,
)
outs, errs = p.communicate()
- self.assertEqual(0, p.returncode, "Subprocess failed: " + outs)
+ assert 0 == p.returncode, "Subprocess failed: " + outs
def test_parse_config_h(self):
config_h = sysconfig.get_config_h_filename()
input = {}
with open(config_h, encoding="utf-8") as f:
result = sysconfig.parse_config_h(f, g=input)
- self.assertTrue(input is result)
+ assert input is result
with open(config_h, encoding="utf-8") as f:
result = sysconfig.parse_config_h(f)
- self.assertTrue(isinstance(result, dict))
+ assert isinstance(result, dict)
- @unittest.skipUnless(sys.platform == 'win32', 'Testing windows pyd suffix')
- @unittest.skipUnless(
- sys.implementation.name == 'cpython', 'Need cpython for this test'
- )
+ @pytest.mark.skipif("platform.system() != 'Windows'")
+ @pytest.mark.skipif("sys.implementation.name != 'cpython'")
def test_win_ext_suffix(self):
- self.assertTrue(sysconfig.get_config_var("EXT_SUFFIX").endswith(".pyd"))
- self.assertNotEqual(sysconfig.get_config_var("EXT_SUFFIX"), ".pyd")
-
- @unittest.skipUnless(sys.platform == 'win32', 'Testing Windows build layout')
- @unittest.skipUnless(
- sys.implementation.name == 'cpython', 'Need cpython for this test'
- )
- @unittest.skipUnless(
- '\\PCbuild\\'.casefold() in sys.executable.casefold(),
- 'Need sys.executable to be in a source tree',
+ assert sysconfig.get_config_var("EXT_SUFFIX").endswith(".pyd")
+ assert sysconfig.get_config_var("EXT_SUFFIX") != ".pyd"
+
+ @pytest.mark.skipif("platform.system() != 'Windows'")
+ @pytest.mark.skipif("sys.implementation.name != 'cpython'")
+ @pytest.mark.skipif(
+ '\\PCbuild\\'.casefold() not in sys.executable.casefold(),
+ reason='Need sys.executable to be in a source tree',
)
def test_win_build_venv_from_source_tree(self):
"""Ensure distutils.sysconfig detects venvs from source tree builds."""
diff --git a/distutils/tests/test_text_file.py b/distutils/tests/test_text_file.py
index ad10b6d5..7c8dc5be 100644
--- a/distutils/tests/test_text_file.py
+++ b/distutils/tests/test_text_file.py
@@ -1,6 +1,5 @@
"""Tests for distutils.text_file."""
import os
-import unittest
from distutils.text_file import TextFile
from distutils.tests import support
@@ -12,7 +11,7 @@ line 3 \\
"""
-class TextFileTestCase(support.TempdirManager, unittest.TestCase):
+class TestTextFile(support.TempdirManager):
def test_class(self):
# old tests moved from text_file.__main__
# so they are really called by the buildbots
@@ -51,7 +50,7 @@ class TextFileTestCase(support.TempdirManager, unittest.TestCase):
def test_input(count, description, file, expected_result):
result = file.readlines()
- self.assertEqual(result, expected_result)
+ assert result == expected_result
tmpdir = self.mkdtemp()
filename = os.path.join(tmpdir, "test.txt")
diff --git a/distutils/tests/test_unixccompiler.py b/distutils/tests/test_unixccompiler.py
index 3cd7c6ca..3978c239 100644
--- a/distutils/tests/test_unixccompiler.py
+++ b/distutils/tests/test_unixccompiler.py
@@ -1,8 +1,7 @@
"""Tests for distutils.unixccompiler."""
import os
import sys
-import unittest
-from unittest.mock import patch
+import unittest.mock as mock
from .py38compat import EnvironmentVarGuard
@@ -12,28 +11,27 @@ from distutils.unixccompiler import UnixCCompiler
from distutils.util import _clear_cached_macosx_ver
from . import support
+import pytest
-class UnixCCompilerTestCase(support.TempdirManager, unittest.TestCase):
- def setUp(self):
- super().setUp()
- self._backup_platform = sys.platform
- self._backup_get_config_var = sysconfig.get_config_var
- self._backup_get_config_vars = sysconfig.get_config_vars
+@pytest.fixture(autouse=True)
+def save_values(monkeypatch):
+ monkeypatch.setattr(sys, 'platform', sys.platform)
+ monkeypatch.setattr(sysconfig, 'get_config_var', sysconfig.get_config_var)
+ monkeypatch.setattr(sysconfig, 'get_config_vars', sysconfig.get_config_vars)
- class CompilerWrapper(UnixCCompiler):
- def rpath_foo(self):
- return self.runtime_library_dir_option('/foo')
- self.cc = CompilerWrapper()
+@pytest.fixture(autouse=True)
+def compiler_wrapper(request):
+ class CompilerWrapper(UnixCCompiler):
+ def rpath_foo(self):
+ return self.runtime_library_dir_option('/foo')
- def tearDown(self):
- super().tearDown()
- sys.platform = self._backup_platform
- sysconfig.get_config_var = self._backup_get_config_var
- sysconfig.get_config_vars = self._backup_get_config_vars
+ request.instance.cc = CompilerWrapper()
- @unittest.skipIf(sys.platform == 'win32', "can't test on Windows")
+
+class TestUnixCCompiler(support.TempdirManager):
+ @pytest.mark.skipif('platform.system == "Windows"') # noqa: C901
def test_runtime_libdir_option(self): # noqa: C901
# Issue #5900; GitHub Issue #37
#
@@ -74,7 +72,7 @@ class UnixCCompilerTestCase(support.TempdirManager, unittest.TestCase):
def do_darwin_test(syscfg_macosx_ver, env_macosx_ver, expected_flag):
env = os.environ
- msg = "macOS version = (sysconfig=%r, env=%r)" % (
+ msg = "macOS version = (sysconfig={!r}, env={!r})".format(
syscfg_macosx_ver,
env_macosx_ver,
)
@@ -93,10 +91,10 @@ class UnixCCompilerTestCase(support.TempdirManager, unittest.TestCase):
# Run the test
if expected_flag is not None:
- self.assertEqual(self.cc.rpath_foo(), expected_flag, msg=msg)
+ assert self.cc.rpath_foo() == expected_flag, msg
else:
- with self.assertRaisesRegex(
- DistutilsPlatformError, darwin_ver_var + r' mismatch', msg=msg
+ with pytest.raises(
+ DistutilsPlatformError, match=darwin_ver_var + r' mismatch'
):
self.cc.rpath_foo()
@@ -128,19 +126,19 @@ class UnixCCompilerTestCase(support.TempdirManager, unittest.TestCase):
return 'xxx'
sysconfig.get_config_var = gcv
- self.assertEqual(self.cc.rpath_foo(), ['+s', '-L/foo'])
+ assert self.cc.rpath_foo() == ['+s', '-L/foo']
def gcv(v):
return 'gcc'
sysconfig.get_config_var = gcv
- self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo'])
+ assert self.cc.rpath_foo() == ['-Wl,+s', '-L/foo']
def gcv(v):
return 'g++'
sysconfig.get_config_var = gcv
- self.assertEqual(self.cc.rpath_foo(), ['-Wl,+s', '-L/foo'])
+ assert self.cc.rpath_foo() == ['-Wl,+s', '-L/foo']
sysconfig.get_config_var = old_gcv
@@ -154,7 +152,7 @@ class UnixCCompilerTestCase(support.TempdirManager, unittest.TestCase):
return 'yes'
sysconfig.get_config_var = gcv
- self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo')
+ assert self.cc.rpath_foo() == '-Wl,--enable-new-dtags,-R/foo'
def gcv(v):
if v == 'CC':
@@ -163,7 +161,7 @@ class UnixCCompilerTestCase(support.TempdirManager, unittest.TestCase):
return 'yes'
sysconfig.get_config_var = gcv
- self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo')
+ assert self.cc.rpath_foo() == '-Wl,--enable-new-dtags,-R/foo'
# GCC non-GNULD
sys.platform = 'bar'
@@ -175,7 +173,7 @@ class UnixCCompilerTestCase(support.TempdirManager, unittest.TestCase):
return 'no'
sysconfig.get_config_var = gcv
- self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo')
+ assert self.cc.rpath_foo() == '-Wl,-R/foo'
# GCC GNULD with fully qualified configuration prefix
# see #7617
@@ -188,7 +186,7 @@ class UnixCCompilerTestCase(support.TempdirManager, unittest.TestCase):
return 'yes'
sysconfig.get_config_var = gcv
- self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo')
+ assert self.cc.rpath_foo() == '-Wl,--enable-new-dtags,-R/foo'
# non-GCC GNULD
sys.platform = 'bar'
@@ -200,7 +198,7 @@ class UnixCCompilerTestCase(support.TempdirManager, unittest.TestCase):
return 'yes'
sysconfig.get_config_var = gcv
- self.assertEqual(self.cc.rpath_foo(), '-Wl,--enable-new-dtags,-R/foo')
+ assert self.cc.rpath_foo() == '-Wl,--enable-new-dtags,-R/foo'
# non-GCC non-GNULD
sys.platform = 'bar'
@@ -212,9 +210,9 @@ class UnixCCompilerTestCase(support.TempdirManager, unittest.TestCase):
return 'no'
sysconfig.get_config_var = gcv
- self.assertEqual(self.cc.rpath_foo(), '-Wl,-R/foo')
+ assert self.cc.rpath_foo() == '-Wl,-R/foo'
- @unittest.skipIf(sys.platform == 'win32', "can't test on Windows")
+ @pytest.mark.skipif('platform.system == "Windows"')
def test_cc_overrides_ldshared(self):
# Issue #18080:
# ensure that setting CC env variable also changes default linker
@@ -234,9 +232,9 @@ class UnixCCompilerTestCase(support.TempdirManager, unittest.TestCase):
env['CC'] = 'my_cc'
del env['LDSHARED']
sysconfig.customize_compiler(self.cc)
- self.assertEqual(self.cc.linker_so[0], 'my_cc')
+ assert self.cc.linker_so[0] == 'my_cc'
- @unittest.skipIf(sys.platform == 'win32', "can't test on Windows")
+ @pytest.mark.skipif('platform.system == "Windows"')
def test_cc_overrides_ldshared_for_cxx_correctly(self):
"""
Ensure that setting CC env variable also changes default linker
@@ -259,24 +257,24 @@ class UnixCCompilerTestCase(support.TempdirManager, unittest.TestCase):
sysconfig.get_config_var = gcv
sysconfig.get_config_vars = gcvs
- with patch.object(
+ with mock.patch.object(
self.cc, 'spawn', return_value=None
- ) as mock_spawn, patch.object(
+ ) as mock_spawn, mock.patch.object(
self.cc, '_need_link', return_value=True
- ), patch.object(
+ ), mock.patch.object(
self.cc, 'mkpath', return_value=None
), EnvironmentVarGuard() as env:
env['CC'] = 'ccache my_cc'
env['CXX'] = 'my_cxx'
del env['LDSHARED']
sysconfig.customize_compiler(self.cc)
- self.assertEqual(self.cc.linker_so[0:2], ['ccache', 'my_cc'])
+ assert self.cc.linker_so[0:2] == ['ccache', 'my_cc']
self.cc.link(None, [], 'a.out', target_lang='c++')
call_args = mock_spawn.call_args[0][0]
expected = ['my_cxx', '-bundle', '-undefined', 'dynamic_lookup']
assert call_args[:4] == expected
- @unittest.skipIf(sys.platform == 'win32', "can't test on Windows")
+ @pytest.mark.skipif('platform.system == "Windows"')
def test_explicit_ldshared(self):
# Issue #18080:
# ensure that setting CC env variable does not change
@@ -297,7 +295,7 @@ class UnixCCompilerTestCase(support.TempdirManager, unittest.TestCase):
env['CC'] = 'my_cc'
env['LDSHARED'] = 'my_ld -bundle -dynamic'
sysconfig.customize_compiler(self.cc)
- self.assertEqual(self.cc.linker_so[0], 'my_ld')
+ assert self.cc.linker_so[0] == 'my_ld'
def test_has_function(self):
# Issue https://github.com/pypa/distutils/issues/64:
diff --git a/distutils/tests/test_upload.py b/distutils/tests/test_upload.py
index a1d37306..fb905b64 100644
--- a/distutils/tests/test_upload.py
+++ b/distutils/tests/test_upload.py
@@ -11,6 +11,7 @@ from distutils.errors import DistutilsError
from distutils.log import ERROR, INFO
from distutils.tests.test_config import PYPIRC, BasePyPIRCCommandTestCase
+import pytest
PYPIRC_LONG_PASSWORD = """\
[distutils]
@@ -42,7 +43,7 @@ username:me
"""
-class FakeOpen(object):
+class FakeOpen:
def __init__(self, url, msg=None, code=None):
self.url = url
if not isinstance(url, str):
@@ -64,19 +65,14 @@ class FakeOpen(object):
return self.code
-class uploadTestCase(BasePyPIRCCommandTestCase):
- def setUp(self):
- super(uploadTestCase, self).setUp()
- self.old_open = upload_mod.urlopen
- upload_mod.urlopen = self._urlopen
- self.last_open = None
- self.next_msg = None
- self.next_code = None
+@pytest.fixture(autouse=True)
+def urlopen(request, monkeypatch):
+ self = request.instance
+ monkeypatch.setattr(upload_mod, 'urlopen', self._urlopen)
+ self.next_msg = self.next_code = None
- def tearDown(self):
- upload_mod.urlopen = self.old_open
- super(uploadTestCase, self).tearDown()
+class TestUpload(BasePyPIRCCommandTestCase):
def _urlopen(self, url):
self.last_open = FakeOpen(url, msg=self.next_msg, code=self.next_code)
return self.last_open
@@ -94,7 +90,7 @@ class uploadTestCase(BasePyPIRCCommandTestCase):
('realm', 'pypi'),
('repository', 'https://upload.pypi.org/legacy/'),
):
- self.assertEqual(getattr(cmd, attr), waited)
+ assert getattr(cmd, attr) == waited
def test_saved_password(self):
# file with no password
@@ -104,14 +100,14 @@ class uploadTestCase(BasePyPIRCCommandTestCase):
dist = Distribution()
cmd = upload(dist)
cmd.finalize_options()
- self.assertEqual(cmd.password, None)
+ assert cmd.password is None
# make sure we get it as well, if another command
# initialized it at the dist level
dist.password = 'xxx'
cmd = upload(dist)
cmd.finalize_options()
- self.assertEqual(cmd.password, 'xxx')
+ assert cmd.password == 'xxx'
def test_upload(self):
tmp = self.mkdtemp()
@@ -130,33 +126,32 @@ class uploadTestCase(BasePyPIRCCommandTestCase):
# what did we send ?
headers = dict(self.last_open.req.headers)
- self.assertGreaterEqual(int(headers['Content-length']), 2162)
+ assert int(headers['Content-length']) >= 2162
content_type = headers['Content-type']
- self.assertTrue(content_type.startswith('multipart/form-data'))
- self.assertEqual(self.last_open.req.get_method(), 'POST')
+ assert content_type.startswith('multipart/form-data')
+ assert self.last_open.req.get_method() == 'POST'
expected_url = 'https://upload.pypi.org/legacy/'
- self.assertEqual(self.last_open.req.get_full_url(), expected_url)
+ assert self.last_open.req.get_full_url() == expected_url
data = self.last_open.req.data
- self.assertIn(b'xxx', data)
- self.assertIn(b'protocol_version', data)
- self.assertIn(b'sha256_digest', data)
- self.assertIn(
- b'cd2eb0837c9b4c962c22d2ff8b5441b7b45805887f051d39bf133b583baf' b'6860',
- data,
+ assert b'xxx' in data
+ assert b'protocol_version' in data
+ assert b'sha256_digest' in data
+ assert (
+ b'cd2eb0837c9b4c962c22d2ff8b5441b7b45805887f051d39bf133b583baf'
+ b'6860' in data
)
if b'md5_digest' in data:
- self.assertIn(b'f561aaf6ef0bf14d4208bb46a4ccb3ad', data)
+ assert b'f561aaf6ef0bf14d4208bb46a4ccb3ad' in data
if b'blake2_256_digest' in data:
- self.assertIn(
+ assert (
b'b6f289a27d4fe90da63c503bfe0a9b761a8f76bb86148565065f040be'
b'6d1c3044cf7ded78ef800509bccb4b648e507d88dc6383d67642aadcc'
- b'ce443f1534330a',
- data,
+ b'ce443f1534330a' in data
)
# The PyPI response body was echoed
results = self.get_logs(INFO)
- self.assertEqual(results[-1], 75 * '-' + '\nxyzzy\n' + 75 * '-')
+ assert results[-1] == 75 * '-' + '\nxyzzy\n' + 75 * '-'
# bpo-32304: archives whose last byte was b'\r' were corrupted due to
# normalization intended for Mac OS 9.
@@ -180,15 +175,28 @@ class uploadTestCase(BasePyPIRCCommandTestCase):
cmd.run()
headers = dict(self.last_open.req.headers)
- self.assertGreaterEqual(int(headers['Content-length']), 2172)
- self.assertIn(b'long description\r', self.last_open.req.data)
+ assert int(headers['Content-length']) >= 2172
+ assert b'long description\r' in self.last_open.req.data
def test_upload_fails(self):
self.next_msg = "Not Found"
self.next_code = 404
- self.assertRaises(DistutilsError, self.test_upload)
+ with pytest.raises(DistutilsError):
+ self.test_upload()
- def test_wrong_exception_order(self):
+ @pytest.mark.parametrize(
+ 'exception,expected,raised_exception',
+ [
+ (OSError('oserror'), 'oserror', OSError),
+ pytest.param(
+ HTTPError('url', 400, 'httperror', {}, None),
+ 'Upload failed (400): httperror',
+ DistutilsError,
+ id="HTTP 400",
+ ),
+ ],
+ )
+ def test_wrong_exception_order(self, exception, expected, raised_exception):
tmp = self.mkdtemp()
path = os.path.join(tmp, 'xxx')
self.write_file(path)
@@ -196,24 +204,15 @@ class uploadTestCase(BasePyPIRCCommandTestCase):
self.write_file(self.rc, PYPIRC_LONG_PASSWORD)
pkg_dir, dist = self.create_dist(dist_files=dist_files)
- tests = [
- (OSError('oserror'), 'oserror', OSError),
- (
- HTTPError('url', 400, 'httperror', {}, None),
- 'Upload failed (400): httperror',
- DistutilsError,
- ),
- ]
- for exception, expected, raised_exception in tests:
- with self.subTest(exception=type(exception).__name__):
- with mock.patch(
- 'distutils.command.upload.urlopen',
- new=mock.Mock(side_effect=exception),
- ):
- with self.assertRaises(raised_exception):
- cmd = upload(dist)
- cmd.ensure_finalized()
- cmd.run()
- results = self.get_logs(ERROR)
- self.assertIn(expected, results[-1])
- self.clear_logs()
+
+ with mock.patch(
+ 'distutils.command.upload.urlopen',
+ new=mock.Mock(side_effect=exception),
+ ):
+ with pytest.raises(raised_exception):
+ cmd = upload(dist)
+ cmd.ensure_finalized()
+ cmd.run()
+ results = self.get_logs(ERROR)
+ assert expected in results[-1]
+ self.clear_logs()
diff --git a/distutils/tests/test_util.py b/distutils/tests/test_util.py
index 5a44f10a..605b0d40 100644
--- a/distutils/tests/test_util.py
+++ b/distutils/tests/test_util.py
@@ -1,10 +1,9 @@
"""Tests for distutils.util."""
import os
import sys
-import unittest
import sysconfig as stdlib_sysconfig
+import unittest.mock as mock
from copy import copy
-from unittest import mock
import pytest
@@ -20,79 +19,45 @@ from distutils.util import (
grok_environment_error,
get_host_platform,
)
-from distutils import util # used to patch _environ_checked
+from distutils import util
from distutils import sysconfig
from distutils.errors import DistutilsPlatformError, DistutilsByteCompileError
-@pytest.mark.usefixtures('save_env')
-class UtilTestCase(unittest.TestCase):
- def setUp(self):
- super(UtilTestCase, self).setUp()
- # saving the environment
- self.name = os.name
- self.platform = sys.platform
- self.version = sys.version
- self.sep = os.sep
- self.join = os.path.join
- self.isabs = os.path.isabs
- self.splitdrive = os.path.splitdrive
- self._config_vars = copy(sysconfig._config_vars)
-
- # patching os.uname
- if hasattr(os, 'uname'):
- self.uname = os.uname
- self._uname = os.uname()
- else:
- self.uname = None
- self._uname = None
-
- os.uname = self._get_uname
-
- def tearDown(self):
- # getting back the environment
- os.name = self.name
- sys.platform = self.platform
- sys.version = self.version
- os.sep = self.sep
- os.path.join = self.join
- os.path.isabs = self.isabs
- os.path.splitdrive = self.splitdrive
- if self.uname is not None:
- os.uname = self.uname
- else:
- del os.uname
- sysconfig._config_vars = copy(self._config_vars)
- super(UtilTestCase, self).tearDown()
-
- def _set_uname(self, uname):
- self._uname = uname
-
- def _get_uname(self):
- return self._uname
+@pytest.fixture(autouse=True)
+def environment(monkeypatch):
+ monkeypatch.setattr(os, 'name', os.name)
+ monkeypatch.setattr(sys, 'platform', sys.platform)
+ monkeypatch.setattr(sys, 'version', sys.version)
+ monkeypatch.setattr(os, 'sep', os.sep)
+ monkeypatch.setattr(os.path, 'join', os.path.join)
+ monkeypatch.setattr(os.path, 'isabs', os.path.isabs)
+ monkeypatch.setattr(os.path, 'splitdrive', os.path.splitdrive)
+ monkeypatch.setattr(sysconfig, '_config_vars', copy(sysconfig._config_vars))
+
+@pytest.mark.usefixtures('save_env')
+class TestUtil:
def test_get_host_platform(self):
- with unittest.mock.patch('os.name', 'nt'):
- with unittest.mock.patch('sys.version', '... [... (ARM64)]'):
- self.assertEqual(get_host_platform(), 'win-arm64')
- with unittest.mock.patch('sys.version', '... [... (ARM)]'):
- self.assertEqual(get_host_platform(), 'win-arm32')
+ with mock.patch('os.name', 'nt'):
+ with mock.patch('sys.version', '... [... (ARM64)]'):
+ assert get_host_platform() == 'win-arm64'
+ with mock.patch('sys.version', '... [... (ARM)]'):
+ assert get_host_platform() == 'win-arm32'
- with unittest.mock.patch('sys.version_info', (3, 9, 0, 'final', 0)):
- self.assertEqual(get_host_platform(), stdlib_sysconfig.get_platform())
+ with mock.patch('sys.version_info', (3, 9, 0, 'final', 0)):
+ assert get_host_platform() == stdlib_sysconfig.get_platform()
def test_get_platform(self):
- with unittest.mock.patch('os.name', 'nt'):
- with unittest.mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'x86'}):
- self.assertEqual(get_platform(), 'win32')
- with unittest.mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'x64'}):
- self.assertEqual(get_platform(), 'win-amd64')
- with unittest.mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'arm'}):
- self.assertEqual(get_platform(), 'win-arm32')
- with unittest.mock.patch.dict(
- 'os.environ', {'VSCMD_ARG_TGT_ARCH': 'arm64'}
- ):
- self.assertEqual(get_platform(), 'win-arm64')
+ with mock.patch('os.name', 'nt'):
+ with mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'x86'}):
+ assert get_platform() == 'win32'
+ with mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'x64'}):
+ assert get_platform() == 'win-amd64'
+ with mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'arm'}):
+ assert get_platform() == 'win-arm32'
+ with mock.patch.dict('os.environ', {'VSCMD_ARG_TGT_ARCH': 'arm64'}):
+ assert get_platform() == 'win-arm64'
def test_convert_path(self):
# linux/mac
@@ -103,7 +68,7 @@ class UtilTestCase(unittest.TestCase):
os.path.join = _join
- self.assertEqual(convert_path('/home/to/my/stuff'), '/home/to/my/stuff')
+ assert convert_path('/home/to/my/stuff') == '/home/to/my/stuff'
# win
os.sep = '\\'
@@ -113,11 +78,13 @@ class UtilTestCase(unittest.TestCase):
os.path.join = _join
- self.assertRaises(ValueError, convert_path, '/home/to/my/stuff')
- self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/')
+ with pytest.raises(ValueError):
+ convert_path('/home/to/my/stuff')
+ with pytest.raises(ValueError):
+ convert_path('home/to/my/stuff/')
- self.assertEqual(convert_path('home/to/my/stuff'), 'home\\to\\my\\stuff')
- self.assertEqual(convert_path('.'), os.curdir)
+ assert convert_path('home/to/my/stuff') == 'home\\to\\my\\stuff'
+ assert convert_path('.') == os.curdir
def test_change_root(self):
# linux/mac
@@ -133,8 +100,8 @@ class UtilTestCase(unittest.TestCase):
os.path.join = _join
- self.assertEqual(change_root('/root', '/old/its/here'), '/root/old/its/here')
- self.assertEqual(change_root('/root', 'its/here'), '/root/its/here')
+ assert change_root('/root', '/old/its/here') == '/root/old/its/here'
+ assert change_root('/root', 'its/here') == '/root/its/here'
# windows
os.name = 'nt'
@@ -156,14 +123,15 @@ class UtilTestCase(unittest.TestCase):
os.path.join = _join
- self.assertEqual(
- change_root('c:\\root', 'c:\\old\\its\\here'), 'c:\\root\\old\\its\\here'
+ assert (
+ change_root('c:\\root', 'c:\\old\\its\\here') == 'c:\\root\\old\\its\\here'
)
- self.assertEqual(change_root('c:\\root', 'its\\here'), 'c:\\root\\its\\here')
+ assert change_root('c:\\root', 'its\\here') == 'c:\\root\\its\\here'
# BugsBunny os (it's a great os)
os.name = 'BugsBunny'
- self.assertRaises(DistutilsPlatformError, change_root, 'c:\\root', 'its\\here')
+ with pytest.raises(DistutilsPlatformError):
+ change_root('c:\\root', 'its\\here')
# XXX platforms to be covered: mac
@@ -173,10 +141,10 @@ class UtilTestCase(unittest.TestCase):
check_environ()
- self.assertEqual(os.environ['PLAT'], get_platform())
- self.assertEqual(util._environ_checked, 1)
+ assert os.environ['PLAT'] == get_platform()
+ assert util._environ_checked == 1
- @unittest.skipUnless(os.name == 'posix', 'specific to posix')
+ @pytest.mark.skipif("os.name != 'posix'")
def test_check_environ_getpwuid(self):
util._environ_checked = 0
os.environ.pop('HOME', None)
@@ -189,7 +157,7 @@ class UtilTestCase(unittest.TestCase):
)
with mock.patch.object(pwd, 'getpwuid', return_value=result):
check_environ()
- self.assertEqual(os.environ['HOME'], '/home/distutils')
+ assert os.environ['HOME'] == '/home/distutils'
util._environ_checked = 0
os.environ.pop('HOME', None)
@@ -197,23 +165,25 @@ class UtilTestCase(unittest.TestCase):
# bpo-10496: Catch pwd.getpwuid() error
with mock.patch.object(pwd, 'getpwuid', side_effect=KeyError):
check_environ()
- self.assertNotIn('HOME', os.environ)
+ assert 'HOME' not in os.environ
def test_split_quoted(self):
- self.assertEqual(
- split_quoted('""one"" "two" \'three\' \\four'),
- ['one', 'two', 'three', 'four'],
- )
+ assert split_quoted('""one"" "two" \'three\' \\four') == [
+ 'one',
+ 'two',
+ 'three',
+ 'four',
+ ]
def test_strtobool(self):
yes = ('y', 'Y', 'yes', 'True', 't', 'true', 'True', 'On', 'on', '1')
no = ('n', 'no', 'f', 'false', 'off', '0', 'Off', 'No', 'N')
for y in yes:
- self.assertTrue(strtobool(y))
+ assert strtobool(y)
for n in no:
- self.assertFalse(strtobool(n))
+ assert not strtobool(n)
def test_rfc822_escape(self):
header = 'I am a\npoor\nlonesome\nheader\n'
@@ -221,7 +191,7 @@ class UtilTestCase(unittest.TestCase):
wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s' 'header%(8s)s') % {
'8s': '\n' + 8 * ' '
}
- self.assertEqual(res, wanted)
+ assert res == wanted
def test_dont_write_bytecode(self):
# makes sure byte_compile raise a DistutilsError
@@ -229,7 +199,8 @@ class UtilTestCase(unittest.TestCase):
old_dont_write_bytecode = sys.dont_write_bytecode
sys.dont_write_bytecode = True
try:
- self.assertRaises(DistutilsByteCompileError, byte_compile, [])
+ with pytest.raises(DistutilsByteCompileError):
+ byte_compile([])
finally:
sys.dont_write_bytecode = old_dont_write_bytecode
@@ -237,4 +208,4 @@ class UtilTestCase(unittest.TestCase):
# test obsolete function to ensure backward compat (#4931)
exc = IOError("Unable to find batch file")
msg = grok_environment_error(exc)
- self.assertEqual(msg, "error: Unable to find batch file")
+ assert msg == "error: Unable to find batch file"
diff --git a/distutils/tests/test_version.py b/distutils/tests/test_version.py
index 3727bac8..ff52ea46 100644
--- a/distutils/tests/test_version.py
+++ b/distutils/tests/test_version.py
@@ -1,26 +1,26 @@
"""Tests for distutils.version."""
-import unittest
+import pytest
+
import distutils
from distutils.version import LooseVersion
from distutils.version import StrictVersion
-class VersionTestCase(unittest.TestCase):
- def setUp(self):
- self.ctx = distutils.version.suppress_known_deprecation()
- self.ctx.__enter__()
+@pytest.fixture(autouse=True)
+def suppress_deprecation():
+ with distutils.version.suppress_known_deprecation():
+ yield
- def tearDown(self):
- self.ctx.__exit__(None, None, None)
+class TestVersion:
def test_prerelease(self):
version = StrictVersion('1.2.3a1')
- self.assertEqual(version.version, (1, 2, 3))
- self.assertEqual(version.prerelease, ('a', 1))
- self.assertEqual(str(version), '1.2.3a1')
+ assert version.version == (1, 2, 3)
+ assert version.prerelease == ('a', 1)
+ assert str(version) == '1.2.3a1'
version = StrictVersion('1.2.0')
- self.assertEqual(str(version), '1.2')
+ assert str(version) == '1.2'
def test_cmp_strict(self):
versions = (
@@ -51,19 +51,17 @@ class VersionTestCase(unittest.TestCase):
raise AssertionError(
("cmp(%s, %s) " "shouldn't raise ValueError") % (v1, v2)
)
- self.assertEqual(
- res, wanted, 'cmp(%s, %s) should be %s, got %s' % (v1, v2, wanted, res)
+ assert res == wanted, 'cmp({}, {}) should be {}, got {}'.format(
+ v1, v2, wanted, res
)
res = StrictVersion(v1)._cmp(v2)
- self.assertEqual(
- res, wanted, 'cmp(%s, %s) should be %s, got %s' % (v1, v2, wanted, res)
+ assert res == wanted, 'cmp({}, {}) should be {}, got {}'.format(
+ v1, v2, wanted, res
)
res = StrictVersion(v1)._cmp(object())
- self.assertIs(
- res,
- NotImplemented,
- 'cmp(%s, %s) should be NotImplemented, got %s' % (v1, v2, res),
- )
+ assert (
+ res is NotImplemented
+ ), 'cmp({}, {}) should be NotImplemented, got {}'.format(v1, v2, res)
def test_cmp(self):
versions = (
@@ -79,16 +77,14 @@ class VersionTestCase(unittest.TestCase):
for v1, v2, wanted in versions:
res = LooseVersion(v1)._cmp(LooseVersion(v2))
- self.assertEqual(
- res, wanted, 'cmp(%s, %s) should be %s, got %s' % (v1, v2, wanted, res)
+ assert res == wanted, 'cmp({}, {}) should be {}, got {}'.format(
+ v1, v2, wanted, res
)
res = LooseVersion(v1)._cmp(v2)
- self.assertEqual(
- res, wanted, 'cmp(%s, %s) should be %s, got %s' % (v1, v2, wanted, res)
+ assert res == wanted, 'cmp({}, {}) should be {}, got {}'.format(
+ v1, v2, wanted, res
)
res = LooseVersion(v1)._cmp(object())
- self.assertIs(
- res,
- NotImplemented,
- 'cmp(%s, %s) should be NotImplemented, got %s' % (v1, v2, res),
- )
+ assert (
+ res is NotImplemented
+ ), 'cmp({}, {}) should be NotImplemented, got {}'.format(v1, v2, res)
diff --git a/distutils/tests/unix_compat.py b/distutils/tests/unix_compat.py
index 8250b363..95fc8eeb 100644
--- a/distutils/tests/unix_compat.py
+++ b/distutils/tests/unix_compat.py
@@ -1,5 +1,4 @@
import sys
-import unittest
try:
import grp
@@ -7,9 +6,13 @@ try:
except ImportError:
grp = pwd = None
+import pytest
+
UNIX_ID_SUPPORT = grp and pwd
UID_0_SUPPORT = UNIX_ID_SUPPORT and sys.platform != "cygwin"
-require_unix_id = unittest.skipUnless(UNIX_ID_SUPPORT, "Requires grp and pwd support")
-require_uid_0 = unittest.skipUnless(UID_0_SUPPORT, "Requires UID 0 support")
+require_unix_id = pytest.mark.skipif(
+ not UNIX_ID_SUPPORT, reason="Requires grp and pwd support"
+)
+require_uid_0 = pytest.mark.skipif(not UID_0_SUPPORT, reason="Requires UID 0 support")
diff --git a/distutils/tests/xxmodule-3.8.c b/distutils/tests/xxmodule-3.8.c
new file mode 100644
index 00000000..0250031d
--- /dev/null
+++ b/distutils/tests/xxmodule-3.8.c
@@ -0,0 +1,411 @@
+
+/* Use this file as a template to start implementing a module that
+ also declares object types. All occurrences of 'Xxo' should be changed
+ to something reasonable for your objects. After that, all other
+ occurrences of 'xx' should be changed to something reasonable for your
+ module. If your module is named foo your sourcefile should be named
+ foomodule.c.
+
+ You will probably want to delete all references to 'x_attr' and add
+ your own types of attributes instead. Maybe you want to name your
+ local variables other than 'self'. If your object type is needed in
+ other files, you'll have to create a file "foobarobject.h"; see
+ floatobject.h for an example. */
+
+/* Xxo objects */
+
+#include "Python.h"
+
+static PyObject *ErrorObject;
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *x_attr; /* Attributes dictionary */
+} XxoObject;
+
+static PyTypeObject Xxo_Type;
+
+#define XxoObject_Check(v) (Py_TYPE(v) == &Xxo_Type)
+
+static XxoObject *
+newXxoObject(PyObject *arg)
+{
+ XxoObject *self;
+ self = PyObject_New(XxoObject, &Xxo_Type);
+ if (self == NULL)
+ return NULL;
+ self->x_attr = NULL;
+ return self;
+}
+
+/* Xxo methods */
+
+static void
+Xxo_dealloc(XxoObject *self)
+{
+ Py_XDECREF(self->x_attr);
+ PyObject_Del(self);
+}
+
+static PyObject *
+Xxo_demo(XxoObject *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ":demo"))
+ return NULL;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef Xxo_methods[] = {
+ {"demo", (PyCFunction)Xxo_demo, METH_VARARGS,
+ PyDoc_STR("demo() -> None")},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject *
+Xxo_getattro(XxoObject *self, PyObject *name)
+{
+ if (self->x_attr != NULL) {
+ PyObject *v = PyDict_GetItemWithError(self->x_attr, name);
+ if (v != NULL) {
+ Py_INCREF(v);
+ return v;
+ }
+ else if (PyErr_Occurred()) {
+ return NULL;
+ }
+ }
+ return PyObject_GenericGetAttr((PyObject *)self, name);
+}
+
+static int
+Xxo_setattr(XxoObject *self, const char *name, PyObject *v)
+{
+ if (self->x_attr == NULL) {
+ self->x_attr = PyDict_New();
+ if (self->x_attr == NULL)
+ return -1;
+ }
+ if (v == NULL) {
+ int rv = PyDict_DelItemString(self->x_attr, name);
+ if (rv < 0 && PyErr_ExceptionMatches(PyExc_KeyError))
+ PyErr_SetString(PyExc_AttributeError,
+ "delete non-existing Xxo attribute");
+ return rv;
+ }
+ else
+ return PyDict_SetItemString(self->x_attr, name, v);
+}
+
+static PyTypeObject Xxo_Type = {
+ /* The ob_type field must be initialized in the module init function
+ * to be portable to Windows without using C++. */
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "xxmodule.Xxo", /*tp_name*/
+ sizeof(XxoObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)Xxo_dealloc, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ (getattrfunc)0, /*tp_getattr*/
+ (setattrfunc)Xxo_setattr, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ (getattrofunc)Xxo_getattro, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ Xxo_methods, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ 0, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+};
+/* --------------------------------------------------------------------- */
+
+/* Function of two integers returning integer */
+
+PyDoc_STRVAR(xx_foo_doc,
+"foo(i,j)\n\
+\n\
+Return the sum of i and j.");
+
+static PyObject *
+xx_foo(PyObject *self, PyObject *args)
+{
+ long i, j;
+ long res;
+ if (!PyArg_ParseTuple(args, "ll:foo", &i, &j))
+ return NULL;
+ res = i+j; /* XXX Do something here */
+ return PyLong_FromLong(res);
+}
+
+
+/* Function of no arguments returning new Xxo object */
+
+static PyObject *
+xx_new(PyObject *self, PyObject *args)
+{
+ XxoObject *rv;
+
+ if (!PyArg_ParseTuple(args, ":new"))
+ return NULL;
+ rv = newXxoObject(args);
+ if (rv == NULL)
+ return NULL;
+ return (PyObject *)rv;
+}
+
+/* Example with subtle bug from extensions manual ("Thin Ice"). */
+
+static PyObject *
+xx_bug(PyObject *self, PyObject *args)
+{
+ PyObject *list, *item;
+
+ if (!PyArg_ParseTuple(args, "O:bug", &list))
+ return NULL;
+
+ item = PyList_GetItem(list, 0);
+ /* Py_INCREF(item); */
+ PyList_SetItem(list, 1, PyLong_FromLong(0L));
+ PyObject_Print(item, stdout, 0);
+ printf("\n");
+ /* Py_DECREF(item); */
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Test bad format character */
+
+static PyObject *
+xx_roj(PyObject *self, PyObject *args)
+{
+ PyObject *a;
+ long b;
+ if (!PyArg_ParseTuple(args, "O#:roj", &a, &b))
+ return NULL;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+/* ---------- */
+
+static PyTypeObject Str_Type = {
+ /* The ob_type field must be initialized in the module init function
+ * to be portable to Windows without using C++. */
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "xxmodule.Str", /*tp_name*/
+ 0, /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ 0, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /* see PyInit_xx */ /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ 0, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+};
+
+/* ---------- */
+
+static PyObject *
+null_richcompare(PyObject *self, PyObject *other, int op)
+{
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+}
+
+static PyTypeObject Null_Type = {
+ /* The ob_type field must be initialized in the module init function
+ * to be portable to Windows without using C++. */
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "xxmodule.Null", /*tp_name*/
+ 0, /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ 0, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ null_richcompare, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /* see PyInit_xx */ /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ PyType_GenericNew, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+};
+
+
+/* ---------- */
+
+
+/* List of functions defined in the module */
+
+static PyMethodDef xx_methods[] = {
+ {"roj", xx_roj, METH_VARARGS,
+ PyDoc_STR("roj(a,b) -> None")},
+ {"foo", xx_foo, METH_VARARGS,
+ xx_foo_doc},
+ {"new", xx_new, METH_VARARGS,
+ PyDoc_STR("new() -> new Xx object")},
+ {"bug", xx_bug, METH_VARARGS,
+ PyDoc_STR("bug(o) -> None")},
+ {NULL, NULL} /* sentinel */
+};
+
+PyDoc_STRVAR(module_doc,
+"This is a template module just for instruction.");
+
+
+static int
+xx_exec(PyObject *m)
+{
+ /* Slot initialization is subject to the rules of initializing globals.
+ C99 requires the initializers to be "address constants". Function
+ designators like 'PyType_GenericNew', with implicit conversion to
+ a pointer, are valid C99 address constants.
+
+ However, the unary '&' operator applied to a non-static variable
+ like 'PyBaseObject_Type' is not required to produce an address
+ constant. Compilers may support this (gcc does), MSVC does not.
+
+ Both compilers are strictly standard conforming in this particular
+ behavior.
+ */
+ Null_Type.tp_base = &PyBaseObject_Type;
+ Str_Type.tp_base = &PyUnicode_Type;
+
+ /* Finalize the type object including setting type of the new type
+ * object; doing it here is required for portability, too. */
+ if (PyType_Ready(&Xxo_Type) < 0)
+ goto fail;
+
+ /* Add some symbolic constants to the module */
+ if (ErrorObject == NULL) {
+ ErrorObject = PyErr_NewException("xx.error", NULL, NULL);
+ if (ErrorObject == NULL)
+ goto fail;
+ }
+ Py_INCREF(ErrorObject);
+ PyModule_AddObject(m, "error", ErrorObject);
+
+ /* Add Str */
+ if (PyType_Ready(&Str_Type) < 0)
+ goto fail;
+ PyModule_AddObject(m, "Str", (PyObject *)&Str_Type);
+
+ /* Add Null */
+ if (PyType_Ready(&Null_Type) < 0)
+ goto fail;
+ PyModule_AddObject(m, "Null", (PyObject *)&Null_Type);
+ return 0;
+ fail:
+ Py_XDECREF(m);
+ return -1;
+}
+
+static struct PyModuleDef_Slot xx_slots[] = {
+ {Py_mod_exec, xx_exec},
+ {0, NULL},
+};
+
+static struct PyModuleDef xxmodule = {
+ PyModuleDef_HEAD_INIT,
+ "xx",
+ module_doc,
+ 0,
+ xx_methods,
+ xx_slots,
+ NULL,
+ NULL,
+ NULL
+};
+
+/* Export function for the module (*must* be called PyInit_xx) */
+
+PyMODINIT_FUNC
+PyInit_xx(void)
+{
+ return PyModuleDef_Init(&xxmodule);
+}
diff --git a/distutils/tests/xxmodule.c b/distutils/tests/xxmodule.c
new file mode 100644
index 00000000..a6e5071d
--- /dev/null
+++ b/distutils/tests/xxmodule.c
@@ -0,0 +1,412 @@
+
+/* Use this file as a template to start implementing a module that
+ also declares object types. All occurrences of 'Xxo' should be changed
+ to something reasonable for your objects. After that, all other
+ occurrences of 'xx' should be changed to something reasonable for your
+ module. If your module is named foo your sourcefile should be named
+ foomodule.c.
+
+ You will probably want to delete all references to 'x_attr' and add
+ your own types of attributes instead. Maybe you want to name your
+ local variables other than 'self'. If your object type is needed in
+ other files, you'll have to create a file "foobarobject.h"; see
+ floatobject.h for an example. */
+
+/* Xxo objects */
+
+#include "Python.h"
+
+static PyObject *ErrorObject;
+
+typedef struct {
+ PyObject_HEAD
+ PyObject *x_attr; /* Attributes dictionary */
+} XxoObject;
+
+static PyTypeObject Xxo_Type;
+
+#define XxoObject_Check(v) Py_IS_TYPE(v, &Xxo_Type)
+
+static XxoObject *
+newXxoObject(PyObject *arg)
+{
+ XxoObject *self;
+ self = PyObject_New(XxoObject, &Xxo_Type);
+ if (self == NULL)
+ return NULL;
+ self->x_attr = NULL;
+ return self;
+}
+
+/* Xxo methods */
+
+static void
+Xxo_dealloc(XxoObject *self)
+{
+ Py_XDECREF(self->x_attr);
+ PyObject_Free(self);
+}
+
+static PyObject *
+Xxo_demo(XxoObject *self, PyObject *args)
+{
+ if (!PyArg_ParseTuple(args, ":demo"))
+ return NULL;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+static PyMethodDef Xxo_methods[] = {
+ {"demo", (PyCFunction)Xxo_demo, METH_VARARGS,
+ PyDoc_STR("demo() -> None")},
+ {NULL, NULL} /* sentinel */
+};
+
+static PyObject *
+Xxo_getattro(XxoObject *self, PyObject *name)
+{
+ if (self->x_attr != NULL) {
+ PyObject *v = PyDict_GetItemWithError(self->x_attr, name);
+ if (v != NULL) {
+ Py_INCREF(v);
+ return v;
+ }
+ else if (PyErr_Occurred()) {
+ return NULL;
+ }
+ }
+ return PyObject_GenericGetAttr((PyObject *)self, name);
+}
+
+static int
+Xxo_setattr(XxoObject *self, const char *name, PyObject *v)
+{
+ if (self->x_attr == NULL) {
+ self->x_attr = PyDict_New();
+ if (self->x_attr == NULL)
+ return -1;
+ }
+ if (v == NULL) {
+ int rv = PyDict_DelItemString(self->x_attr, name);
+ if (rv < 0 && PyErr_ExceptionMatches(PyExc_KeyError))
+ PyErr_SetString(PyExc_AttributeError,
+ "delete non-existing Xxo attribute");
+ return rv;
+ }
+ else
+ return PyDict_SetItemString(self->x_attr, name, v);
+}
+
+static PyTypeObject Xxo_Type = {
+ /* The ob_type field must be initialized in the module init function
+ * to be portable to Windows without using C++. */
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "xxmodule.Xxo", /*tp_name*/
+ sizeof(XxoObject), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ (destructor)Xxo_dealloc, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ (getattrfunc)0, /*tp_getattr*/
+ (setattrfunc)Xxo_setattr, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ (getattrofunc)Xxo_getattro, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ Xxo_methods, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ 0, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+};
+/* --------------------------------------------------------------------- */
+
+/* Function of two integers returning integer */
+
+PyDoc_STRVAR(xx_foo_doc,
+"foo(i,j)\n\
+\n\
+Return the sum of i and j.");
+
+static PyObject *
+xx_foo(PyObject *self, PyObject *args)
+{
+ long i, j;
+ long res;
+ if (!PyArg_ParseTuple(args, "ll:foo", &i, &j))
+ return NULL;
+ res = i+j; /* XXX Do something here */
+ return PyLong_FromLong(res);
+}
+
+
+/* Function of no arguments returning new Xxo object */
+
+static PyObject *
+xx_new(PyObject *self, PyObject *args)
+{
+ XxoObject *rv;
+
+ if (!PyArg_ParseTuple(args, ":new"))
+ return NULL;
+ rv = newXxoObject(args);
+ if (rv == NULL)
+ return NULL;
+ return (PyObject *)rv;
+}
+
+/* Example with subtle bug from extensions manual ("Thin Ice"). */
+
+static PyObject *
+xx_bug(PyObject *self, PyObject *args)
+{
+ PyObject *list, *item;
+
+ if (!PyArg_ParseTuple(args, "O:bug", &list))
+ return NULL;
+
+ item = PyList_GetItem(list, 0);
+ /* Py_INCREF(item); */
+ PyList_SetItem(list, 1, PyLong_FromLong(0L));
+ PyObject_Print(item, stdout, 0);
+ printf("\n");
+ /* Py_DECREF(item); */
+
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+/* Test bad format character */
+
+static PyObject *
+xx_roj(PyObject *self, PyObject *args)
+{
+ PyObject *a;
+ long b;
+ if (!PyArg_ParseTuple(args, "O#:roj", &a, &b))
+ return NULL;
+ Py_INCREF(Py_None);
+ return Py_None;
+}
+
+
+/* ---------- */
+
+static PyTypeObject Str_Type = {
+ /* The ob_type field must be initialized in the module init function
+ * to be portable to Windows without using C++. */
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "xxmodule.Str", /*tp_name*/
+ 0, /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ 0, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /* see PyInit_xx */ /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ 0, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+};
+
+/* ---------- */
+
+static PyObject *
+null_richcompare(PyObject *self, PyObject *other, int op)
+{
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+}
+
+static PyTypeObject Null_Type = {
+ /* The ob_type field must be initialized in the module init function
+ * to be portable to Windows without using C++. */
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "xxmodule.Null", /*tp_name*/
+ 0, /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ /* methods */
+ 0, /*tp_dealloc*/
+ 0, /*tp_vectorcall_offset*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_as_async*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ 0, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ null_richcompare, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /* see PyInit_xx */ /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ 0, /*tp_init*/
+ 0, /*tp_alloc*/
+ PyType_GenericNew, /*tp_new*/
+ 0, /*tp_free*/
+ 0, /*tp_is_gc*/
+};
+
+
+/* ---------- */
+
+
+/* List of functions defined in the module */
+
+static PyMethodDef xx_methods[] = {
+ {"roj", xx_roj, METH_VARARGS,
+ PyDoc_STR("roj(a,b) -> None")},
+ {"foo", xx_foo, METH_VARARGS,
+ xx_foo_doc},
+ {"new", xx_new, METH_VARARGS,
+ PyDoc_STR("new() -> new Xx object")},
+ {"bug", xx_bug, METH_VARARGS,
+ PyDoc_STR("bug(o) -> None")},
+ {NULL, NULL} /* sentinel */
+};
+
+PyDoc_STRVAR(module_doc,
+"This is a template module just for instruction.");
+
+
+static int
+xx_exec(PyObject *m)
+{
+ /* Slot initialization is subject to the rules of initializing globals.
+ C99 requires the initializers to be "address constants". Function
+ designators like 'PyType_GenericNew', with implicit conversion to
+ a pointer, are valid C99 address constants.
+
+ However, the unary '&' operator applied to a non-static variable
+ like 'PyBaseObject_Type' is not required to produce an address
+ constant. Compilers may support this (gcc does), MSVC does not.
+
+ Both compilers are strictly standard conforming in this particular
+ behavior.
+ */
+ Null_Type.tp_base = &PyBaseObject_Type;
+ Str_Type.tp_base = &PyUnicode_Type;
+
+ /* Finalize the type object including setting type of the new type
+ * object; doing it here is required for portability, too. */
+ if (PyType_Ready(&Xxo_Type) < 0) {
+ return -1;
+ }
+
+ /* Add some symbolic constants to the module */
+ if (ErrorObject == NULL) {
+ ErrorObject = PyErr_NewException("xx.error", NULL, NULL);
+ if (ErrorObject == NULL) {
+ return -1;
+ }
+ }
+ int rc = PyModule_AddType(m, (PyTypeObject *)ErrorObject);
+ Py_DECREF(ErrorObject);
+ if (rc < 0) {
+ return -1;
+ }
+
+ /* Add Str and Null types */
+ if (PyModule_AddType(m, &Str_Type) < 0) {
+ return -1;
+ }
+ if (PyModule_AddType(m, &Null_Type) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct PyModuleDef_Slot xx_slots[] = {
+ {Py_mod_exec, xx_exec},
+ {0, NULL},
+};
+
+static struct PyModuleDef xxmodule = {
+ PyModuleDef_HEAD_INIT,
+ "xx",
+ module_doc,
+ 0,
+ xx_methods,
+ xx_slots,
+ NULL,
+ NULL,
+ NULL
+};
+
+/* Export function for the module (*must* be called PyInit_xx) */
+
+PyMODINIT_FUNC
+PyInit_xx(void)
+{
+ return PyModuleDef_Init(&xxmodule);
+}
diff --git a/distutils/text_file.py b/distutils/text_file.py
index cffcd099..7274d4b1 100644
--- a/distutils/text_file.py
+++ b/distutils/text_file.py
@@ -5,7 +5,6 @@ that (optionally) takes care of stripping comments, ignoring blank
lines, and joining lines with backslashes."""
import sys
-import io
class TextFile:
@@ -116,7 +115,7 @@ class TextFile:
"""Open a new file named 'filename'. This overrides both the
'filename' and 'file' arguments to the constructor."""
self.filename = filename
- self.file = io.open(self.filename, 'r', errors=self.errors)
+ self.file = open(self.filename, errors=self.errors)
self.current_line = 0
def close(self):
diff --git a/distutils/unixccompiler.py b/distutils/unixccompiler.py
index b3eece97..4ab771a4 100644
--- a/distutils/unixccompiler.py
+++ b/distutils/unixccompiler.py
@@ -17,6 +17,7 @@ import os
import sys
import re
import shlex
+import itertools
from distutils import sysconfig
from distutils.dep_util import newer
@@ -163,17 +164,21 @@ class UnixCCompiler(CCompiler):
pp_args.extend(extra_postargs)
pp_args.append(source)
- # We need to preprocess: either we're being forced to, or we're
- # generating output to stdout, or there's a target output file and
- # the source file is newer than the target (or the target doesn't
- # exist).
- if self.force or output_file is None or newer(source, output_file):
- if output_file:
- self.mkpath(os.path.dirname(output_file))
- try:
- self.spawn(pp_args)
- except DistutilsExecError as msg:
- raise CompileError(msg)
+ # reasons to preprocess:
+ # - force is indicated
+ # - output is directed to stdout
+ # - source file is newer than the target
+ preprocess = self.force or output_file is None or newer(source, output_file)
+ if not preprocess:
+ return
+
+ if output_file:
+ self.mkpath(os.path.dirname(output_file))
+
+ try:
+ self.spawn(pp_args)
+ except DistutilsExecError as msg:
+ raise CompileError(msg)
def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
compiler_so = compiler_fixup(self.compiler_so, cc_args + extra_postargs)
@@ -320,66 +325,77 @@ class UnixCCompiler(CCompiler):
def library_option(self, lib):
return "-l" + lib
- def find_library_file(self, dirs, lib, debug=0):
- shared_f = self.library_filename(lib, lib_type='shared')
- dylib_f = self.library_filename(lib, lib_type='dylib')
- xcode_stub_f = self.library_filename(lib, lib_type='xcode_stub')
- static_f = self.library_filename(lib, lib_type='static')
-
- if sys.platform == 'darwin':
- # On OSX users can specify an alternate SDK using
- # '-isysroot', calculate the SDK root if it is specified
- # (and use it further on)
- #
- # Note that, as of Xcode 7, Apple SDKs may contain textual stub
- # libraries with .tbd extensions rather than the normal .dylib
- # shared libraries installed in /. The Apple compiler tool
- # chain handles this transparently but it can cause problems
- # for programs that are being built with an SDK and searching
- # for specific libraries. Callers of find_library_file need to
- # keep in mind that the base filename of the returned SDK library
- # file might have a different extension from that of the library
- # file installed on the running system, for example:
- # /Applications/Xcode.app/Contents/Developer/Platforms/
- # MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/
- # usr/lib/libedit.tbd
- # vs
- # /usr/lib/libedit.dylib
- cflags = sysconfig.get_config_var('CFLAGS')
- m = re.search(r'-isysroot\s*(\S+)', cflags)
- if m is None:
- sysroot = '/'
- else:
- sysroot = m.group(1)
-
- for dir in dirs:
- shared = os.path.join(dir, shared_f)
- dylib = os.path.join(dir, dylib_f)
- static = os.path.join(dir, static_f)
- xcode_stub = os.path.join(dir, xcode_stub_f)
-
- if sys.platform == 'darwin' and (
+ @staticmethod
+ def _library_root(dir):
+ """
+ macOS users can specify an alternate SDK using'-isysroot'.
+ Calculate the SDK root if it is specified.
+
+ Note that, as of Xcode 7, Apple SDKs may contain textual stub
+ libraries with .tbd extensions rather than the normal .dylib
+ shared libraries installed in /. The Apple compiler tool
+ chain handles this transparently but it can cause problems
+ for programs that are being built with an SDK and searching
+ for specific libraries. Callers of find_library_file need to
+ keep in mind that the base filename of the returned SDK library
+ file might have a different extension from that of the library
+ file installed on the running system, for example:
+ /Applications/Xcode.app/Contents/Developer/Platforms/
+ MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/
+ usr/lib/libedit.tbd
+ vs
+ /usr/lib/libedit.dylib
+ """
+ cflags = sysconfig.get_config_var('CFLAGS')
+ match = re.search(r'-isysroot\s*(\S+)', cflags)
+
+ apply_root = (
+ sys.platform == 'darwin'
+ and match
+ and (
dir.startswith('/System/')
or (dir.startswith('/usr/') and not dir.startswith('/usr/local/'))
- ):
-
- shared = os.path.join(sysroot, dir[1:], shared_f)
- dylib = os.path.join(sysroot, dir[1:], dylib_f)
- static = os.path.join(sysroot, dir[1:], static_f)
- xcode_stub = os.path.join(sysroot, dir[1:], xcode_stub_f)
-
- # We're second-guessing the linker here, with not much hard
- # data to go on: GCC seems to prefer the shared library, so I'm
- # assuming that *all* Unix C compilers do. And of course I'm
- # ignoring even GCC's "-static" option. So sue me.
- if os.path.exists(dylib):
- return dylib
- elif os.path.exists(xcode_stub):
- return xcode_stub
- elif os.path.exists(shared):
- return shared
- elif os.path.exists(static):
- return static
-
- # Oops, didn't find it in *any* of 'dirs'
- return None
+ )
+ )
+
+ return os.path.join(match.group(1), dir[1:]) if apply_root else dir
+
+ def find_library_file(self, dirs, lib, debug=0):
+ r"""
+ Second-guess the linker with not much hard
+ data to go on: GCC seems to prefer the shared library, so
+ assume that *all* Unix C compilers do,
+ ignoring even GCC's "-static" option.
+
+ >>> compiler = UnixCCompiler()
+ >>> compiler._library_root = lambda dir: dir
+ >>> monkeypatch = getfixture('monkeypatch')
+ >>> monkeypatch.setattr(os.path, 'exists', lambda d: 'existing' in d)
+ >>> dirs = ('/foo/bar/missing', '/foo/bar/existing')
+ >>> compiler.find_library_file(dirs, 'abc').replace('\\', '/')
+ '/foo/bar/existing/libabc.dylib'
+ >>> compiler.find_library_file(reversed(dirs), 'abc').replace('\\', '/')
+ '/foo/bar/existing/libabc.dylib'
+ >>> monkeypatch.setattr(os.path, 'exists',
+ ... lambda d: 'existing' in d and '.a' in d)
+ >>> compiler.find_library_file(dirs, 'abc').replace('\\', '/')
+ '/foo/bar/existing/libabc.a'
+ >>> compiler.find_library_file(reversed(dirs), 'abc').replace('\\', '/')
+ '/foo/bar/existing/libabc.a'
+ """
+ lib_names = (
+ self.library_filename(lib, lib_type=type)
+ for type in 'dylib xcode_stub shared static'.split()
+ )
+
+ roots = map(self._library_root, dirs)
+
+ searched = (
+ os.path.join(root, lib_name)
+ for root, lib_name in itertools.product(roots, lib_names)
+ )
+
+ found = filter(os.path.exists, searched)
+
+ # Return None if it could not be found in any dir.
+ return next(found, None)
diff --git a/distutils/util.py b/distutils/util.py
index b22cf984..d95992ec 100644
--- a/distutils/util.py
+++ b/distutils/util.py
@@ -334,7 +334,7 @@ def execute(func, args, msg=None, verbose=0, dry_run=0):
print.
"""
if msg is None:
- msg = "%s%r" % (func.__name__, args)
+ msg = "{}{!r}".format(func.__name__, args)
if msg[-2:] == ',)': # correct for singleton tuple
msg = msg[0:-2] + ')'
@@ -356,7 +356,7 @@ def strtobool(val):
elif val in ('n', 'no', 'f', 'false', 'off', '0'):
return 0
else:
- raise ValueError("invalid truth value %r" % (val,))
+ raise ValueError("invalid truth value {!r}".format(val))
def byte_compile( # noqa: C901
diff --git a/distutils/version.py b/distutils/version.py
index 7e33fb7c..e29e2657 100644
--- a/distutils/version.py
+++ b/distutils/version.py
@@ -60,7 +60,7 @@ class Version:
)
def __repr__(self):
- return "%s ('%s')" % (self.__class__.__name__, str(self))
+ return "{} ('{}')".format(self.__class__.__name__, str(self))
def __eq__(self, other):
c = self._cmp(other)
diff --git a/pyproject.toml b/pyproject.toml
index 0097e9f6..e6863cff 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,2 +1,11 @@
[tool.black]
skip-string-normalization = true
+
+[tool.pytest-enabler.black]
+addopts = "--black"
+
+[tool.pytest-enabler.flake8]
+addopts = "--flake8"
+
+[tool.pytest-enabler.cov]
+addopts = "--cov"
diff --git a/pytest.ini b/pytest.ini
index dba42e75..56dcdec4 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,6 +1,23 @@
[pytest]
addopts=--doctest-modules
filterwarnings=
- # acknowledge that TestDistribution isn't a test
- ignore:cannot collect test class 'TestDistribution'
- ignore:Fallback spawn triggered
+ # Suppress deprecation warning in flake8
+ ignore:SelectableGroups dict interface is deprecated::flake8
+
+ # shopkeep/pytest-black#55
+ ignore:<class 'pytest_black.BlackItem'> is not using a cooperative constructor:pytest.PytestDeprecationWarning
+ ignore:The \(fspath. py.path.local\) argument to BlackItem is deprecated.:pytest.PytestDeprecationWarning
+ ignore:BlackItem is an Item subclass and should not be a collector:pytest.PytestWarning
+
+ # tholo/pytest-flake8#83
+ ignore:<class 'pytest_flake8.Flake8Item'> is not using a cooperative constructor:pytest.PytestDeprecationWarning
+ ignore:The \(fspath. py.path.local\) argument to Flake8Item is deprecated.:pytest.PytestDeprecationWarning
+ ignore:Flake8Item is an Item subclass and should not be a collector:pytest.PytestWarning
+
+ # acknowledge that TestDistribution isn't a test
+ ignore:cannot collect test class 'TestDistribution'
+ ignore:Fallback spawn triggered
+
+ # ignore spurious and unactionable warnings
+ ignore:The frontend.OptionParser class will be replaced by a subclass of argparse.ArgumentParser in Docutils 0.21 or later.:DeprecationWarning:
+ ignore: The frontend.Option class will be removed in Docutils 0.21 or later.:DeprecationWarning:
diff --git a/tox.ini b/tox.ini
index ef3efecf..5facd6b9 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,10 +1,24 @@
[tox]
minversion = 3.25
+toxworkdir={env:TOX_WORK_DIR:.tox}
+
[testenv]
deps =
pytest
+
+ pytest-flake8
+ # workaround for tholo/pytest-flake8#87
+ flake8 < 5
+
+ pytest-black
+ pytest-cov
+ pytest-enabler >= 1.3
+
jaraco.envs>=2.4
+ jaraco.path
+ path
+ docutils
commands =
pytest {posargs}
setenv =