dependence 0.1.1__tar.gz → 0.2.0__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dependence
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: Requirement (dependency) management for python projects
5
5
  Home-page: https://github.com/enorganic/dependence
6
6
  Author-email: david@belais.me
@@ -95,50 +95,43 @@ dependence update -aen all setup.cfg pyproject.toml tox.ini
95
95
 
96
96
  ```console
97
97
  $ dependence freeze -h
98
- usage: dependence freeze [-h] [-e EXCLUDE] [-er EXCLUDE_RECURSIVE]
99
- [-nv NO_VERSION] [-do] [--reverse]
98
+ usage: dependence freeze [-h] [-e EXCLUDE] [-er EXCLUDE_RECURSIVE] [-nv NO_VERSION]
99
+ [-do] [--reverse] [-d DEPTH]
100
100
  requirement [requirement ...]
101
101
 
102
- This command prints dependencies inferred from an installed
103
- distribution or project, in a similar format to the output of `pip
104
- freeze`, except that all generated requirements are specified in the
105
- format "distribution-name==0.0.0" (including for editable
106
- installations). Using this command instead of `pip freeze` to generate
107
- requirement files ensures that you don't bloat your requirements files
108
- with superfluous distributions. The default sorting starts with
109
- directly specified requirements, followed by recursively discovered
110
- requirements, in the order of discovery.
102
+ This command prints dependencies inferred from an installed distribution or project, in
103
+ a similar format to the output of `pip freeze`, except that all generated requirements
104
+ are specified in the format "distribution-name==0.0.0" (including for editable
105
+ installations). Using this command instead of `pip freeze` to generate requirement
106
+ files ensures that you don't bloat your requirements files with superfluous
107
+ distributions.
111
108
 
112
109
  positional arguments:
113
- requirement One or more requirement specifiers (for
114
- example: "requirement-name", "requirement-
115
- name[extra-a,extra-b]", ".[extra-a, extra-b]"
116
- or "../other-editable-package-
117
- directory[extra-a, extra-b]) and/or paths to a
118
- setup.py, setup.cfg, pyproject.toml, tox.ini or
119
- requirements.txt file
110
+ requirement One or more requirement specifiers (for example: "requirement-
111
+ name", "requirement-name[extra-a,extra-b]", ".[extra-a,
112
+ extra-b]" or "../other-editable-package-directory[extra-a,
113
+ extra-b]) and/or paths to a setup.py, setup.cfg,
114
+ pyproject.toml, tox.ini or requirements.txt file
120
115
 
121
116
  optional arguments:
122
117
  -h, --help show this help message and exit
123
118
  -e EXCLUDE, --exclude EXCLUDE
124
- A distribution (or comma-separated list of
125
- distributions) to exclude from the output
119
+ A distribution (or comma-separated list of distributions) to
120
+ exclude from the output
126
121
  -er EXCLUDE_RECURSIVE, --exclude-recursive EXCLUDE_RECURSIVE
127
- A distribution (or comma-separated list of
128
- distributions) to exclude from the output.
129
- Unlike -e / --exclude, this argument also
130
- precludes recursive requirement discovery for
131
- the specified packages, thereby excluding all
132
- of the excluded package's requirements which
133
- are not required by another (non-excluded)
134
- distribution.
122
+ A distribution (or comma-separated list of distributions) to
123
+ exclude from the output. Unlike -e / --exclude, this argument
124
+ also precludes recursive requirement discovery for the
125
+ specified packages, thereby excluding all of the excluded
126
+ package's requirements which are not required by another (non-
127
+ excluded) distribution.
135
128
  -nv NO_VERSION, --no-version NO_VERSION
136
- Don't include versions (only output
137
- distribution names) for packages matching
138
- this/these glob pattern(s) (note: the value
129
+ Don't include versions (only output distribution names) for
130
+ packages matching this/these glob pattern(s) (note: the value
139
131
  must be single-quoted if it contains wildcards)
140
132
  -do, --dependency-order
141
- Sort requirements so that dependents precede
142
- dependencies
133
+ Sort requirements so that dependents precede dependencies
143
134
  --reverse Print requirements in reverse order
135
+ -d DEPTH, --depth DEPTH
136
+ Depth of recursive requirement discovery
144
137
  ```
@@ -78,50 +78,43 @@ dependence update -aen all setup.cfg pyproject.toml tox.ini
78
78
 
79
79
  ```console
80
80
  $ dependence freeze -h
81
- usage: dependence freeze [-h] [-e EXCLUDE] [-er EXCLUDE_RECURSIVE]
82
- [-nv NO_VERSION] [-do] [--reverse]
81
+ usage: dependence freeze [-h] [-e EXCLUDE] [-er EXCLUDE_RECURSIVE] [-nv NO_VERSION]
82
+ [-do] [--reverse] [-d DEPTH]
83
83
  requirement [requirement ...]
84
84
 
85
- This command prints dependencies inferred from an installed
86
- distribution or project, in a similar format to the output of `pip
87
- freeze`, except that all generated requirements are specified in the
88
- format "distribution-name==0.0.0" (including for editable
89
- installations). Using this command instead of `pip freeze` to generate
90
- requirement files ensures that you don't bloat your requirements files
91
- with superfluous distributions. The default sorting starts with
92
- directly specified requirements, followed by recursively discovered
93
- requirements, in the order of discovery.
85
+ This command prints dependencies inferred from an installed distribution or project, in
86
+ a similar format to the output of `pip freeze`, except that all generated requirements
87
+ are specified in the format "distribution-name==0.0.0" (including for editable
88
+ installations). Using this command instead of `pip freeze` to generate requirement
89
+ files ensures that you don't bloat your requirements files with superfluous
90
+ distributions.
94
91
 
95
92
  positional arguments:
96
- requirement One or more requirement specifiers (for
97
- example: "requirement-name", "requirement-
98
- name[extra-a,extra-b]", ".[extra-a, extra-b]"
99
- or "../other-editable-package-
100
- directory[extra-a, extra-b]) and/or paths to a
101
- setup.py, setup.cfg, pyproject.toml, tox.ini or
102
- requirements.txt file
93
+ requirement One or more requirement specifiers (for example: "requirement-
94
+ name", "requirement-name[extra-a,extra-b]", ".[extra-a,
95
+ extra-b]" or "../other-editable-package-directory[extra-a,
96
+ extra-b]) and/or paths to a setup.py, setup.cfg,
97
+ pyproject.toml, tox.ini or requirements.txt file
103
98
 
104
99
  optional arguments:
105
100
  -h, --help show this help message and exit
106
101
  -e EXCLUDE, --exclude EXCLUDE
107
- A distribution (or comma-separated list of
108
- distributions) to exclude from the output
102
+ A distribution (or comma-separated list of distributions) to
103
+ exclude from the output
109
104
  -er EXCLUDE_RECURSIVE, --exclude-recursive EXCLUDE_RECURSIVE
110
- A distribution (or comma-separated list of
111
- distributions) to exclude from the output.
112
- Unlike -e / --exclude, this argument also
113
- precludes recursive requirement discovery for
114
- the specified packages, thereby excluding all
115
- of the excluded package's requirements which
116
- are not required by another (non-excluded)
117
- distribution.
105
+ A distribution (or comma-separated list of distributions) to
106
+ exclude from the output. Unlike -e / --exclude, this argument
107
+ also precludes recursive requirement discovery for the
108
+ specified packages, thereby excluding all of the excluded
109
+ package's requirements which are not required by another (non-
110
+ excluded) distribution.
118
111
  -nv NO_VERSION, --no-version NO_VERSION
119
- Don't include versions (only output
120
- distribution names) for packages matching
121
- this/these glob pattern(s) (note: the value
112
+ Don't include versions (only output distribution names) for
113
+ packages matching this/these glob pattern(s) (note: the value
122
114
  must be single-quoted if it contains wildcards)
123
115
  -do, --dependency-order
124
- Sort requirements so that dependents precede
125
- dependencies
116
+ Sort requirements so that dependents precede dependencies
126
117
  --reverse Print requirements in reverse order
118
+ -d DEPTH, --depth DEPTH
119
+ Depth of recursive requirement discovery
127
120
  ```
@@ -3,7 +3,7 @@ from fnmatch import fnmatch
3
3
  from importlib.metadata import Distribution
4
4
  from importlib.metadata import distribution as _get_distribution
5
5
  from itertools import chain
6
- from typing import Dict, Iterable, MutableSet, Tuple, cast
6
+ from typing import Dict, Iterable, MutableSet, Optional, Tuple, cast
7
7
 
8
8
  from more_itertools import unique_everseen
9
9
 
@@ -76,6 +76,7 @@ def get_frozen_requirements(
76
76
  no_version: Iterable[str] = (),
77
77
  dependency_order: bool = False,
78
78
  reverse: bool = False,
79
+ depth: Optional[int] = None,
79
80
  ) -> Tuple[str, ...]:
80
81
  """
81
82
  Get the (frozen) requirements for one or more specified distributions or
@@ -95,6 +96,7 @@ def get_frozen_requirements(
95
96
  (only return distribution names)
96
97
  - dependency_order (bool) = False: Sort requirements so that dependents
97
98
  precede dependencies
99
+ - depth (int|None) = None: Depth of recursive requirement discovery
98
100
  """
99
101
  # Separate requirement strings from requirement files
100
102
  if isinstance(requirements, str):
@@ -111,37 +113,42 @@ def get_frozen_requirements(
111
113
  requirement_strings: MutableSet[str] = cast(
112
114
  MutableSet[str], requirements - requirement_files
113
115
  )
114
- frozen_requirements: Iterable[str] = _iter_frozen_requirements(
115
- unique_everseen(
116
- chain(
117
- requirement_strings,
118
- *map(
119
- iter_configuration_file_requirement_strings,
120
- requirement_files,
121
- ),
122
- )
123
- ),
124
- exclude=set(
125
- chain(
126
- # Exclude requirement strings which are *not*
127
- # distribution names (such as editable package paths),
128
- # as in these cases we are typically looking for this
129
- # package's dependencies
130
- (
131
- set(
132
- map(
133
- get_requirement_string_distribution_name,
134
- requirement_strings,
135
- )
136
- )
137
- - set(map(normalize_name, requirement_strings))
138
- ),
139
- map(normalize_name, exclude),
140
- )
141
- ),
142
- exclude_recursive=set(map(normalize_name, exclude_recursive)),
143
- no_version=no_version,
116
+ frozen_requirements: Iterable[str] = unique_everseen(
117
+ chain(
118
+ requirement_strings,
119
+ *map(
120
+ iter_configuration_file_requirement_strings,
121
+ requirement_files,
122
+ ),
123
+ )
144
124
  )
125
+ if depth is not None:
126
+ depth -= 1
127
+ if (depth is None) or depth >= 0:
128
+ frozen_requirements = _iter_frozen_requirements(
129
+ frozen_requirements,
130
+ exclude=set(
131
+ chain(
132
+ # Exclude requirement strings which are *not*
133
+ # distribution names (such as editable package paths),
134
+ # as in these cases we are typically looking for this
135
+ # package's dependencies
136
+ (
137
+ set(
138
+ map(
139
+ get_requirement_string_distribution_name,
140
+ requirement_strings,
141
+ )
142
+ )
143
+ - set(map(normalize_name, requirement_strings))
144
+ ),
145
+ map(normalize_name, exclude),
146
+ )
147
+ ),
148
+ exclude_recursive=set(map(normalize_name, exclude_recursive)),
149
+ no_version=no_version,
150
+ depth=depth,
151
+ )
145
152
  if dependency_order:
146
153
  frozen_requirements = tuple(
147
154
  _iter_sort_dependents_last(frozen_requirements)
@@ -165,6 +172,7 @@ def _iter_frozen_requirements(
165
172
  exclude: MutableSet[str],
166
173
  exclude_recursive: MutableSet[str],
167
174
  no_version: Iterable[str] = (),
175
+ depth: Optional[int] = None,
168
176
  ) -> Iterable[str]:
169
177
  def get_requirement_string(distribution_name: str) -> str:
170
178
  def distribution_name_matches_pattern(pattern: str) -> bool:
@@ -185,25 +193,35 @@ def _iter_frozen_requirements(
185
193
 
186
194
  def get_required_distribution_names_(
187
195
  requirement_string: str,
196
+ depth_: Optional[int] = None,
188
197
  ) -> MutableSet[str]:
189
198
  name: str = get_requirement_string_distribution_name(
190
199
  requirement_string
191
200
  )
192
201
  if name in exclude_recursive:
193
202
  return set()
203
+ distribution_names: MutableSet[str] = {name}
204
+ if (depth_ is None) or depth_:
205
+ distribution_names |= get_required_distribution_names(
206
+ requirement_string,
207
+ exclude=exclude_recursive,
208
+ depth=None if (depth_ is None) else depth_ - 1,
209
+ )
194
210
  return cast(
195
211
  MutableSet[str],
196
- (
197
- set((name,))
198
- | get_required_distribution_names(
199
- requirement_string, exclude=exclude_recursive
200
- )
201
- )
202
- - exclude,
212
+ distribution_names - exclude,
203
213
  )
204
214
 
215
+ distribution_names: MutableSet[str]
205
216
  requirements: Iterable[str] = unique_everseen(
206
- chain(*map(get_required_distribution_names_, requirement_strings)),
217
+ chain(
218
+ *map(
219
+ lambda distribution_names: get_required_distribution_names_(
220
+ distribution_names, None if (depth is None) else depth - 1
221
+ ),
222
+ requirement_strings,
223
+ )
224
+ ),
207
225
  )
208
226
 
209
227
  requirements = map(get_requirement_string, requirements)
@@ -217,6 +235,7 @@ def freeze(
217
235
  no_version: Iterable[str] = (),
218
236
  dependency_order: bool = False,
219
237
  reverse: bool = False,
238
+ depth: Optional[int] = None,
220
239
  ) -> None:
221
240
  """
222
241
  Print the (frozen) requirements for one or more specified requirements or
@@ -238,6 +257,7 @@ def freeze(
238
257
  patterns
239
258
  - dependency_order (bool) = False: Sort requirements so that dependents
240
259
  precede dependencies
260
+ - depth (int|None) = None: Depth of recursive requirement discovery
241
261
  """
242
262
  print(
243
263
  "\n".join(
@@ -248,6 +268,7 @@ def freeze(
248
268
  no_version=no_version,
249
269
  dependency_order=dependency_order,
250
270
  reverse=reverse,
271
+ depth=depth,
251
272
  )
252
273
  )
253
274
  )
@@ -331,15 +352,23 @@ def main() -> None:
331
352
  action="store_true",
332
353
  help="Print requirements in reverse order",
333
354
  )
334
- arguments: argparse.Namespace = parser.parse_args()
355
+ parser.add_argument(
356
+ "-d",
357
+ "--depth",
358
+ default=None,
359
+ type=int,
360
+ help="Depth of recursive requirement discovery",
361
+ )
362
+ namespace: argparse.Namespace = parser.parse_args()
335
363
  freeze(
336
- requirements=arguments.requirement,
337
- exclude=tuple(iter_parse_delimited_values(arguments.exclude)),
364
+ requirements=namespace.requirement,
365
+ exclude=tuple(iter_parse_delimited_values(namespace.exclude)),
338
366
  exclude_recursive=tuple(
339
- iter_parse_delimited_values(arguments.exclude_recursive)
367
+ iter_parse_delimited_values(namespace.exclude_recursive)
340
368
  ),
341
- no_version=arguments.no_version,
342
- dependency_order=arguments.dependency_order,
369
+ no_version=namespace.no_version,
370
+ dependency_order=namespace.dependency_order,
371
+ depth=namespace.depth,
343
372
  )
344
373
 
345
374
 
@@ -636,6 +636,7 @@ def get_required_distribution_names(
636
636
  exclude: Iterable[str] = (),
637
637
  recursive: bool = True,
638
638
  echo: bool = False,
639
+ depth: Optional[int] = None,
639
640
  ) -> MutableSet[str]:
640
641
  """
641
642
  Return a `tuple` of all distribution names which are required by the
@@ -652,6 +653,8 @@ def get_required_distribution_names(
652
653
  be obtained recursively.
653
654
  - echo (bool) = False: If `True`, commands and responses executed in
654
655
  subprocesses will be printed to `sys.stdout`
656
+ - depth (int|None) = None: The maximum depth of recursion to follow
657
+ requirements. If `None` (the default), recursion is not restricted.
655
658
  """
656
659
  if isinstance(exclude, str):
657
660
  exclude = set((normalize_name(exclude),))
@@ -663,6 +666,7 @@ def get_required_distribution_names(
663
666
  exclude=exclude,
664
667
  recursive=recursive,
665
668
  echo=echo,
669
+ depth=depth,
666
670
  )
667
671
  )
668
672
 
@@ -817,6 +821,7 @@ def _iter_requirement_names(
817
821
  exclude: MutableSet[str],
818
822
  recursive: bool = True,
819
823
  echo: bool = False,
824
+ depth: Optional[int] = None,
820
825
  ) -> Iterable[str]:
821
826
  name: str = normalize_name(requirement.name)
822
827
  extras: Tuple[str, ...] = tuple(requirement.extras)
@@ -843,20 +848,23 @@ def _iter_requirement_names(
843
848
 
844
849
  def iter_requirement_names_(
845
850
  requirement_: Requirement,
851
+ depth_: Optional[int] = None,
846
852
  ) -> Iterable[str]:
847
- return _iter_requirement_names(
848
- requirement_,
849
- exclude=cast(
850
- MutableSet[str],
851
- exclude
852
- | (
853
- lateral_exclude
854
- - set((_get_requirement_name(requirement_),))
853
+ if (depth_ is None) or depth_ >= 0:
854
+ yield from _iter_requirement_names(
855
+ requirement_,
856
+ exclude=cast(
857
+ MutableSet[str],
858
+ exclude
859
+ | (
860
+ lateral_exclude
861
+ - set((_get_requirement_name(requirement_),))
862
+ ),
855
863
  ),
856
- ),
857
- recursive=recursive,
858
- echo=echo,
859
- )
864
+ recursive=recursive,
865
+ echo=echo,
866
+ depth=None if (depth_ is None) else depth_ - 1,
867
+ )
860
868
 
861
869
  def not_excluded(name: str) -> bool:
862
870
  if name not in exclude:
@@ -865,10 +873,19 @@ def _iter_requirement_names(
865
873
  return True
866
874
  return False
867
875
 
876
+ requirement_names: Iterable[str] = filter(
877
+ not_excluded, map(_get_requirement_name, requirements)
878
+ )
868
879
  if recursive:
880
+ requirement_: Requirement
869
881
  requirement_names = chain(
870
- filter(not_excluded, map(_get_requirement_name, requirements)),
871
- *map(iter_requirement_names_, requirements),
882
+ requirement_names,
883
+ *map(
884
+ lambda requirement_: iter_requirement_names_(
885
+ requirement_, None if (depth is None) else depth - 1
886
+ ),
887
+ requirements,
888
+ ),
872
889
  )
873
890
  return requirement_names
874
891
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dependence
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: Requirement (dependency) management for python projects
5
5
  Home-page: https://github.com/enorganic/dependence
6
6
  Author-email: david@belais.me
@@ -95,50 +95,43 @@ dependence update -aen all setup.cfg pyproject.toml tox.ini
95
95
 
96
96
  ```console
97
97
  $ dependence freeze -h
98
- usage: dependence freeze [-h] [-e EXCLUDE] [-er EXCLUDE_RECURSIVE]
99
- [-nv NO_VERSION] [-do] [--reverse]
98
+ usage: dependence freeze [-h] [-e EXCLUDE] [-er EXCLUDE_RECURSIVE] [-nv NO_VERSION]
99
+ [-do] [--reverse] [-d DEPTH]
100
100
  requirement [requirement ...]
101
101
 
102
- This command prints dependencies inferred from an installed
103
- distribution or project, in a similar format to the output of `pip
104
- freeze`, except that all generated requirements are specified in the
105
- format "distribution-name==0.0.0" (including for editable
106
- installations). Using this command instead of `pip freeze` to generate
107
- requirement files ensures that you don't bloat your requirements files
108
- with superfluous distributions. The default sorting starts with
109
- directly specified requirements, followed by recursively discovered
110
- requirements, in the order of discovery.
102
+ This command prints dependencies inferred from an installed distribution or project, in
103
+ a similar format to the output of `pip freeze`, except that all generated requirements
104
+ are specified in the format "distribution-name==0.0.0" (including for editable
105
+ installations). Using this command instead of `pip freeze` to generate requirement
106
+ files ensures that you don't bloat your requirements files with superfluous
107
+ distributions.
111
108
 
112
109
  positional arguments:
113
- requirement One or more requirement specifiers (for
114
- example: "requirement-name", "requirement-
115
- name[extra-a,extra-b]", ".[extra-a, extra-b]"
116
- or "../other-editable-package-
117
- directory[extra-a, extra-b]) and/or paths to a
118
- setup.py, setup.cfg, pyproject.toml, tox.ini or
119
- requirements.txt file
110
+ requirement One or more requirement specifiers (for example: "requirement-
111
+ name", "requirement-name[extra-a,extra-b]", ".[extra-a,
112
+ extra-b]" or "../other-editable-package-directory[extra-a,
113
+ extra-b]) and/or paths to a setup.py, setup.cfg,
114
+ pyproject.toml, tox.ini or requirements.txt file
120
115
 
121
116
  optional arguments:
122
117
  -h, --help show this help message and exit
123
118
  -e EXCLUDE, --exclude EXCLUDE
124
- A distribution (or comma-separated list of
125
- distributions) to exclude from the output
119
+ A distribution (or comma-separated list of distributions) to
120
+ exclude from the output
126
121
  -er EXCLUDE_RECURSIVE, --exclude-recursive EXCLUDE_RECURSIVE
127
- A distribution (or comma-separated list of
128
- distributions) to exclude from the output.
129
- Unlike -e / --exclude, this argument also
130
- precludes recursive requirement discovery for
131
- the specified packages, thereby excluding all
132
- of the excluded package's requirements which
133
- are not required by another (non-excluded)
134
- distribution.
122
+ A distribution (or comma-separated list of distributions) to
123
+ exclude from the output. Unlike -e / --exclude, this argument
124
+ also precludes recursive requirement discovery for the
125
+ specified packages, thereby excluding all of the excluded
126
+ package's requirements which are not required by another (non-
127
+ excluded) distribution.
135
128
  -nv NO_VERSION, --no-version NO_VERSION
136
- Don't include versions (only output
137
- distribution names) for packages matching
138
- this/these glob pattern(s) (note: the value
129
+ Don't include versions (only output distribution names) for
130
+ packages matching this/these glob pattern(s) (note: the value
139
131
  must be single-quoted if it contains wildcards)
140
132
  -do, --dependency-order
141
- Sort requirements so that dependents precede
142
- dependencies
133
+ Sort requirements so that dependents precede dependencies
143
134
  --reverse Print requirements in reverse order
135
+ -d DEPTH, --depth DEPTH
136
+ Depth of recursive requirement discovery
144
137
  ```
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = dependence
3
- version = 0.1.1
3
+ version = 0.2.0
4
4
  author_email = david@belais.me
5
5
  description = Requirement (dependency) management for python projects
6
6
  long_description = file: README.md
File without changes
File without changes