Skip to content

Commit 64f10c5

Browse files
authored
Merge pull request #4539 from pypa/feature/distutils-b7ee725f3
Merge with pypa/distutils@b7ee725f3
2 parents 54936c7 + d4ad24b commit 64f10c5

32 files changed

+256
-431
lines changed

newsfragments/4538.feature.rst

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Merged with distutils@d7ffdb9c7 including: Support for Pathlike objects in data files and extensions (pypa/distutils#272, pypa/distutils#237), native support for C++ compilers (pypa/distuils#228) and removed unused get_msvcr() (pypa/distutils#274).

setuptools/_distutils/_collections.py

-145
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
11
from __future__ import annotations
22

33
import collections
4-
import functools
54
import itertools
6-
import operator
7-
from collections.abc import Mapping
8-
from typing import Any
95

106

117
# from jaraco.collections 3.5.1
@@ -60,144 +56,3 @@ def __contains__(self, other):
6056

6157
def __len__(self):
6258
return len(list(iter(self)))
63-
64-
65-
# from jaraco.collections 5.0.1
66-
class RangeMap(dict):
67-
"""
68-
A dictionary-like object that uses the keys as bounds for a range.
69-
Inclusion of the value for that range is determined by the
70-
key_match_comparator, which defaults to less-than-or-equal.
71-
A value is returned for a key if it is the first key that matches in
72-
the sorted list of keys.
73-
74-
One may supply keyword parameters to be passed to the sort function used
75-
to sort keys (i.e. key, reverse) as sort_params.
76-
77-
Create a map that maps 1-3 -> 'a', 4-6 -> 'b'
78-
79-
>>> r = RangeMap({3: 'a', 6: 'b'}) # boy, that was easy
80-
>>> r[1], r[2], r[3], r[4], r[5], r[6]
81-
('a', 'a', 'a', 'b', 'b', 'b')
82-
83-
Even float values should work so long as the comparison operator
84-
supports it.
85-
86-
>>> r[4.5]
87-
'b'
88-
89-
Notice that the way rangemap is defined, it must be open-ended
90-
on one side.
91-
92-
>>> r[0]
93-
'a'
94-
>>> r[-1]
95-
'a'
96-
97-
One can close the open-end of the RangeMap by using undefined_value
98-
99-
>>> r = RangeMap({0: RangeMap.undefined_value, 3: 'a', 6: 'b'})
100-
>>> r[0]
101-
Traceback (most recent call last):
102-
...
103-
KeyError: 0
104-
105-
One can get the first or last elements in the range by using RangeMap.Item
106-
107-
>>> last_item = RangeMap.Item(-1)
108-
>>> r[last_item]
109-
'b'
110-
111-
.last_item is a shortcut for Item(-1)
112-
113-
>>> r[RangeMap.last_item]
114-
'b'
115-
116-
Sometimes it's useful to find the bounds for a RangeMap
117-
118-
>>> r.bounds()
119-
(0, 6)
120-
121-
RangeMap supports .get(key, default)
122-
123-
>>> r.get(0, 'not found')
124-
'not found'
125-
126-
>>> r.get(7, 'not found')
127-
'not found'
128-
129-
One often wishes to define the ranges by their left-most values,
130-
which requires use of sort params and a key_match_comparator.
131-
132-
>>> r = RangeMap({1: 'a', 4: 'b'},
133-
... sort_params=dict(reverse=True),
134-
... key_match_comparator=operator.ge)
135-
>>> r[1], r[2], r[3], r[4], r[5], r[6]
136-
('a', 'a', 'a', 'b', 'b', 'b')
137-
138-
That wasn't nearly as easy as before, so an alternate constructor
139-
is provided:
140-
141-
>>> r = RangeMap.left({1: 'a', 4: 'b', 7: RangeMap.undefined_value})
142-
>>> r[1], r[2], r[3], r[4], r[5], r[6]
143-
('a', 'a', 'a', 'b', 'b', 'b')
144-
145-
"""
146-
147-
def __init__(
148-
self,
149-
source,
150-
sort_params: Mapping[str, Any] = {},
151-
key_match_comparator=operator.le,
152-
):
153-
dict.__init__(self, source)
154-
self.sort_params = sort_params
155-
self.match = key_match_comparator
156-
157-
@classmethod
158-
def left(cls, source):
159-
return cls(
160-
source, sort_params=dict(reverse=True), key_match_comparator=operator.ge
161-
)
162-
163-
def __getitem__(self, item):
164-
sorted_keys = sorted(self.keys(), **self.sort_params)
165-
if isinstance(item, RangeMap.Item):
166-
result = self.__getitem__(sorted_keys[item])
167-
else:
168-
key = self._find_first_match_(sorted_keys, item)
169-
result = dict.__getitem__(self, key)
170-
if result is RangeMap.undefined_value:
171-
raise KeyError(key)
172-
return result
173-
174-
def get(self, key, default=None):
175-
"""
176-
Return the value for key if key is in the dictionary, else default.
177-
If default is not given, it defaults to None, so that this method
178-
never raises a KeyError.
179-
"""
180-
try:
181-
return self[key]
182-
except KeyError:
183-
return default
184-
185-
def _find_first_match_(self, keys, item):
186-
is_match = functools.partial(self.match, item)
187-
matches = list(filter(is_match, keys))
188-
if matches:
189-
return matches[0]
190-
raise KeyError(item)
191-
192-
def bounds(self):
193-
sorted_keys = sorted(self.keys(), **self.sort_params)
194-
return (sorted_keys[RangeMap.first_item], sorted_keys[RangeMap.last_item])
195-
196-
# some special values for the RangeMap
197-
undefined_value = type('RangeValueUndefined', (), {})()
198-
199-
class Item(int):
200-
"RangeMap Item"
201-
202-
first_item = Item(0)
203-
last_item = Item(-1)

setuptools/_distutils/bcppcompiler.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -236,8 +236,7 @@ def link( # noqa: C901
236236
temp_dir = os.path.dirname(objects[0]) # preserve tree structure
237237
def_file = os.path.join(temp_dir, f'{modname}.def')
238238
contents = ['EXPORTS']
239-
for sym in export_symbols or []:
240-
contents.append(f' {sym}=_{sym}')
239+
contents.extend(f' {sym}=_{sym}' for sym in export_symbols)
241240
self.execute(write_file, (def_file, contents), f"writing {def_file}")
242241

243242
# Borland C++ has problems with '/' in paths

setuptools/_distutils/ccompiler.py

+7-11
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
)
2323
from .file_util import move_file
2424
from .spawn import spawn
25-
from .util import execute, split_quoted, is_mingw
25+
from .util import execute, is_mingw, split_quoted
2626

2727

2828
class CCompiler:
@@ -1124,10 +1124,10 @@ def show_compilers():
11241124
# commands that use it.
11251125
from distutils.fancy_getopt import FancyGetopt
11261126

1127-
compilers = []
1128-
for compiler in compiler_class.keys():
1129-
compilers.append(("compiler=" + compiler, None, compiler_class[compiler][2]))
1130-
compilers.sort()
1127+
compilers = sorted(
1128+
("compiler=" + compiler, None, compiler_class[compiler][2])
1129+
for compiler in compiler_class.keys()
1130+
)
11311131
pretty_printer = FancyGetopt(compilers)
11321132
pretty_printer.print_help("List of available compilers:")
11331133

@@ -1218,8 +1218,7 @@ def gen_preprocess_options(macros, include_dirs):
12181218
# shell at all costs when we spawn the command!
12191219
pp_opts.append("-D{}={}".format(*macro))
12201220

1221-
for dir in include_dirs:
1222-
pp_opts.append(f"-I{dir}")
1221+
pp_opts.extend(f"-I{dir}" for dir in include_dirs)
12231222
return pp_opts
12241223

12251224

@@ -1230,10 +1229,7 @@ def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries):
12301229
directories. Returns a list of command-line options suitable for use
12311230
with some compiler (depending on the two format strings passed in).
12321231
"""
1233-
lib_opts = []
1234-
1235-
for dir in library_dirs:
1236-
lib_opts.append(compiler.library_dir_option(dir))
1232+
lib_opts = [compiler.library_dir_option(dir) for dir in library_dirs]
12371233

12381234
for dir in runtime_library_dirs:
12391235
lib_opts.extend(always_iterable(compiler.runtime_library_dir_option(dir)))

setuptools/_distutils/command/bdist.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,10 @@ def show_formats():
1515
"""Print list of available formats (arguments to "--format" option)."""
1616
from ..fancy_getopt import FancyGetopt
1717

18-
formats = []
19-
for format in bdist.format_commands:
20-
formats.append(("formats=" + format, None, bdist.format_commands[format][1]))
18+
formats = [
19+
("formats=" + format, None, bdist.format_commands[format][1])
20+
for format in bdist.format_commands
21+
]
2122
pretty_printer = FancyGetopt(formats)
2223
pretty_printer.print_help("List of available distribution formats:")
2324

setuptools/_distutils/command/build_ext.py

+1-4
Original file line numberDiff line numberDiff line change
@@ -465,10 +465,7 @@ def get_outputs(self):
465465
# And build the list of output (built) filenames. Note that this
466466
# ignores the 'inplace' flag, and assumes everything goes in the
467467
# "build" tree.
468-
outputs = []
469-
for ext in self.extensions:
470-
outputs.append(self.get_ext_fullpath(ext.name))
471-
return outputs
468+
return [self.get_ext_fullpath(ext.name) for ext in self.extensions]
472469

473470
def build_extensions(self):
474471
# First, sanity-check the 'extensions' list

setuptools/_distutils/command/check.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,9 @@ def check_metadata(self):
100100
"""
101101
metadata = self.distribution.metadata
102102

103-
missing = []
104-
for attr in 'name', 'version':
105-
if not getattr(metadata, attr, None):
106-
missing.append(attr)
103+
missing = [
104+
attr for attr in ('name', 'version') if not getattr(metadata, attr, None)
105+
]
107106

108107
if missing:
109108
self.warn("missing required meta-data: {}".format(', '.join(missing)))

setuptools/_distutils/command/install_data.py

+39-29
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@
55

66
# contributed by Bastian Kleineidam
77

8+
from __future__ import annotations
9+
10+
import functools
811
import os
12+
from typing import Iterable
913

1014
from ..core import Command
1115
from ..util import change_root, convert_path
@@ -46,36 +50,42 @@ def finalize_options(self):
4650
def run(self):
4751
self.mkpath(self.install_dir)
4852
for f in self.data_files:
49-
if isinstance(f, str):
50-
# it's a simple file, so copy it
51-
f = convert_path(f)
52-
if self.warn_dir:
53-
self.warn(
54-
"setup script did not provide a directory for "
55-
f"'{f}' -- installing right in '{self.install_dir}'"
56-
)
57-
(out, _) = self.copy_file(f, self.install_dir)
53+
self._copy(f)
54+
55+
@functools.singledispatchmethod
56+
def _copy(self, f: tuple[str | os.PathLike, Iterable[str | os.PathLike]]):
57+
# it's a tuple with path to install to and a list of files
58+
dir = convert_path(f[0])
59+
if not os.path.isabs(dir):
60+
dir = os.path.join(self.install_dir, dir)
61+
elif self.root:
62+
dir = change_root(self.root, dir)
63+
self.mkpath(dir)
64+
65+
if f[1] == []:
66+
# If there are no files listed, the user must be
67+
# trying to create an empty directory, so add the
68+
# directory to the list of output files.
69+
self.outfiles.append(dir)
70+
else:
71+
# Copy files, adding them to the list of output files.
72+
for data in f[1]:
73+
data = convert_path(data)
74+
(out, _) = self.copy_file(data, dir)
5875
self.outfiles.append(out)
59-
else:
60-
# it's a tuple with path to install to and a list of files
61-
dir = convert_path(f[0])
62-
if not os.path.isabs(dir):
63-
dir = os.path.join(self.install_dir, dir)
64-
elif self.root:
65-
dir = change_root(self.root, dir)
66-
self.mkpath(dir)
67-
68-
if f[1] == []:
69-
# If there are no files listed, the user must be
70-
# trying to create an empty directory, so add the
71-
# directory to the list of output files.
72-
self.outfiles.append(dir)
73-
else:
74-
# Copy files, adding them to the list of output files.
75-
for data in f[1]:
76-
data = convert_path(data)
77-
(out, _) = self.copy_file(data, dir)
78-
self.outfiles.append(out)
76+
77+
@_copy.register(str)
78+
@_copy.register(os.PathLike)
79+
def _(self, f: str | os.PathLike):
80+
# it's a simple file, so copy it
81+
f = convert_path(f)
82+
if self.warn_dir:
83+
self.warn(
84+
"setup script did not provide a directory for "
85+
f"'{f}' -- installing right in '{self.install_dir}'"
86+
)
87+
(out, _) = self.copy_file(f, self.install_dir)
88+
self.outfiles.append(out)
7989

8090
def get_inputs(self):
8191
return self.data_files or []

setuptools/_distutils/command/install_lib.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,7 @@ def _mutate_outputs(self, has_any, build_cmd, cmd_option, output_dir):
161161
build_dir = getattr(build_cmd, cmd_option)
162162

163163
prefix_len = len(build_dir) + len(os.sep)
164-
outputs = []
165-
for file in build_files:
166-
outputs.append(os.path.join(output_dir, file[prefix_len:]))
164+
outputs = [os.path.join(output_dir, file[prefix_len:]) for file in build_files]
167165

168166
return outputs
169167

setuptools/_distutils/command/sdist.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ def show_formats():
2424
from ..archive_util import ARCHIVE_FORMATS
2525
from ..fancy_getopt import FancyGetopt
2626

27-
formats = []
28-
for format in ARCHIVE_FORMATS.keys():
29-
formats.append(("formats=" + format, None, ARCHIVE_FORMATS[format][2]))
30-
formats.sort()
27+
formats = sorted(
28+
("formats=" + format, None, ARCHIVE_FORMATS[format][2])
29+
for format in ARCHIVE_FORMATS.keys()
30+
)
3131
FancyGetopt(formats).print_help("List of available source distribution formats:")
3232

3333

setuptools/_distutils/compat/py38.py

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ def removeprefix(self, prefix):
1414
return self[len(prefix) :]
1515
else:
1616
return self[:]
17+
1718
else:
1819

1920
def removesuffix(self, suffix):

0 commit comments

Comments
 (0)