dependence 1.0.2__py3-none-any.whl → 1.2.5__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.
- dependence/__main__.py +13 -6
- dependence/_utilities.py +352 -163
- dependence/freeze.py +111 -53
- dependence/update.py +75 -57
- dependence/upgrade.py +223 -0
- {dependence-1.0.2.dist-info → dependence-1.2.5.dist-info}/METADATA +84 -18
- dependence-1.2.5.dist-info/RECORD +11 -0
- {dependence-1.0.2.dist-info → dependence-1.2.5.dist-info}/WHEEL +1 -1
- dependence-1.0.2.dist-info/RECORD +0 -10
- {dependence-1.0.2.dist-info → dependence-1.2.5.dist-info}/entry_points.txt +0 -0
dependence/freeze.py
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import argparse
|
|
4
|
+
import os
|
|
2
5
|
from fnmatch import fnmatch
|
|
3
6
|
from functools import partial
|
|
4
|
-
from importlib.metadata import Distribution
|
|
5
|
-
from importlib.metadata import distribution as _get_distribution
|
|
6
7
|
from itertools import chain
|
|
7
|
-
from
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import TYPE_CHECKING, cast
|
|
8
10
|
|
|
9
|
-
from ._utilities import (
|
|
11
|
+
from dependence._utilities import (
|
|
10
12
|
get_distribution,
|
|
11
13
|
get_required_distribution_names,
|
|
12
14
|
get_requirement_string_distribution_name,
|
|
13
|
-
install_requirement,
|
|
14
|
-
is_configuration_file,
|
|
15
15
|
iter_configuration_file_requirement_strings,
|
|
16
|
+
iter_configuration_files,
|
|
16
17
|
iter_distinct,
|
|
17
18
|
iter_parse_delimited_values,
|
|
18
19
|
normalize_name,
|
|
19
20
|
)
|
|
20
21
|
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from collections.abc import Iterable, MutableSet
|
|
24
|
+
from importlib.metadata import Distribution
|
|
25
|
+
|
|
21
26
|
_DO_NOT_PIN_DISTRIBUTION_NAMES: MutableSet[str] = {
|
|
22
27
|
"importlib-metadata",
|
|
23
28
|
"importlib-resources",
|
|
@@ -30,18 +35,18 @@ def _iter_sort_dependents_last(requirements: Iterable[str]) -> Iterable[str]:
|
|
|
30
35
|
"""
|
|
31
36
|
requirements = list(requirements)
|
|
32
37
|
distribution_name: str
|
|
33
|
-
distribution_requirement:
|
|
38
|
+
distribution_requirement: dict[str, str] = {
|
|
34
39
|
get_requirement_string_distribution_name(requirement): requirement
|
|
35
40
|
for requirement in requirements
|
|
36
41
|
}
|
|
37
|
-
dependent_dependencies:
|
|
42
|
+
dependent_dependencies: dict[str, MutableSet[str]] = {
|
|
38
43
|
distribution_name: get_required_distribution_names(requirement)
|
|
39
44
|
for distribution_name, requirement in distribution_requirement.items()
|
|
40
45
|
}
|
|
41
46
|
while dependent_dependencies:
|
|
42
47
|
dependent: str
|
|
43
48
|
dependencies: MutableSet[str]
|
|
44
|
-
item:
|
|
49
|
+
item: tuple[str, MutableSet[str]]
|
|
45
50
|
for dependent, dependencies in sorted( # noqa: C414
|
|
46
51
|
tuple(dependent_dependencies.items()),
|
|
47
52
|
key=lambda item: item[0].lower(),
|
|
@@ -71,17 +76,19 @@ def _iter_sort_dependents_last(requirements: Iterable[str]) -> Iterable[str]:
|
|
|
71
76
|
del dependent_dependencies[dependent]
|
|
72
77
|
|
|
73
78
|
|
|
74
|
-
def get_frozen_requirements(
|
|
75
|
-
requirements: Iterable[str] = (),
|
|
79
|
+
def get_frozen_requirements( # noqa: C901
|
|
80
|
+
requirements: Iterable[str | Path] = (),
|
|
81
|
+
*,
|
|
76
82
|
exclude: Iterable[str] = (),
|
|
77
83
|
exclude_recursive: Iterable[str] = (),
|
|
84
|
+
keep_version_specifier: Iterable[str] = (),
|
|
78
85
|
no_version: Iterable[str] = (),
|
|
79
86
|
dependency_order: bool = False,
|
|
80
87
|
reverse: bool = False,
|
|
81
|
-
depth:
|
|
82
|
-
include_pointers:
|
|
83
|
-
exclude_pointers:
|
|
84
|
-
) ->
|
|
88
|
+
depth: int | None = None,
|
|
89
|
+
include_pointers: tuple[str, ...] = (),
|
|
90
|
+
exclude_pointers: tuple[str, ...] = (),
|
|
91
|
+
) -> tuple[str, ...]:
|
|
85
92
|
"""
|
|
86
93
|
Get the (frozen) requirements for one or more specified distributions or
|
|
87
94
|
configuration files.
|
|
@@ -95,8 +102,14 @@ def get_frozen_requirements(
|
|
|
95
102
|
exclude_recursive: One or more distributions to exclude/ignore.
|
|
96
103
|
Note: Excluding a distribution here excludes all requirements which
|
|
97
104
|
would be identified through recursion.
|
|
105
|
+
keep_version_specifier: Keep the original (non-frozen) version
|
|
106
|
+
specifier for package names matching any of these patterns. This
|
|
107
|
+
supercedes `no_version`, if both sets of patterns match a package
|
|
108
|
+
name.
|
|
98
109
|
no_version: Exclude version numbers from the output
|
|
99
|
-
(only return distribution names)
|
|
110
|
+
(only return distribution names). This is superceded by
|
|
111
|
+
`keep_version_specifier`, if both sets of patterns match a package
|
|
112
|
+
name.
|
|
100
113
|
dependency_order: Sort requirements so that dependents
|
|
101
114
|
precede dependencies
|
|
102
115
|
depth: Depth of recursive requirement discovery
|
|
@@ -105,34 +118,65 @@ def get_frozen_requirements(
|
|
|
105
118
|
exclude_pointers: A tuple of JSON pointers indicating elements to
|
|
106
119
|
exclude (defaults to no exclusions). Only applies to TOML files.
|
|
107
120
|
"""
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
requirements = {requirements}
|
|
121
|
+
if isinstance(requirements, (str, Path)):
|
|
122
|
+
requirements = {str(requirements)}
|
|
111
123
|
else:
|
|
112
|
-
requirements = set(requirements)
|
|
124
|
+
requirements = set(map(str, requirements))
|
|
113
125
|
if isinstance(no_version, str):
|
|
114
126
|
no_version = (no_version,)
|
|
115
127
|
elif not isinstance(no_version, tuple):
|
|
116
128
|
no_version = tuple(no_version)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
129
|
+
if isinstance(keep_version_specifier, str):
|
|
130
|
+
keep_version_specifier = (keep_version_specifier,)
|
|
131
|
+
elif not isinstance(keep_version_specifier, tuple):
|
|
132
|
+
keep_version_specifier = tuple(keep_version_specifier)
|
|
133
|
+
# Separate requirement strings from requirement files
|
|
134
|
+
configuration_files: dict[str, dict[str, tuple[str, ...]]] = {}
|
|
135
|
+
requirement_strings: MutableSet[str] = set()
|
|
136
|
+
requirement: str | Path
|
|
137
|
+
for requirement in requirements:
|
|
138
|
+
if TYPE_CHECKING:
|
|
139
|
+
assert isinstance(requirement, str)
|
|
140
|
+
requirement_configuration_files: set[str] = set(
|
|
141
|
+
iter_configuration_files(requirement)
|
|
142
|
+
)
|
|
143
|
+
if requirement_configuration_files:
|
|
144
|
+
is_directory: bool = os.path.isdir(requirement)
|
|
145
|
+
for (
|
|
146
|
+
requirement_configuration_file
|
|
147
|
+
) in requirement_configuration_files:
|
|
148
|
+
configuration_files[requirement_configuration_file] = (
|
|
149
|
+
{"include_pointers": ("/project",)}
|
|
150
|
+
if (
|
|
151
|
+
is_directory
|
|
152
|
+
and os.path.basename(
|
|
153
|
+
requirement_configuration_file
|
|
154
|
+
).lower()
|
|
155
|
+
== "pyproject.toml"
|
|
156
|
+
)
|
|
157
|
+
else {
|
|
158
|
+
"include_pointers": include_pointers,
|
|
159
|
+
"exclude_pointers": exclude_pointers,
|
|
160
|
+
}
|
|
161
|
+
)
|
|
162
|
+
else:
|
|
163
|
+
if requirement.startswith("setup.py"):
|
|
164
|
+
raise ValueError(requirement)
|
|
165
|
+
requirement_strings.add(requirement)
|
|
166
|
+
configuration_file: str
|
|
167
|
+
kwargs: dict[str, tuple[str, ...]]
|
|
123
168
|
frozen_requirements: Iterable[str] = iter_distinct(
|
|
124
169
|
chain(
|
|
125
170
|
requirement_strings,
|
|
126
|
-
*
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
),
|
|
132
|
-
requirement_files,
|
|
171
|
+
*(
|
|
172
|
+
iter_configuration_file_requirement_strings(
|
|
173
|
+
configuration_file, **kwargs
|
|
174
|
+
)
|
|
175
|
+
for configuration_file, kwargs in configuration_files.items()
|
|
133
176
|
),
|
|
134
177
|
)
|
|
135
178
|
)
|
|
179
|
+
frozen_requirements = tuple(frozen_requirements)
|
|
136
180
|
if depth is not None:
|
|
137
181
|
depth -= 1
|
|
138
182
|
if (depth is None) or depth >= 0:
|
|
@@ -158,6 +202,7 @@ def get_frozen_requirements(
|
|
|
158
202
|
),
|
|
159
203
|
exclude_recursive=set(map(normalize_name, exclude_recursive)),
|
|
160
204
|
no_version=no_version,
|
|
205
|
+
keep_version_specifier=keep_version_specifier,
|
|
161
206
|
depth=depth,
|
|
162
207
|
)
|
|
163
208
|
if dependency_order:
|
|
@@ -183,34 +228,47 @@ def _iter_frozen_requirements(
|
|
|
183
228
|
exclude: MutableSet[str],
|
|
184
229
|
exclude_recursive: MutableSet[str],
|
|
185
230
|
no_version: Iterable[str] = (),
|
|
186
|
-
depth:
|
|
231
|
+
depth: int | None = None,
|
|
232
|
+
keep_version_specifier: Iterable[str] = (),
|
|
187
233
|
) -> Iterable[str]:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
234
|
+
# This retains a mapping of distribution names to their original
|
|
235
|
+
# requirement strings in order to return those which match
|
|
236
|
+
# `keep_version_specifier` patterns with their original specifiers
|
|
237
|
+
distribution_names_specifiers: dict[str, str] = {}
|
|
191
238
|
|
|
239
|
+
def get_requirement_string(distribution_name: str) -> str | None:
|
|
240
|
+
if distribution_names_specifiers and (
|
|
241
|
+
distribution_name in distribution_names_specifiers
|
|
242
|
+
):
|
|
243
|
+
return distribution_names_specifiers[distribution_name]
|
|
192
244
|
if (distribution_name in _DO_NOT_PIN_DISTRIBUTION_NAMES) or any(
|
|
193
|
-
|
|
245
|
+
fnmatch(distribution_name, pattern) for pattern in no_version
|
|
194
246
|
):
|
|
195
247
|
return distribution_name
|
|
196
248
|
distribution: Distribution
|
|
197
249
|
try:
|
|
198
250
|
distribution = get_distribution(distribution_name)
|
|
199
251
|
except KeyError:
|
|
252
|
+
# If the distribution is not installed, skip it
|
|
253
|
+
return None
|
|
200
254
|
# If the distribution is missing, install it
|
|
201
|
-
install_requirement(distribution_name
|
|
202
|
-
distribution = _get_distribution(distribution_name)
|
|
255
|
+
# install_requirement(distribution_name)
|
|
256
|
+
# distribution = _get_distribution(distribution_name)
|
|
203
257
|
return f"{distribution.metadata['Name']}=={distribution.version}"
|
|
204
258
|
|
|
205
259
|
def get_required_distribution_names_(
|
|
206
260
|
requirement_string: str,
|
|
207
|
-
depth_:
|
|
261
|
+
depth_: int | None = None,
|
|
208
262
|
) -> MutableSet[str]:
|
|
209
263
|
name: str = get_requirement_string_distribution_name(
|
|
210
264
|
requirement_string
|
|
211
265
|
)
|
|
212
266
|
if name in exclude_recursive:
|
|
213
267
|
return set()
|
|
268
|
+
if keep_version_specifier and any(
|
|
269
|
+
fnmatch(name, pattern) for pattern in keep_version_specifier
|
|
270
|
+
):
|
|
271
|
+
distribution_names_specifiers[name] = requirement_string.rstrip()
|
|
214
272
|
distribution_names: MutableSet[str] = {name}
|
|
215
273
|
if (depth_ is None) or depth_:
|
|
216
274
|
distribution_names |= get_required_distribution_names(
|
|
@@ -219,7 +277,7 @@ def _iter_frozen_requirements(
|
|
|
219
277
|
depth=None if (depth_ is None) else depth_ - 1,
|
|
220
278
|
)
|
|
221
279
|
return cast(
|
|
222
|
-
MutableSet[str],
|
|
280
|
+
"MutableSet[str]",
|
|
223
281
|
distribution_names - exclude,
|
|
224
282
|
)
|
|
225
283
|
|
|
@@ -234,20 +292,20 @@ def _iter_frozen_requirements(
|
|
|
234
292
|
)
|
|
235
293
|
),
|
|
236
294
|
)
|
|
237
|
-
|
|
238
|
-
return requirements
|
|
295
|
+
return filter(None, map(get_requirement_string, requirements))
|
|
239
296
|
|
|
240
297
|
|
|
241
298
|
def freeze(
|
|
242
|
-
requirements: Iterable[str] = (),
|
|
299
|
+
requirements: Iterable[str | Path] = (),
|
|
300
|
+
*,
|
|
243
301
|
exclude: Iterable[str] = (),
|
|
244
302
|
exclude_recursive: Iterable[str] = (),
|
|
245
303
|
no_version: Iterable[str] = (),
|
|
246
304
|
dependency_order: bool = False,
|
|
247
305
|
reverse: bool = False,
|
|
248
|
-
depth:
|
|
249
|
-
include_pointers:
|
|
250
|
-
exclude_pointers:
|
|
306
|
+
depth: int | None = None,
|
|
307
|
+
include_pointers: tuple[str, ...] = (),
|
|
308
|
+
exclude_pointers: tuple[str, ...] = (),
|
|
251
309
|
) -> None:
|
|
252
310
|
"""
|
|
253
311
|
Print the (frozen) requirements for one or more specified requirements or
|
|
@@ -258,10 +316,10 @@ def freeze(
|
|
|
258
316
|
"requirement-name[extra-a,extra-b]" or ".[extra-a, extra-b]) and/or
|
|
259
317
|
paths to a setup.py, setup.cfg, pyproject.toml, tox.ini or
|
|
260
318
|
requirements.txt file
|
|
261
|
-
exclude: One or more distributions to exclude
|
|
262
|
-
exclude_recursive: One or more distributions to exclude
|
|
263
|
-
|
|
264
|
-
|
|
319
|
+
exclude: One or more distributions to exclude.
|
|
320
|
+
exclude_recursive: One or more distributions to exclude. Recursive
|
|
321
|
+
dependency discovery is also halted for these distributions,
|
|
322
|
+
unlike those passed to `exclude`.
|
|
265
323
|
no_version: Exclude version numbers from the output
|
|
266
324
|
(only print distribution names) for package names matching any of
|
|
267
325
|
these patterns
|
|
@@ -273,7 +331,7 @@ def freeze(
|
|
|
273
331
|
exclude_pointers: If not empty, these TOML tables will *not* be
|
|
274
332
|
inspected (for pyproject.toml files)
|
|
275
333
|
"""
|
|
276
|
-
print(
|
|
334
|
+
print( # noqa: T201
|
|
277
335
|
"\n".join(
|
|
278
336
|
get_frozen_requirements(
|
|
279
337
|
requirements=requirements,
|
dependence/update.py
CHANGED
|
@@ -1,23 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import argparse
|
|
2
4
|
import re
|
|
3
5
|
from collections import deque
|
|
4
6
|
from configparser import ConfigParser, SectionProxy
|
|
5
7
|
from copy import deepcopy
|
|
6
8
|
from dataclasses import dataclass
|
|
7
|
-
from importlib.metadata import Distribution
|
|
8
9
|
from io import StringIO
|
|
9
10
|
from itertools import chain
|
|
11
|
+
from pathlib import Path
|
|
10
12
|
from typing import (
|
|
11
13
|
IO,
|
|
14
|
+
TYPE_CHECKING,
|
|
12
15
|
Any,
|
|
13
|
-
Callable,
|
|
14
|
-
Dict,
|
|
15
|
-
Iterable,
|
|
16
|
-
List,
|
|
17
|
-
Optional,
|
|
18
|
-
Set,
|
|
19
|
-
Tuple,
|
|
20
|
-
Union,
|
|
21
16
|
)
|
|
22
17
|
|
|
23
18
|
import tomli
|
|
@@ -27,7 +22,7 @@ from packaging.specifiers import Specifier, SpecifierSet
|
|
|
27
22
|
from packaging.version import Version
|
|
28
23
|
from packaging.version import parse as parse_version
|
|
29
24
|
|
|
30
|
-
from ._utilities import (
|
|
25
|
+
from dependence._utilities import (
|
|
31
26
|
ConfigurationFileType,
|
|
32
27
|
get_configuration_file_type,
|
|
33
28
|
get_installed_distributions,
|
|
@@ -38,6 +33,10 @@ from ._utilities import (
|
|
|
38
33
|
normalize_name,
|
|
39
34
|
)
|
|
40
35
|
|
|
36
|
+
if TYPE_CHECKING:
|
|
37
|
+
from collections.abc import Callable, Iterable
|
|
38
|
+
from importlib.metadata import Distribution
|
|
39
|
+
|
|
41
40
|
|
|
42
41
|
@dataclass
|
|
43
42
|
class _Version:
|
|
@@ -48,7 +47,7 @@ class _Version:
|
|
|
48
47
|
"""
|
|
49
48
|
|
|
50
49
|
epoch: int
|
|
51
|
-
release:
|
|
50
|
+
release: tuple[int, ...]
|
|
52
51
|
pre: Any
|
|
53
52
|
post: Any
|
|
54
53
|
dev: Any
|
|
@@ -64,13 +63,14 @@ def _update_requirement_specifiers(
|
|
|
64
63
|
"""
|
|
65
64
|
installed_version: Version = parse_version(installed_version_string)
|
|
66
65
|
specifier: Specifier
|
|
67
|
-
updated_specifier_strings:
|
|
66
|
+
updated_specifier_strings: list[str] = []
|
|
68
67
|
for specifier in requirement.specifier: # type: ignore
|
|
69
68
|
# Only update requirement to match our installed version
|
|
70
69
|
# if the requirement is *inclusive*
|
|
71
70
|
if ("=" in specifier.operator) and ("!" not in specifier.operator):
|
|
72
71
|
specifier_version: Version = parse_version(specifier.version)
|
|
73
|
-
|
|
72
|
+
if installed_version.release is None:
|
|
73
|
+
raise ValueError(installed_version)
|
|
74
74
|
if specifier_version.release is None:
|
|
75
75
|
updated_specifier_strings.append(f"{specifier.operator}")
|
|
76
76
|
else:
|
|
@@ -117,7 +117,7 @@ def _update_requirement_specifiers(
|
|
|
117
117
|
|
|
118
118
|
|
|
119
119
|
def _get_updated_requirement_string(
|
|
120
|
-
requirement_string: str, ignore:
|
|
120
|
+
requirement_string: str, ignore: set[str]
|
|
121
121
|
) -> str:
|
|
122
122
|
"""
|
|
123
123
|
This function updates version numbers in a requirement string to match
|
|
@@ -132,15 +132,23 @@ def _get_updated_requirement_string(
|
|
|
132
132
|
return requirement_string
|
|
133
133
|
try:
|
|
134
134
|
distribution: Distribution = get_installed_distributions()[name]
|
|
135
|
+
if distribution.version is None:
|
|
136
|
+
return requirement_string
|
|
135
137
|
_update_requirement_specifiers(requirement, distribution.version)
|
|
136
138
|
except KeyError:
|
|
137
139
|
# If the requirement isn't installed, we can't update the version
|
|
138
140
|
pass
|
|
141
|
+
except TypeError as error:
|
|
142
|
+
message: str = (
|
|
143
|
+
f"Unable to determine installed version for {requirement_string}: "
|
|
144
|
+
f"{distribution!r}"
|
|
145
|
+
)
|
|
146
|
+
raise ValueError(message) from error
|
|
139
147
|
return str(requirement)
|
|
140
148
|
|
|
141
149
|
|
|
142
|
-
def _normalize_ignore_argument(ignore: Iterable[str]) ->
|
|
143
|
-
ignore_set:
|
|
150
|
+
def _normalize_ignore_argument(ignore: Iterable[str]) -> set[str]:
|
|
151
|
+
ignore_set: set[str]
|
|
144
152
|
# Normalize/harmonize excluded project names
|
|
145
153
|
if isinstance(ignore, str):
|
|
146
154
|
ignore = (ignore,)
|
|
@@ -161,7 +169,7 @@ def _get_updated_requirements_txt(
|
|
|
161
169
|
- data (str): The contents of a *requirements.txt* file
|
|
162
170
|
- ignore ([str]): One or more project names to leave as-is
|
|
163
171
|
"""
|
|
164
|
-
ignore_set:
|
|
172
|
+
ignore_set: set[str] = _normalize_ignore_argument(ignore)
|
|
165
173
|
|
|
166
174
|
def get_updated_requirement_string(requirement: str) -> str:
|
|
167
175
|
return _get_updated_requirement_string(requirement, ignore=ignore_set)
|
|
@@ -184,7 +192,7 @@ def _get_updated_setup_cfg(
|
|
|
184
192
|
- all_extra_name (str): An (optional) extra name which will
|
|
185
193
|
consolidate requirements from all other extras
|
|
186
194
|
"""
|
|
187
|
-
ignore_set:
|
|
195
|
+
ignore_set: set[str] = _normalize_ignore_argument(ignore)
|
|
188
196
|
|
|
189
197
|
def get_updated_requirement_string(requirement: str) -> str:
|
|
190
198
|
return _get_updated_requirement_string(requirement, ignore=ignore_set)
|
|
@@ -202,10 +210,10 @@ def _get_updated_setup_cfg(
|
|
|
202
210
|
)
|
|
203
211
|
if "options.extras_require" in parser:
|
|
204
212
|
extras_require: SectionProxy = parser["options.extras_require"]
|
|
205
|
-
all_extra_requirements:
|
|
213
|
+
all_extra_requirements: list[str] = []
|
|
206
214
|
extra_name: str
|
|
207
215
|
extra_requirements_string: str
|
|
208
|
-
extra_requirements:
|
|
216
|
+
extra_requirements: list[str]
|
|
209
217
|
for extra_name, extra_requirements_string in extras_require.items():
|
|
210
218
|
if extra_name != all_extra_name:
|
|
211
219
|
extra_requirements = list(
|
|
@@ -223,7 +231,7 @@ def _get_updated_setup_cfg(
|
|
|
223
231
|
# We pre-pend an empty requirement string in order to]
|
|
224
232
|
# force new-line creation at the beginning of the extra
|
|
225
233
|
extras_require[all_extra_name] = "\n".join(
|
|
226
|
-
iter_distinct([""
|
|
234
|
+
iter_distinct(["", *all_extra_requirements])
|
|
227
235
|
)
|
|
228
236
|
# Return as a string
|
|
229
237
|
setup_cfg: str
|
|
@@ -246,10 +254,10 @@ def _get_updated_tox_ini(data: str, ignore: Iterable[str] = ()) -> str:
|
|
|
246
254
|
- data (str): The contents of a **tox.ini** file
|
|
247
255
|
- ignore ([str]): One or more project names to leave as-is
|
|
248
256
|
"""
|
|
249
|
-
ignore_set:
|
|
257
|
+
ignore_set: set[str] = _normalize_ignore_argument(ignore)
|
|
250
258
|
|
|
251
259
|
def get_updated_requirement_string(requirement: str) -> str:
|
|
252
|
-
prefix:
|
|
260
|
+
prefix: str | None = None
|
|
253
261
|
if ":" in requirement:
|
|
254
262
|
prefix, requirement = requirement.split(":", maxsplit=1)
|
|
255
263
|
requirement = _get_updated_requirement_string(
|
|
@@ -294,18 +302,18 @@ def _get_updated_tox_ini(data: str, ignore: Iterable[str] = ()) -> str:
|
|
|
294
302
|
|
|
295
303
|
|
|
296
304
|
def _update_document_requirements(
|
|
297
|
-
document:
|
|
305
|
+
document: dict[str, Any],
|
|
298
306
|
ignore: Iterable[str] = (),
|
|
299
|
-
include_pointers:
|
|
300
|
-
exclude_pointers:
|
|
307
|
+
include_pointers: tuple[str, ...] = (),
|
|
308
|
+
exclude_pointers: tuple[str, ...] = (),
|
|
301
309
|
) -> None:
|
|
302
|
-
ignore_set:
|
|
310
|
+
ignore_set: set[str] = _normalize_ignore_argument(ignore)
|
|
303
311
|
|
|
304
312
|
def get_updated_requirement_string(requirement: str) -> str:
|
|
305
313
|
return _get_updated_requirement_string(requirement, ignore=ignore_set)
|
|
306
314
|
|
|
307
315
|
# Find and update requirements
|
|
308
|
-
requirements_list:
|
|
316
|
+
requirements_list: list[str]
|
|
309
317
|
for requirements_list in iter_find_requirements_lists(
|
|
310
318
|
document,
|
|
311
319
|
include_pointers=include_pointers,
|
|
@@ -323,8 +331,8 @@ def _get_updated_pyproject_toml(
|
|
|
323
331
|
data: str,
|
|
324
332
|
ignore: Iterable[str] = (),
|
|
325
333
|
all_extra_name: str = "",
|
|
326
|
-
include_pointers:
|
|
327
|
-
exclude_pointers:
|
|
334
|
+
include_pointers: tuple[str, ...] = (),
|
|
335
|
+
exclude_pointers: tuple[str, ...] = (),
|
|
328
336
|
) -> str:
|
|
329
337
|
"""
|
|
330
338
|
Return the contents of a *pyproject.toml* file, updated to reflect the
|
|
@@ -345,8 +353,8 @@ def _get_updated_pyproject_toml(
|
|
|
345
353
|
The contents of the updated pyproject.toml file.
|
|
346
354
|
"""
|
|
347
355
|
# Parse pyproject.toml
|
|
348
|
-
original_pyproject:
|
|
349
|
-
updated_pyproject:
|
|
356
|
+
original_pyproject: dict[str, Any] = tomli.loads(data)
|
|
357
|
+
updated_pyproject: dict[str, Any] = deepcopy(original_pyproject)
|
|
350
358
|
# Find and update requirements
|
|
351
359
|
_update_document_requirements(
|
|
352
360
|
updated_pyproject,
|
|
@@ -355,13 +363,13 @@ def _get_updated_pyproject_toml(
|
|
|
355
363
|
exclude_pointers=exclude_pointers,
|
|
356
364
|
)
|
|
357
365
|
# Update consolidated optional requirements
|
|
358
|
-
project_optional_dependencies:
|
|
366
|
+
project_optional_dependencies: dict[str, list[str]] = (
|
|
359
367
|
updated_pyproject.get("project", {}).get("optional-dependencies", {})
|
|
360
368
|
)
|
|
361
369
|
# Update an extra indicated to encompass all other extras
|
|
362
370
|
if project_optional_dependencies and all_extra_name:
|
|
363
371
|
key: str
|
|
364
|
-
dependencies:
|
|
372
|
+
dependencies: list[str]
|
|
365
373
|
project_optional_dependencies[all_extra_name] = list(
|
|
366
374
|
iter_distinct(
|
|
367
375
|
chain(
|
|
@@ -384,8 +392,8 @@ def _get_updated_pyproject_toml(
|
|
|
384
392
|
def _get_updated_toml(
|
|
385
393
|
data: str,
|
|
386
394
|
ignore: Iterable[str] = (),
|
|
387
|
-
include_pointers:
|
|
388
|
-
exclude_pointers:
|
|
395
|
+
include_pointers: tuple[str, ...] = (),
|
|
396
|
+
exclude_pointers: tuple[str, ...] = (),
|
|
389
397
|
) -> str:
|
|
390
398
|
"""
|
|
391
399
|
Return the contents of a TOML file, updated to reflect the
|
|
@@ -407,8 +415,8 @@ def _get_updated_toml(
|
|
|
407
415
|
The contents of the updated TOML file.
|
|
408
416
|
"""
|
|
409
417
|
# Parse pyproject.toml
|
|
410
|
-
original_pyproject:
|
|
411
|
-
updated_pyproject:
|
|
418
|
+
original_pyproject: dict[str, Any] = tomli.loads(data)
|
|
419
|
+
updated_pyproject: dict[str, Any] = deepcopy(original_pyproject)
|
|
412
420
|
# Find and update requirements
|
|
413
421
|
_update_document_requirements(
|
|
414
422
|
updated_pyproject,
|
|
@@ -423,15 +431,16 @@ def _get_updated_toml(
|
|
|
423
431
|
|
|
424
432
|
|
|
425
433
|
def _update(
|
|
426
|
-
path: str,
|
|
434
|
+
path: str | Path,
|
|
427
435
|
ignore: Iterable[str] = (),
|
|
428
436
|
all_extra_name: str = "",
|
|
429
|
-
include_pointers:
|
|
430
|
-
exclude_pointers:
|
|
437
|
+
include_pointers: tuple[str, ...] = (),
|
|
438
|
+
exclude_pointers: tuple[str, ...] = (),
|
|
431
439
|
) -> None:
|
|
440
|
+
message: str
|
|
432
441
|
data: str
|
|
433
442
|
update_function: Callable[[str], str]
|
|
434
|
-
kwargs:
|
|
443
|
+
kwargs: dict[str, str | Iterable[str]] = {}
|
|
435
444
|
configuration_file_type: ConfigurationFileType = (
|
|
436
445
|
get_configuration_file_type(path)
|
|
437
446
|
)
|
|
@@ -457,49 +466,58 @@ def _update(
|
|
|
457
466
|
elif configuration_file_type == ConfigurationFileType.REQUIREMENTS_TXT:
|
|
458
467
|
update_function = _get_updated_requirements_txt
|
|
459
468
|
else:
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
)
|
|
469
|
+
message = f"Updating requirements for {path!s} is not supported"
|
|
470
|
+
raise NotImplementedError(message)
|
|
463
471
|
kwargs["ignore"] = ignore
|
|
464
472
|
file_io: IO[str]
|
|
465
473
|
with open(path) as file_io:
|
|
466
474
|
data = file_io.read()
|
|
467
475
|
updated_data: str = update_function(data, **kwargs)
|
|
468
476
|
if updated_data == data:
|
|
469
|
-
print(
|
|
477
|
+
print( # noqa: T201
|
|
478
|
+
f"All requirements were already up-to-date in {path!s}"
|
|
479
|
+
)
|
|
470
480
|
else:
|
|
471
|
-
print(
|
|
481
|
+
print( # noqa: T201
|
|
482
|
+
f"Updating requirements in {path!s}"
|
|
483
|
+
)
|
|
472
484
|
with open(path, "w") as file_io:
|
|
473
485
|
file_io.write(updated_data)
|
|
474
486
|
|
|
475
487
|
|
|
476
488
|
def update(
|
|
477
|
-
paths: Iterable[str],
|
|
489
|
+
paths: Iterable[str | Path],
|
|
490
|
+
*,
|
|
478
491
|
ignore: Iterable[str] = (),
|
|
479
492
|
all_extra_name: str = "",
|
|
480
|
-
include_pointers:
|
|
481
|
-
exclude_pointers:
|
|
493
|
+
include_pointers: tuple[str, ...] = (),
|
|
494
|
+
exclude_pointers: tuple[str, ...] = (),
|
|
482
495
|
) -> None:
|
|
483
496
|
"""
|
|
484
497
|
Update requirement versions in the specified files.
|
|
485
498
|
|
|
486
499
|
Parameters:
|
|
487
|
-
|
|
500
|
+
paths: One or more local paths to a pyproject.toml,
|
|
488
501
|
setup.cfg, and/or requirements.txt files
|
|
489
|
-
ignore: One or more project names to ignore (leave
|
|
502
|
+
ignore: One or more project/package names to ignore (leave
|
|
503
|
+
as-is) when updating dependency requirement specifiers.
|
|
490
504
|
all_extra_name: If provided, an extra which consolidates
|
|
491
505
|
the requirements for all other extras will be added/updated to
|
|
492
|
-
|
|
506
|
+
pyproject.toml or setup.cfg (this argument is ignored for
|
|
493
507
|
requirements.txt files)
|
|
494
508
|
include_pointers: A tuple of JSON pointers indicating elements to
|
|
495
|
-
include (defaults to all elements).
|
|
509
|
+
include (defaults to all elements). This applies only to TOML
|
|
510
|
+
files (including pyproject.toml), and is ignored for all other
|
|
511
|
+
file types.
|
|
496
512
|
exclude_pointers: A tuple of JSON pointers indicating elements to
|
|
497
|
-
exclude (defaults to no exclusions).
|
|
513
|
+
exclude (defaults to no exclusions). This applies only to TOML
|
|
514
|
+
files (including pyproject.toml), and is ignored for all other
|
|
515
|
+
file types.
|
|
498
516
|
"""
|
|
499
|
-
if isinstance(paths, str):
|
|
517
|
+
if isinstance(paths, (str, Path)):
|
|
500
518
|
paths = (paths,)
|
|
501
519
|
|
|
502
|
-
def update_(path: str) -> None:
|
|
520
|
+
def update_(path: str | Path) -> None:
|
|
503
521
|
_update(
|
|
504
522
|
path,
|
|
505
523
|
ignore=ignore,
|