py2docfx 0.1.11.dev1824276__py3-none-any.whl → 0.1.11.dev1830301__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. py2docfx/convert_prepare/environment.py +1 -1
  2. py2docfx/convert_prepare/package_info.py +1 -1
  3. py2docfx/convert_prepare/pip_utils.py +1 -1
  4. py2docfx/convert_prepare/tests/test_package_info.py +7 -22
  5. py2docfx/convert_prepare/tests/test_params.py +5 -0
  6. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/_collections.py +0 -145
  7. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/bcppcompiler.py +1 -2
  8. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/ccompiler.py +7 -11
  9. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/command/bdist.py +4 -3
  10. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/command/build_ext.py +1 -4
  11. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/command/check.py +3 -4
  12. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/command/install_data.py +39 -29
  13. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/command/install_lib.py +1 -3
  14. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/command/sdist.py +4 -4
  15. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/compat/py38.py +1 -0
  16. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py +21 -42
  17. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/dist.py +3 -12
  18. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/extension.py +9 -4
  19. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/msvc9compiler.py +1 -3
  20. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/msvccompiler.py +1 -3
  21. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/spawn.py +0 -1
  22. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/sysconfig.py +44 -25
  23. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/tests/test_archive_util.py +1 -1
  24. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/tests/test_build.py +1 -2
  25. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/tests/test_build_ext.py +1 -1
  26. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/tests/test_cygwinccompiler.py +0 -42
  27. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/tests/test_dist.py +1 -1
  28. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/tests/test_extension.py +4 -1
  29. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/tests/test_install_data.py +17 -9
  30. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/tests/test_mingwccompiler.py +5 -4
  31. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/tests/test_spawn.py +1 -1
  32. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/tests/test_sysconfig.py +6 -3
  33. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/tests/test_unixccompiler.py +35 -1
  34. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/tests/test_util.py +4 -24
  35. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/unixccompiler.py +19 -20
  36. py2docfx/venv/0/Lib/site-packages/setuptools/_distutils/util.py +20 -26
  37. py2docfx/venv/0/Lib/site-packages/wheel/__init__.py +3 -0
  38. py2docfx/venv/0/Lib/site-packages/wheel/__main__.py +23 -0
  39. py2docfx/venv/0/Lib/site-packages/wheel/_bdist_wheel.py +604 -0
  40. py2docfx/venv/0/Lib/site-packages/wheel/_setuptools_logging.py +26 -0
  41. py2docfx/venv/0/Lib/site-packages/wheel/bdist_wheel.py +11 -0
  42. py2docfx/venv/0/Lib/site-packages/wheel/cli/__init__.py +155 -0
  43. py2docfx/venv/0/Lib/site-packages/wheel/cli/convert.py +273 -0
  44. py2docfx/venv/0/Lib/site-packages/wheel/cli/pack.py +85 -0
  45. py2docfx/venv/0/Lib/site-packages/wheel/cli/tags.py +139 -0
  46. py2docfx/venv/0/Lib/site-packages/wheel/cli/unpack.py +30 -0
  47. py2docfx/venv/0/Lib/site-packages/wheel/macosx_libfile.py +482 -0
  48. py2docfx/venv/0/Lib/site-packages/wheel/metadata.py +183 -0
  49. py2docfx/venv/0/Lib/site-packages/wheel/util.py +26 -0
  50. py2docfx/venv/0/Lib/site-packages/wheel/vendored/__init__.py +0 -0
  51. py2docfx/venv/0/Lib/site-packages/wheel/vendored/packaging/__init__.py +0 -0
  52. py2docfx/venv/0/Lib/site-packages/wheel/vendored/packaging/_elffile.py +108 -0
  53. py2docfx/venv/0/Lib/site-packages/wheel/vendored/packaging/_manylinux.py +260 -0
  54. py2docfx/venv/0/Lib/site-packages/wheel/vendored/packaging/_musllinux.py +83 -0
  55. py2docfx/venv/0/Lib/site-packages/wheel/vendored/packaging/_parser.py +356 -0
  56. py2docfx/venv/0/Lib/site-packages/wheel/vendored/packaging/_structures.py +61 -0
  57. py2docfx/venv/0/Lib/site-packages/wheel/vendored/packaging/_tokenizer.py +192 -0
  58. py2docfx/venv/0/Lib/site-packages/wheel/vendored/packaging/markers.py +253 -0
  59. py2docfx/venv/0/Lib/site-packages/wheel/vendored/packaging/requirements.py +90 -0
  60. py2docfx/venv/0/Lib/site-packages/wheel/vendored/packaging/specifiers.py +1011 -0
  61. py2docfx/venv/0/Lib/site-packages/wheel/vendored/packaging/tags.py +571 -0
  62. py2docfx/venv/0/Lib/site-packages/wheel/vendored/packaging/utils.py +172 -0
  63. py2docfx/venv/0/Lib/site-packages/wheel/vendored/packaging/version.py +561 -0
  64. py2docfx/venv/0/Lib/site-packages/wheel/wheelfile.py +227 -0
  65. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/_collections.py +0 -145
  66. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/bcppcompiler.py +1 -2
  67. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/ccompiler.py +7 -11
  68. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/command/bdist.py +4 -3
  69. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/command/build_ext.py +1 -4
  70. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/command/check.py +3 -4
  71. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/command/install_data.py +39 -29
  72. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/command/install_lib.py +1 -3
  73. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/command/sdist.py +4 -4
  74. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/compat/py38.py +1 -0
  75. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/cygwinccompiler.py +21 -42
  76. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/dist.py +3 -12
  77. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/extension.py +9 -4
  78. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/msvc9compiler.py +1 -3
  79. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/msvccompiler.py +1 -3
  80. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/spawn.py +0 -1
  81. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/sysconfig.py +44 -25
  82. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/tests/test_archive_util.py +1 -1
  83. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/tests/test_build.py +1 -2
  84. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/tests/test_build_ext.py +1 -1
  85. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/tests/test_cygwinccompiler.py +0 -42
  86. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/tests/test_dist.py +1 -1
  87. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/tests/test_extension.py +4 -1
  88. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/tests/test_install_data.py +17 -9
  89. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/tests/test_mingwccompiler.py +5 -4
  90. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/tests/test_spawn.py +1 -1
  91. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/tests/test_sysconfig.py +6 -3
  92. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/tests/test_unixccompiler.py +35 -1
  93. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/tests/test_util.py +4 -24
  94. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/unixccompiler.py +19 -20
  95. py2docfx/venv/template/Lib/site-packages/setuptools/_distutils/util.py +20 -26
  96. py2docfx/venv/template/Lib/site-packages/wheel/__init__.py +3 -0
  97. py2docfx/venv/template/Lib/site-packages/wheel/__main__.py +23 -0
  98. py2docfx/venv/template/Lib/site-packages/wheel/_bdist_wheel.py +604 -0
  99. py2docfx/venv/template/Lib/site-packages/wheel/_setuptools_logging.py +26 -0
  100. py2docfx/venv/template/Lib/site-packages/wheel/bdist_wheel.py +11 -0
  101. py2docfx/venv/template/Lib/site-packages/wheel/cli/__init__.py +155 -0
  102. py2docfx/venv/template/Lib/site-packages/wheel/cli/convert.py +273 -0
  103. py2docfx/venv/template/Lib/site-packages/wheel/cli/pack.py +85 -0
  104. py2docfx/venv/template/Lib/site-packages/wheel/cli/tags.py +139 -0
  105. py2docfx/venv/template/Lib/site-packages/wheel/cli/unpack.py +30 -0
  106. py2docfx/venv/template/Lib/site-packages/wheel/macosx_libfile.py +482 -0
  107. py2docfx/venv/template/Lib/site-packages/wheel/metadata.py +183 -0
  108. py2docfx/venv/template/Lib/site-packages/wheel/util.py +26 -0
  109. py2docfx/venv/template/Lib/site-packages/wheel/vendored/__init__.py +0 -0
  110. py2docfx/venv/template/Lib/site-packages/wheel/vendored/packaging/__init__.py +0 -0
  111. py2docfx/venv/template/Lib/site-packages/wheel/vendored/packaging/_elffile.py +108 -0
  112. py2docfx/venv/template/Lib/site-packages/wheel/vendored/packaging/_manylinux.py +260 -0
  113. py2docfx/venv/template/Lib/site-packages/wheel/vendored/packaging/_musllinux.py +83 -0
  114. py2docfx/venv/template/Lib/site-packages/wheel/vendored/packaging/_parser.py +356 -0
  115. py2docfx/venv/template/Lib/site-packages/wheel/vendored/packaging/_structures.py +61 -0
  116. py2docfx/venv/template/Lib/site-packages/wheel/vendored/packaging/_tokenizer.py +192 -0
  117. py2docfx/venv/template/Lib/site-packages/wheel/vendored/packaging/markers.py +253 -0
  118. py2docfx/venv/template/Lib/site-packages/wheel/vendored/packaging/requirements.py +90 -0
  119. py2docfx/venv/template/Lib/site-packages/wheel/vendored/packaging/specifiers.py +1011 -0
  120. py2docfx/venv/template/Lib/site-packages/wheel/vendored/packaging/tags.py +571 -0
  121. py2docfx/venv/template/Lib/site-packages/wheel/vendored/packaging/utils.py +172 -0
  122. py2docfx/venv/template/Lib/site-packages/wheel/vendored/packaging/version.py +561 -0
  123. py2docfx/venv/template/Lib/site-packages/wheel/wheelfile.py +227 -0
  124. {py2docfx-0.1.11.dev1824276.dist-info → py2docfx-0.1.11.dev1830301.dist-info}/METADATA +1 -1
  125. {py2docfx-0.1.11.dev1824276.dist-info → py2docfx-0.1.11.dev1830301.dist-info}/RECORD +127 -71
  126. {py2docfx-0.1.11.dev1824276.dist-info → py2docfx-0.1.11.dev1830301.dist-info}/WHEEL +0 -0
  127. {py2docfx-0.1.11.dev1824276.dist-info → py2docfx-0.1.11.dev1830301.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,227 @@
1
+ from __future__ import annotations
2
+
3
+ import csv
4
+ import hashlib
5
+ import os.path
6
+ import re
7
+ import stat
8
+ import time
9
+ from io import StringIO, TextIOWrapper
10
+ from typing import IO, TYPE_CHECKING, Literal
11
+ from zipfile import ZIP_DEFLATED, ZipFile, ZipInfo
12
+
13
+ from wheel.cli import WheelError
14
+ from wheel.util import log, urlsafe_b64decode, urlsafe_b64encode
15
+
16
+ if TYPE_CHECKING:
17
+ from typing import Protocol, Sized, Union
18
+
19
+ from typing_extensions import Buffer
20
+
21
+ StrPath = Union[str, os.PathLike[str]]
22
+
23
+ class SizedBuffer(Sized, Buffer, Protocol): ...
24
+
25
+
26
+ # Non-greedy matching of an optional build number may be too clever (more
27
+ # invalid wheel filenames will match). Separate regex for .dist-info?
28
+ WHEEL_INFO_RE = re.compile(
29
+ r"""^(?P<namever>(?P<name>[^\s-]+?)-(?P<ver>[^\s-]+?))(-(?P<build>\d[^\s-]*))?
30
+ -(?P<pyver>[^\s-]+?)-(?P<abi>[^\s-]+?)-(?P<plat>\S+)\.whl$""",
31
+ re.VERBOSE,
32
+ )
33
+ MINIMUM_TIMESTAMP = 315532800 # 1980-01-01 00:00:00 UTC
34
+
35
+
36
+ def get_zipinfo_datetime(timestamp: float | None = None):
37
+ # Some applications need reproducible .whl files, but they can't do this without
38
+ # forcing the timestamp of the individual ZipInfo objects. See issue #143.
39
+ timestamp = int(os.environ.get("SOURCE_DATE_EPOCH", timestamp or time.time()))
40
+ timestamp = max(timestamp, MINIMUM_TIMESTAMP)
41
+ return time.gmtime(timestamp)[0:6]
42
+
43
+
44
+ class WheelFile(ZipFile):
45
+ """A ZipFile derivative class that also reads SHA-256 hashes from
46
+ .dist-info/RECORD and checks any read files against those.
47
+ """
48
+
49
+ _default_algorithm = hashlib.sha256
50
+
51
+ def __init__(
52
+ self,
53
+ file: StrPath,
54
+ mode: Literal["r", "w", "x", "a"] = "r",
55
+ compression: int = ZIP_DEFLATED,
56
+ ):
57
+ basename = os.path.basename(file)
58
+ self.parsed_filename = WHEEL_INFO_RE.match(basename)
59
+ if not basename.endswith(".whl") or self.parsed_filename is None:
60
+ raise WheelError(f"Bad wheel filename {basename!r}")
61
+
62
+ ZipFile.__init__(self, file, mode, compression=compression, allowZip64=True)
63
+
64
+ self.dist_info_path = "{}.dist-info".format(
65
+ self.parsed_filename.group("namever")
66
+ )
67
+ self.record_path = self.dist_info_path + "/RECORD"
68
+ self._file_hashes: dict[str, tuple[None, None] | tuple[int, bytes]] = {}
69
+ self._file_sizes = {}
70
+ if mode == "r":
71
+ # Ignore RECORD and any embedded wheel signatures
72
+ self._file_hashes[self.record_path] = None, None
73
+ self._file_hashes[self.record_path + ".jws"] = None, None
74
+ self._file_hashes[self.record_path + ".p7s"] = None, None
75
+
76
+ # Fill in the expected hashes by reading them from RECORD
77
+ try:
78
+ record = self.open(self.record_path)
79
+ except KeyError:
80
+ raise WheelError(f"Missing {self.record_path} file") from None
81
+
82
+ with record:
83
+ for line in csv.reader(
84
+ TextIOWrapper(record, newline="", encoding="utf-8")
85
+ ):
86
+ path, hash_sum, size = line
87
+ if not hash_sum:
88
+ continue
89
+
90
+ algorithm, hash_sum = hash_sum.split("=")
91
+ try:
92
+ hashlib.new(algorithm)
93
+ except ValueError:
94
+ raise WheelError(
95
+ f"Unsupported hash algorithm: {algorithm}"
96
+ ) from None
97
+
98
+ if algorithm.lower() in {"md5", "sha1"}:
99
+ raise WheelError(
100
+ f"Weak hash algorithm ({algorithm}) is not permitted by "
101
+ f"PEP 427"
102
+ )
103
+
104
+ self._file_hashes[path] = (
105
+ algorithm,
106
+ urlsafe_b64decode(hash_sum.encode("ascii")),
107
+ )
108
+
109
+ def open(
110
+ self,
111
+ name_or_info: str | ZipInfo,
112
+ mode: Literal["r", "w"] = "r",
113
+ pwd: bytes | None = None,
114
+ ) -> IO[bytes]:
115
+ def _update_crc(newdata: bytes) -> None:
116
+ eof = ef._eof
117
+ update_crc_orig(newdata)
118
+ running_hash.update(newdata)
119
+ if eof and running_hash.digest() != expected_hash:
120
+ raise WheelError(f"Hash mismatch for file '{ef_name}'")
121
+
122
+ ef_name = (
123
+ name_or_info.filename if isinstance(name_or_info, ZipInfo) else name_or_info
124
+ )
125
+ if (
126
+ mode == "r"
127
+ and not ef_name.endswith("/")
128
+ and ef_name not in self._file_hashes
129
+ ):
130
+ raise WheelError(f"No hash found for file '{ef_name}'")
131
+
132
+ ef = ZipFile.open(self, name_or_info, mode, pwd)
133
+ if mode == "r" and not ef_name.endswith("/"):
134
+ algorithm, expected_hash = self._file_hashes[ef_name]
135
+ if expected_hash is not None:
136
+ # Monkey patch the _update_crc method to also check for the hash from
137
+ # RECORD
138
+ running_hash = hashlib.new(algorithm)
139
+ update_crc_orig, ef._update_crc = ef._update_crc, _update_crc
140
+
141
+ return ef
142
+
143
+ def write_files(self, base_dir: str):
144
+ log.info(f"creating '{self.filename}' and adding '{base_dir}' to it")
145
+ deferred: list[tuple[str, str]] = []
146
+ for root, dirnames, filenames in os.walk(base_dir):
147
+ # Sort the directory names so that `os.walk` will walk them in a
148
+ # defined order on the next iteration.
149
+ dirnames.sort()
150
+ for name in sorted(filenames):
151
+ path = os.path.normpath(os.path.join(root, name))
152
+ if os.path.isfile(path):
153
+ arcname = os.path.relpath(path, base_dir).replace(os.path.sep, "/")
154
+ if arcname == self.record_path:
155
+ pass
156
+ elif root.endswith(".dist-info"):
157
+ deferred.append((path, arcname))
158
+ else:
159
+ self.write(path, arcname)
160
+
161
+ deferred.sort()
162
+ for path, arcname in deferred:
163
+ self.write(path, arcname)
164
+
165
+ def write(
166
+ self,
167
+ filename: str,
168
+ arcname: str | None = None,
169
+ compress_type: int | None = None,
170
+ ) -> None:
171
+ with open(filename, "rb") as f:
172
+ st = os.fstat(f.fileno())
173
+ data = f.read()
174
+
175
+ zinfo = ZipInfo(
176
+ arcname or filename, date_time=get_zipinfo_datetime(st.st_mtime)
177
+ )
178
+ zinfo.external_attr = (stat.S_IMODE(st.st_mode) | stat.S_IFMT(st.st_mode)) << 16
179
+ zinfo.compress_type = compress_type or self.compression
180
+ self.writestr(zinfo, data, compress_type)
181
+
182
+ def writestr(
183
+ self,
184
+ zinfo_or_arcname: str | ZipInfo,
185
+ data: SizedBuffer | str,
186
+ compress_type: int | None = None,
187
+ ):
188
+ if isinstance(zinfo_or_arcname, str):
189
+ zinfo_or_arcname = ZipInfo(
190
+ zinfo_or_arcname, date_time=get_zipinfo_datetime()
191
+ )
192
+ zinfo_or_arcname.compress_type = self.compression
193
+ zinfo_or_arcname.external_attr = (0o664 | stat.S_IFREG) << 16
194
+
195
+ if isinstance(data, str):
196
+ data = data.encode("utf-8")
197
+
198
+ ZipFile.writestr(self, zinfo_or_arcname, data, compress_type)
199
+ fname = (
200
+ zinfo_or_arcname.filename
201
+ if isinstance(zinfo_or_arcname, ZipInfo)
202
+ else zinfo_or_arcname
203
+ )
204
+ log.info(f"adding '{fname}'")
205
+ if fname != self.record_path:
206
+ hash_ = self._default_algorithm(data)
207
+ self._file_hashes[fname] = (
208
+ hash_.name,
209
+ urlsafe_b64encode(hash_.digest()).decode("ascii"),
210
+ )
211
+ self._file_sizes[fname] = len(data)
212
+
213
+ def close(self):
214
+ # Write RECORD
215
+ if self.fp is not None and self.mode == "w" and self._file_hashes:
216
+ data = StringIO()
217
+ writer = csv.writer(data, delimiter=",", quotechar='"', lineterminator="\n")
218
+ writer.writerows(
219
+ (
220
+ (fname, algorithm + "=" + hash_, self._file_sizes[fname])
221
+ for fname, (algorithm, hash_) in self._file_hashes.items()
222
+ )
223
+ )
224
+ writer.writerow((format(self.record_path), "", ""))
225
+ self.writestr(self.record_path, data.getvalue())
226
+
227
+ ZipFile.close(self)
@@ -1,11 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import collections
4
- import functools
5
4
  import itertools
6
- import operator
7
- from collections.abc import Mapping
8
- from typing import Any
9
5
 
10
6
 
11
7
  # from jaraco.collections 3.5.1
@@ -60,144 +56,3 @@ class DictStack(list, collections.abc.Mapping):
60
56
 
61
57
  def __len__(self):
62
58
  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)
@@ -236,8 +236,7 @@ class BCPPCompiler(CCompiler):
236
236
  temp_dir = os.path.dirname(objects[0]) # preserve tree structure
237
237
  def_file = os.path.join(temp_dir, f'{modname}.def')
238
238
  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)
241
240
  self.execute(write_file, (def_file, contents), f"writing {def_file}")
242
241
 
243
242
  # Borland C++ has problems with '/' in paths
@@ -22,7 +22,7 @@ from .errors import (
22
22
  )
23
23
  from .file_util import move_file
24
24
  from .spawn import spawn
25
- from .util import execute, split_quoted, is_mingw
25
+ from .util import execute, is_mingw, split_quoted
26
26
 
27
27
 
28
28
  class CCompiler:
@@ -1124,10 +1124,10 @@ def show_compilers():
1124
1124
  # commands that use it.
1125
1125
  from distutils.fancy_getopt import FancyGetopt
1126
1126
 
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
+ )
1131
1131
  pretty_printer = FancyGetopt(compilers)
1132
1132
  pretty_printer.print_help("List of available compilers:")
1133
1133
 
@@ -1218,8 +1218,7 @@ def gen_preprocess_options(macros, include_dirs):
1218
1218
  # shell at all costs when we spawn the command!
1219
1219
  pp_opts.append("-D{}={}".format(*macro))
1220
1220
 
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)
1223
1222
  return pp_opts
1224
1223
 
1225
1224
 
@@ -1230,10 +1229,7 @@ def gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries):
1230
1229
  directories. Returns a list of command-line options suitable for use
1231
1230
  with some compiler (depending on the two format strings passed in).
1232
1231
  """
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]
1237
1233
 
1238
1234
  for dir in runtime_library_dirs:
1239
1235
  lib_opts.extend(always_iterable(compiler.runtime_library_dir_option(dir)))
@@ -15,9 +15,10 @@ def show_formats():
15
15
  """Print list of available formats (arguments to "--format" option)."""
16
16
  from ..fancy_getopt import FancyGetopt
17
17
 
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
+ ]
21
22
  pretty_printer = FancyGetopt(formats)
22
23
  pretty_printer.print_help("List of available distribution formats:")
23
24
 
@@ -465,10 +465,7 @@ class build_ext(Command):
465
465
  # And build the list of output (built) filenames. Note that this
466
466
  # ignores the 'inplace' flag, and assumes everything goes in the
467
467
  # "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]
472
469
 
473
470
  def build_extensions(self):
474
471
  # First, sanity-check the 'extensions' list
@@ -100,10 +100,9 @@ class check(Command):
100
100
  """
101
101
  metadata = self.distribution.metadata
102
102
 
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
+ ]
107
106
 
108
107
  if missing:
109
108
  self.warn("missing required meta-data: {}".format(', '.join(missing)))
@@ -5,7 +5,11 @@ platform-independent data files."""
5
5
 
6
6
  # contributed by Bastian Kleineidam
7
7
 
8
+ from __future__ import annotations
9
+
10
+ import functools
8
11
  import os
12
+ from typing import Iterable
9
13
 
10
14
  from ..core import Command
11
15
  from ..util import change_root, convert_path
@@ -46,36 +50,42 @@ class install_data(Command):
46
50
  def run(self):
47
51
  self.mkpath(self.install_dir)
48
52
  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)
58
75
  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)
79
89
 
80
90
  def get_inputs(self):
81
91
  return self.data_files or []
@@ -161,9 +161,7 @@ class install_lib(Command):
161
161
  build_dir = getattr(build_cmd, cmd_option)
162
162
 
163
163
  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]
167
165
 
168
166
  return outputs
169
167
 
@@ -24,10 +24,10 @@ def show_formats():
24
24
  from ..archive_util import ARCHIVE_FORMATS
25
25
  from ..fancy_getopt import FancyGetopt
26
26
 
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
+ )
31
31
  FancyGetopt(formats).print_help("List of available source distribution formats:")
32
32
 
33
33
 
@@ -14,6 +14,7 @@ if sys.version_info < (3, 9):
14
14
  return self[len(prefix) :]
15
15
  else:
16
16
  return self[:]
17
+
17
18
  else:
18
19
 
19
20
  def removesuffix(self, suffix):