Skip to content

Commit 520336e

Browse files
authored
Merge pull request #4103 from pypa/feature/distutils-sync
Sync latest distutils
2 parents d42e248 + c614ef5 commit 520336e

21 files changed

+273
-176
lines changed

docs/deprecated/distutils/configfile.rst

-7
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,6 @@ Note that the ``doc_files`` option is simply a whitespace-separated string
131131
split across multiple lines for readability.
132132

133133

134-
.. seealso::
135-
136-
:ref:`inst-config-syntax` in "Installing Python Modules"
137-
More information on the configuration files is available in the manual for
138-
system administrators.
139-
140-
141134
.. rubric:: Footnotes
142135

143136
.. [#] This ideal probably won't be achieved until auto-configuration is fully

docs/deprecated/distutils/packageindex.rst

+6-7
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@
66
The Python Package Index (PyPI)
77
*******************************
88

9-
The `Python Package Index (PyPI)`_ stores metadata describing distributions
10-
packaged with distutils and other publishing tools, as well the distribution
11-
archives themselves.
9+
The `Python Package Index (PyPI) <https://pypi.org>`_ stores
10+
metadata describing distributions packaged with distutils and
11+
other publishing tools, as well the distribution archives
12+
themselves.
1213

13-
References to up to date PyPI documentation can be found at
14-
:ref:`publishing-python-packages`.
15-
16-
.. _Python Package Index (PyPI): https://pypi.org
14+
The best resource for working with PyPI is the
15+
`Python Packaging User Guide <https://packaging.python.org>`_.

docs/deprecated/distutils/uploading.rst

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
Uploading Packages to the Package Index
55
***************************************
66

7-
References to up to date PyPI documentation can be found at
8-
:ref:`publishing-python-packages`.
7+
See the
8+
`Python Packaging User Guide <https://packaging.python.org>`_
9+
for the best guidance on uploading packages.

newsfragments/+f8383dcd.feature.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Merged with pypa/distutils@7a04cbda0fc714.

setuptools/_distutils/_functools.py

+53
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import collections.abc
12
import functools
23

34

@@ -18,3 +19,55 @@ def wrapper(param, *args, **kwargs):
1819
return func(param, *args, **kwargs)
1920

2021
return wrapper
22+
23+
24+
# from jaraco.functools 4.0
25+
@functools.singledispatch
26+
def _splat_inner(args, func):
27+
"""Splat args to func."""
28+
return func(*args)
29+
30+
31+
@_splat_inner.register
32+
def _(args: collections.abc.Mapping, func):
33+
"""Splat kargs to func as kwargs."""
34+
return func(**args)
35+
36+
37+
def splat(func):
38+
"""
39+
Wrap func to expect its parameters to be passed positionally in a tuple.
40+
41+
Has a similar effect to that of ``itertools.starmap`` over
42+
simple ``map``.
43+
44+
>>> import itertools, operator
45+
>>> pairs = [(-1, 1), (0, 2)]
46+
>>> _ = tuple(itertools.starmap(print, pairs))
47+
-1 1
48+
0 2
49+
>>> _ = tuple(map(splat(print), pairs))
50+
-1 1
51+
0 2
52+
53+
The approach generalizes to other iterators that don't have a "star"
54+
equivalent, such as a "starfilter".
55+
56+
>>> list(filter(splat(operator.add), pairs))
57+
[(0, 2)]
58+
59+
Splat also accepts a mapping argument.
60+
61+
>>> def is_nice(msg, code):
62+
... return "smile" in msg or code == 0
63+
>>> msgs = [
64+
... dict(msg='smile!', code=20),
65+
... dict(msg='error :(', code=1),
66+
... dict(msg='unknown', code=0),
67+
... ]
68+
>>> for msg in filter(splat(is_nice), msgs):
69+
... print(msg)
70+
{'msg': 'smile!', 'code': 20}
71+
{'msg': 'unknown', 'code': 0}
72+
"""
73+
return functools.wraps(func)(functools.partial(_splat_inner, func=func))

setuptools/_distutils/_modified.py

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"""Timestamp comparison of files and groups of files."""
2+
3+
import functools
4+
import os.path
5+
6+
from .errors import DistutilsFileError
7+
from .py39compat import zip_strict
8+
from ._functools import splat
9+
10+
11+
def _newer(source, target):
12+
return not os.path.exists(target) or (
13+
os.path.getmtime(source) > os.path.getmtime(target)
14+
)
15+
16+
17+
def newer(source, target):
18+
"""
19+
Is source modified more recently than target.
20+
21+
Returns True if 'source' is modified more recently than
22+
'target' or if 'target' does not exist.
23+
24+
Raises DistutilsFileError if 'source' does not exist.
25+
"""
26+
if not os.path.exists(source):
27+
raise DistutilsFileError("file '%s' does not exist" % os.path.abspath(source))
28+
29+
return _newer(source, target)
30+
31+
32+
def newer_pairwise(sources, targets, newer=newer):
33+
"""
34+
Filter filenames where sources are newer than targets.
35+
36+
Walk two filename iterables in parallel, testing if each source is newer
37+
than its corresponding target. Returns a pair of lists (sources,
38+
targets) where source is newer than target, according to the semantics
39+
of 'newer()'.
40+
"""
41+
newer_pairs = filter(splat(newer), zip_strict(sources, targets))
42+
return tuple(map(list, zip(*newer_pairs))) or ([], [])
43+
44+
45+
def newer_group(sources, target, missing='error'):
46+
"""
47+
Is target out-of-date with respect to any file in sources.
48+
49+
Return True if 'target' is out-of-date with respect to any file
50+
listed in 'sources'. In other words, if 'target' exists and is newer
51+
than every file in 'sources', return False; otherwise return True.
52+
``missing`` controls how to handle a missing source file:
53+
54+
- error (default): allow the ``stat()`` call to fail.
55+
- ignore: silently disregard any missing source files.
56+
- newer: treat missing source files as "target out of date". This
57+
mode is handy in "dry-run" mode: it will pretend to carry out
58+
commands that wouldn't work because inputs are missing, but
59+
that doesn't matter because dry-run won't run the commands.
60+
"""
61+
62+
def missing_as_newer(source):
63+
return missing == 'newer' and not os.path.exists(source)
64+
65+
ignored = os.path.exists if missing == 'ignore' else None
66+
return any(
67+
missing_as_newer(source) or _newer(source, target)
68+
for source in filter(ignored, sources)
69+
)
70+
71+
72+
newer_pairwise_group = functools.partial(newer_pairwise, newer=newer_group)

setuptools/_distutils/bcppcompiler.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
)
2525
from .ccompiler import CCompiler, gen_preprocess_options
2626
from .file_util import write_file
27-
from .dep_util import newer
27+
from ._modified import newer
2828
from ._log import log
2929

3030

setuptools/_distutils/ccompiler.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from .spawn import spawn
1919
from .file_util import move_file
2020
from .dir_util import mkpath
21-
from .dep_util import newer_group
21+
from ._modified import newer_group
2222
from .util import split_quoted, execute
2323
from ._log import log
2424

setuptools/_distutils/cmd.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
import logging
1111

1212
from .errors import DistutilsOptionError
13-
from . import util, dir_util, file_util, archive_util, dep_util
13+
from . import util, dir_util, file_util, archive_util, _modified
1414
from ._log import log
1515

1616

@@ -428,7 +428,7 @@ def make_file(
428428
# If 'outfile' must be regenerated (either because it doesn't
429429
# exist, is out-of-date, or the 'force' flag is true) then
430430
# perform the action that presumably regenerates it
431-
if self.force or dep_util.newer_group(infiles, outfile):
431+
if self.force or _modified.newer_group(infiles, outfile):
432432
self.execute(func, args, exec_msg, level)
433433
# Otherwise, print the "skip" message
434434
else:

setuptools/_distutils/command/build_ext.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
)
2020
from ..sysconfig import customize_compiler, get_python_version
2121
from ..sysconfig import get_config_h_filename
22-
from ..dep_util import newer_group
22+
from .._modified import newer_group
2323
from ..extension import Extension
2424
from ..util import get_platform
2525
from distutils._log import log

setuptools/_distutils/command/build_scripts.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from stat import ST_MODE
88
from distutils import sysconfig
99
from ..core import Command
10-
from ..dep_util import newer
10+
from .._modified import newer
1111
from ..util import convert_path
1212
from distutils._log import log
1313
import tokenize

setuptools/_distutils/dep_util.py

+11-93
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,14 @@
1-
"""distutils.dep_util
1+
import warnings
22

3-
Utility functions for simple, timestamp-based dependency of files
4-
and groups of files; also, function based entirely on such
5-
timestamp dependency analysis."""
3+
from . import _modified
64

7-
import os
8-
from .errors import DistutilsFileError
95

10-
11-
def newer(source, target):
12-
"""Return true if 'source' exists and is more recently modified than
13-
'target', or if 'source' exists and 'target' doesn't. Return false if
14-
both exist and 'target' is the same age or younger than 'source'.
15-
Raise DistutilsFileError if 'source' does not exist.
16-
"""
17-
if not os.path.exists(source):
18-
raise DistutilsFileError("file '%s' does not exist" % os.path.abspath(source))
19-
if not os.path.exists(target):
20-
return 1
21-
22-
from stat import ST_MTIME
23-
24-
mtime1 = os.stat(source)[ST_MTIME]
25-
mtime2 = os.stat(target)[ST_MTIME]
26-
27-
return mtime1 > mtime2
28-
29-
30-
# newer ()
31-
32-
33-
def newer_pairwise(sources, targets):
34-
"""Walk two filename lists in parallel, testing if each source is newer
35-
than its corresponding target. Return a pair of lists (sources,
36-
targets) where source is newer than target, according to the semantics
37-
of 'newer()'.
38-
"""
39-
if len(sources) != len(targets):
40-
raise ValueError("'sources' and 'targets' must be same length")
41-
42-
# build a pair of lists (sources, targets) where source is newer
43-
n_sources = []
44-
n_targets = []
45-
for i in range(len(sources)):
46-
if newer(sources[i], targets[i]):
47-
n_sources.append(sources[i])
48-
n_targets.append(targets[i])
49-
50-
return (n_sources, n_targets)
51-
52-
53-
# newer_pairwise ()
54-
55-
56-
def newer_group(sources, target, missing='error'):
57-
"""Return true if 'target' is out-of-date with respect to any file
58-
listed in 'sources'. In other words, if 'target' exists and is newer
59-
than every file in 'sources', return false; otherwise return true.
60-
'missing' controls what we do when a source file is missing; the
61-
default ("error") is to blow up with an OSError from inside 'stat()';
62-
if it is "ignore", we silently drop any missing source files; if it is
63-
"newer", any missing source files make us assume that 'target' is
64-
out-of-date (this is handy in "dry-run" mode: it'll make you pretend to
65-
carry out commands that wouldn't work because inputs are missing, but
66-
that doesn't matter because you're not actually going to run the
67-
commands).
68-
"""
69-
# If the target doesn't even exist, then it's definitely out-of-date.
70-
if not os.path.exists(target):
71-
return 1
72-
73-
# Otherwise we have to find out the hard way: if *any* source file
74-
# is more recent than 'target', then 'target' is out-of-date and
75-
# we can immediately return true. If we fall through to the end
76-
# of the loop, then 'target' is up-to-date and we return false.
77-
from stat import ST_MTIME
78-
79-
target_mtime = os.stat(target)[ST_MTIME]
80-
for source in sources:
81-
if not os.path.exists(source):
82-
if missing == 'error': # blow up when we stat() the file
83-
pass
84-
elif missing == 'ignore': # missing source dropped from
85-
continue # target's dependency list
86-
elif missing == 'newer': # missing source means target is
87-
return 1 # out-of-date
88-
89-
source_mtime = os.stat(source)[ST_MTIME]
90-
if source_mtime > target_mtime:
91-
return 1
92-
else:
93-
return 0
94-
95-
96-
# newer_group ()
6+
def __getattr__(name):
7+
if name not in ['newer', 'newer_group', 'newer_pairwise']:
8+
raise AttributeError(name)
9+
warnings.warn(
10+
"dep_util is Deprecated. Use functions from setuptools instead.",
11+
DeprecationWarning,
12+
stacklevel=2,
13+
)
14+
return getattr(_modified, name)

setuptools/_distutils/file_util.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def copy_file( # noqa: C901
108108
# changing it (ie. it's not already a hard/soft link to src OR
109109
# (not update) and (src newer than dst).
110110

111-
from distutils.dep_util import newer
111+
from distutils._modified import newer
112112
from stat import ST_ATIME, ST_MTIME, ST_MODE, S_IMODE
113113

114114
if not os.path.isfile(src):

0 commit comments

Comments
 (0)