Skip to content

Commit 2dc468c

Browse files
authored
Merge pull request #3496 from pypa/distutils-b65aa40
Merge with pypa/distutils@b65aa40
2 parents 71c44d9 + d6d341f commit 2dc468c

40 files changed

Lines changed: 1378 additions & 719 deletions

changelog.d/3496.misc.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Update to pypa/distutils@b65aa40 including more robust support for library/include dir handling in msvccompiler (pypa/distutils#153) and test suite improvements.

setuptools/_distutils/_msvccompiler.py

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import subprocess
1818
import contextlib
1919
import warnings
20-
import unittest.mock
20+
import unittest.mock as mock
2121

2222
with contextlib.suppress(ImportError):
2323
import winreg
@@ -224,6 +224,18 @@ def __init__(self, verbose=0, dry_run=0, force=0):
224224
self.plat_name = None
225225
self.initialized = False
226226

227+
@classmethod
228+
def _configure(cls, vc_env):
229+
"""
230+
Set class-level include/lib dirs.
231+
"""
232+
cls.include_dirs = cls._parse_path(vc_env.get('include', ''))
233+
cls.library_dirs = cls._parse_path(vc_env.get('lib', ''))
234+
235+
@staticmethod
236+
def _parse_path(val):
237+
return [dir.rstrip(os.sep) for dir in val.split(os.pathsep) if dir]
238+
227239
def initialize(self, plat_name=None):
228240
# multi-init means we would need to check platform same each time...
229241
assert not self.initialized, "don't init multiple times"
@@ -243,6 +255,7 @@ def initialize(self, plat_name=None):
243255
raise DistutilsPlatformError(
244256
"Unable to find a compatible " "Visual Studio installation."
245257
)
258+
self._configure(vc_env)
246259

247260
self._paths = vc_env.get('path', '')
248261
paths = self._paths.split(os.pathsep)
@@ -253,14 +266,6 @@ def initialize(self, plat_name=None):
253266
self.mc = _find_exe("mc.exe", paths) # message compiler
254267
self.mt = _find_exe("mt.exe", paths) # message compiler
255268

256-
for dir in vc_env.get('include', '').split(os.pathsep):
257-
if dir:
258-
self.add_include_dir(dir.rstrip(os.sep))
259-
260-
for dir in vc_env.get('lib', '').split(os.pathsep):
261-
if dir:
262-
self.add_library_dir(dir.rstrip(os.sep))
263-
264269
self.preprocess_options = None
265270
# bpo-38597: Always compile with dynamic linking
266271
# Future releases of Python 3.x will include all past
@@ -554,7 +559,7 @@ def _fallback_spawn(self, cmd, env):
554559
else:
555560
return
556561
warnings.warn("Fallback spawn triggered. Please update distutils monkeypatch.")
557-
with unittest.mock.patch.dict('os.environ', env):
562+
with mock.patch.dict('os.environ', env):
558563
bag.value = super().spawn(cmd)
559564

560565
# -- Miscellaneous methods -----------------------------------------

setuptools/_distutils/archive_util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ def _set_uid_gid(tarinfo):
121121

122122
# compression using `compress`
123123
if compress == 'compress':
124-
warn("'compress' will be deprecated.", PendingDeprecationWarning)
124+
warn("'compress' is deprecated.", DeprecationWarning)
125125
# the option varies depending on the platform
126126
compressed_name = archive_name + compress_ext[compress]
127127
if sys.platform == 'win32':

setuptools/_distutils/ccompiler.py

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,16 @@ class CCompiler:
9191
}
9292
language_order = ["c++", "objc", "c"]
9393

94+
include_dirs = []
95+
"""
96+
include dirs specific to this compiler class
97+
"""
98+
99+
library_dirs = []
100+
"""
101+
library dirs specific to this compiler class
102+
"""
103+
94104
def __init__(self, verbose=0, dry_run=0, force=0):
95105
self.dry_run = dry_run
96106
self.force = force
@@ -324,24 +334,7 @@ def set_link_objects(self, objects):
324334

325335
def _setup_compile(self, outdir, macros, incdirs, sources, depends, extra):
326336
"""Process arguments and decide which source files to compile."""
327-
if outdir is None:
328-
outdir = self.output_dir
329-
elif not isinstance(outdir, str):
330-
raise TypeError("'output_dir' must be a string or None")
331-
332-
if macros is None:
333-
macros = self.macros
334-
elif isinstance(macros, list):
335-
macros = macros + (self.macros or [])
336-
else:
337-
raise TypeError("'macros' (if supplied) must be a list of tuples")
338-
339-
if incdirs is None:
340-
incdirs = self.include_dirs
341-
elif isinstance(incdirs, (list, tuple)):
342-
incdirs = list(incdirs) + (self.include_dirs or [])
343-
else:
344-
raise TypeError("'include_dirs' (if supplied) must be a list of strings")
337+
outdir, macros, incdirs = self._fix_compile_args(outdir, macros, incdirs)
345338

346339
if extra is None:
347340
extra = []
@@ -400,6 +393,9 @@ def _fix_compile_args(self, output_dir, macros, include_dirs):
400393
else:
401394
raise TypeError("'include_dirs' (if supplied) must be a list of strings")
402395

396+
# add include dirs for class
397+
include_dirs += self.__class__.include_dirs
398+
403399
return output_dir, macros, include_dirs
404400

405401
def _prep_compile(self, sources, output_dir, depends=None):
@@ -456,6 +452,9 @@ def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs):
456452
else:
457453
raise TypeError("'library_dirs' (if supplied) must be a list of strings")
458454

455+
# add library dirs for class
456+
library_dirs += self.__class__.library_dirs
457+
459458
if runtime_library_dirs is None:
460459
runtime_library_dirs = self.runtime_library_dirs
461460
elif isinstance(runtime_library_dirs, (list, tuple)):

setuptools/_distutils/command/check.py

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@
22
33
Implements the Distutils 'check' command.
44
"""
5+
import contextlib
6+
57
from distutils.core import Command
68
from distutils.errors import DistutilsSetupError
79

8-
try:
9-
# docutils is installed
10-
from docutils.utils import Reporter
11-
from docutils.parsers.rst import Parser
12-
from docutils import frontend
13-
from docutils import nodes
10+
with contextlib.suppress(ImportError):
11+
import docutils.utils
12+
import docutils.parsers.rst
13+
import docutils.frontend
14+
import docutils.nodes
1415

15-
class SilentReporter(Reporter):
16+
class SilentReporter(docutils.utils.Reporter):
1617
def __init__(
1718
self,
1819
source,
@@ -30,16 +31,10 @@ def __init__(
3031

3132
def system_message(self, level, message, *children, **kwargs):
3233
self.messages.append((level, message, children, kwargs))
33-
return nodes.system_message(
34+
return docutils.nodes.system_message(
3435
message, level=level, type=self.levels[level], *children, **kwargs
3536
)
3637

37-
HAS_DOCUTILS = True
38-
except Exception:
39-
# Catch all exceptions because exceptions besides ImportError probably
40-
# indicate that docutils is not ported to Py3k.
41-
HAS_DOCUTILS = False
42-
4338

4439
class check(Command):
4540
"""This command checks the meta-data of the package."""
@@ -81,8 +76,11 @@ def run(self):
8176
if self.metadata:
8277
self.check_metadata()
8378
if self.restructuredtext:
84-
if HAS_DOCUTILS:
85-
self.check_restructuredtext()
79+
if 'docutils' in globals():
80+
try:
81+
self.check_restructuredtext()
82+
except TypeError as exc:
83+
raise DistutilsSetupError(str(exc))
8684
elif self.strict:
8785
raise DistutilsSetupError('The docutils package is needed.')
8886

@@ -124,8 +122,10 @@ def _check_rst_data(self, data):
124122
"""Returns warnings when the provided data doesn't compile."""
125123
# the include and csv_table directives need this to be a path
126124
source_path = self.distribution.script_name or 'setup.py'
127-
parser = Parser()
128-
settings = frontend.OptionParser(components=(Parser,)).get_default_values()
125+
parser = docutils.parsers.rst.Parser()
126+
settings = docutils.frontend.OptionParser(
127+
components=(docutils.parsers.rst.Parser,)
128+
).get_default_values()
129129
settings.tab_width = 4
130130
settings.pep_references = None
131131
settings.rfc_references = None
@@ -139,7 +139,7 @@ def _check_rst_data(self, data):
139139
error_handler=settings.error_encoding_error_handler,
140140
)
141141

142-
document = nodes.document(settings, reporter, source=source_path)
142+
document = docutils.nodes.document(settings, reporter, source=source_path)
143143
document.note_source(source_path, -1)
144144
try:
145145
parser.parse(data, document)

setuptools/_distutils/command/register.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ def run(self):
6666
def check_metadata(self):
6767
"""Deprecated API."""
6868
warn(
69-
"distutils.command.register.check_metadata is deprecated, \
70-
use the check command instead",
71-
PendingDeprecationWarning,
69+
"distutils.command.register.check_metadata is deprecated; "
70+
"use the check command instead",
71+
DeprecationWarning,
7272
)
7373
check = self.distribution.get_command_obj('check')
7474
check.ensure_finalized()

setuptools/_distutils/tests/py38compat.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,17 @@
4242
)
4343

4444

45+
try:
46+
from test.support.import_helper import (
47+
DirsOnSysPath,
48+
CleanImport,
49+
)
50+
except (ModuleNotFoundError, ImportError):
51+
from test.support import (
52+
DirsOnSysPath,
53+
CleanImport,
54+
)
55+
56+
4557
if sys.version_info < (3, 9):
4658
requires_zlib = lambda: test.support.requires_zlib

setuptools/_distutils/tests/support.py

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import sys
44
import shutil
55
import tempfile
6-
import unittest
76
import sysconfig
87
import itertools
98

@@ -31,9 +30,8 @@ def clear_logs(self):
3130

3231
@pytest.mark.usefixtures('distutils_managed_tempdir')
3332
class TempdirManager:
34-
"""Mix-in class that handles temporary directories for test cases.
35-
36-
This is intended to be used with unittest.TestCase.
33+
"""
34+
Mix-in class that handles temporary directories for test cases.
3735
"""
3836

3937
def mkdtemp(self):
@@ -99,29 +97,12 @@ def test_compile(self):
9997
If the source file can be found, it will be copied to *directory*. If not,
10098
the test will be skipped. Errors during copy are not caught.
10199
"""
102-
filename = _get_xxmodule_path()
103-
if filename is None:
104-
raise unittest.SkipTest(
105-
'cannot find xxmodule.c (test must run in ' 'the python build dir)'
106-
)
107-
shutil.copy(filename, directory)
100+
shutil.copy(_get_xxmodule_path(), os.path.join(directory, 'xxmodule.c'))
108101

109102

110103
def _get_xxmodule_path():
111-
srcdir = sysconfig.get_config_var('srcdir')
112-
candidates = [
113-
# use installed copy if available
114-
os.path.join(os.path.dirname(__file__), 'xxmodule.c'),
115-
# otherwise try using copy from build directory
116-
os.path.join(srcdir, 'Modules', 'xxmodule.c'),
117-
# srcdir mysteriously can be $srcdir/Lib/distutils/tests when
118-
# this file is run from its parent directory, so walk up the
119-
# tree to find the real srcdir
120-
os.path.join(srcdir, '..', '..', '..', 'Modules', 'xxmodule.c'),
121-
]
122-
for path in candidates:
123-
if os.path.exists(path):
124-
return path
104+
source_name = 'xxmodule.c' if sys.version_info > (3, 9) else 'xxmodule-3.8.c'
105+
return os.path.join(os.path.dirname(__file__), source_name)
125106

126107

127108
def fixup_build_ext(cmd):

0 commit comments

Comments
 (0)