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/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 typing import Dict, Iterable, MutableSet, Optional, Tuple, cast
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: Dict[str, str] = {
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: Dict[str, MutableSet[str]] = {
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: Tuple[str, MutableSet[str]]
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: Optional[int] = None,
82
- include_pointers: Tuple[str, ...] = (),
83
- exclude_pointers: Tuple[str, ...] = (),
84
- ) -> Tuple[str, ...]:
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
- # Separate requirement strings from requirement files
109
- if isinstance(requirements, str):
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
- requirement_files: MutableSet[str] = set(
118
- filter(is_configuration_file, requirements)
119
- )
120
- requirement_strings: MutableSet[str] = cast(
121
- MutableSet[str], requirements - requirement_files
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
- *map(
127
- partial(
128
- iter_configuration_file_requirement_strings,
129
- include_pointers=include_pointers,
130
- exclude_pointers=exclude_pointers,
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: Optional[int] = None,
231
+ depth: int | None = None,
232
+ keep_version_specifier: Iterable[str] = (),
187
233
  ) -> Iterable[str]:
188
- def get_requirement_string(distribution_name: str) -> str:
189
- def distribution_name_matches_pattern(pattern: str) -> bool:
190
- return fnmatch(distribution_name, pattern)
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
- map(distribution_name_matches_pattern, no_version)
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, echo=False)
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_: Optional[int] = None,
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
- requirements = map(get_requirement_string, requirements)
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: Optional[int] = None,
249
- include_pointers: Tuple[str, ...] = (),
250
- exclude_pointers: Tuple[str, ...] = (),
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/ignore
262
- exclude_recursive: One or more distributions to exclude/ignore.
263
- Note: Excluding a distribution here halts recursive
264
- discovery of requirements.
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: Tuple[int, ...]
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: List[str] = []
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
- assert installed_version.release is not None
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: Set[str]
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]) -> Set[str]:
143
- ignore_set: Set[str]
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: Set[str] = _normalize_ignore_argument(ignore)
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: Set[str] = _normalize_ignore_argument(ignore)
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: List[str] = []
213
+ all_extra_requirements: list[str] = []
206
214
  extra_name: str
207
215
  extra_requirements_string: str
208
- extra_requirements: List[str]
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([""] + all_extra_requirements)
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: Set[str] = _normalize_ignore_argument(ignore)
257
+ ignore_set: set[str] = _normalize_ignore_argument(ignore)
250
258
 
251
259
  def get_updated_requirement_string(requirement: str) -> str:
252
- prefix: Optional[str] = None
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: Dict[str, Any],
305
+ document: dict[str, Any],
298
306
  ignore: Iterable[str] = (),
299
- include_pointers: Tuple[str, ...] = (),
300
- exclude_pointers: Tuple[str, ...] = (),
307
+ include_pointers: tuple[str, ...] = (),
308
+ exclude_pointers: tuple[str, ...] = (),
301
309
  ) -> None:
302
- ignore_set: Set[str] = _normalize_ignore_argument(ignore)
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: List[str]
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: Tuple[str, ...] = (),
327
- exclude_pointers: Tuple[str, ...] = (),
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: Dict[str, Any] = tomli.loads(data)
349
- updated_pyproject: Dict[str, Any] = deepcopy(original_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: Dict[str, List[str]] = (
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: List[str]
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: Tuple[str, ...] = (),
388
- exclude_pointers: Tuple[str, ...] = (),
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: Dict[str, Any] = tomli.loads(data)
411
- updated_pyproject: Dict[str, Any] = deepcopy(original_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: Tuple[str, ...] = (),
430
- exclude_pointers: Tuple[str, ...] = (),
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: Dict[str, Union[str, Iterable[str]]] = {}
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
- raise NotImplementedError(
461
- f"Updating requirements for {path} is not supported"
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(f"All requirements were already up-to-date in {path}")
477
+ print( # noqa: T201
478
+ f"All requirements were already up-to-date in {path!s}"
479
+ )
470
480
  else:
471
- print(f"Updating requirements in {path}")
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: Tuple[str, ...] = (),
481
- exclude_pointers: Tuple[str, ...] = (),
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
- path: One or more local paths to a setup.cfg,
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 as-is)
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
- setup.cfg or setup.cfg (this argument is ignored for
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,