dependence 0.3.1__tar.gz → 0.3.5__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.3.1
3
+ Version: 0.3.5
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
@@ -1,32 +1,25 @@
1
- import os
2
1
  import sys
3
- from functools import lru_cache
2
+ from functools import lru_cache, wraps
4
3
  from itertools import chain
5
- from subprocess import check_output as _check_output
6
- from tempfile import mktemp
4
+ from pathlib import Path
5
+ from subprocess import DEVNULL, PIPE, list2cmdline, run
7
6
  from traceback import format_exception
8
- from typing import Any, Dict, Hashable, Iterable, List, Set, Tuple
7
+ from typing import (
8
+ Any,
9
+ Callable,
10
+ Dict,
11
+ Hashable,
12
+ Iterable,
13
+ List,
14
+ Set,
15
+ Tuple,
16
+ Union,
17
+ )
18
+ from warnings import warn
9
19
 
10
20
  import tomli
11
21
 
12
22
 
13
- def check_output(command: Tuple[str, ...]) -> str:
14
- """
15
- This function wraps `subprocess.check_output`, but redirects stderr
16
- to a temporary file, then deletes that file (a platform-independent
17
- means of redirecting output to DEVNULL).
18
-
19
- Parameters:
20
-
21
- - command (Tuple[str, ...]): The command to run
22
- """
23
- stderr_path: str = mktemp()
24
- with open(stderr_path, "w") as stderr:
25
- output = _check_output(command, stderr=stderr, text=True)
26
- os.remove(stderr_path)
27
- return output
28
-
29
-
30
23
  def iter_distinct(items: Iterable[Hashable]) -> Iterable:
31
24
  """
32
25
  Yield distinct elements, preserving order
@@ -102,3 +95,81 @@ def iter_parse_delimited_values(
102
95
  return _iter_parse_delimited_value(value, delimiter=delimiter)
103
96
 
104
97
  return chain(*map(iter_parse_delimited_value_, values))
98
+
99
+
100
+ def check_output(
101
+ args: Tuple[str, ...],
102
+ cwd: Union[str, Path] = "",
103
+ echo: bool = False,
104
+ ) -> str:
105
+ """
106
+ This function mimics `subprocess.check_output`, but redirects stderr
107
+ to DEVNULL, and ignores unicode decoding errors.
108
+
109
+ Parameters:
110
+
111
+ - command (Tuple[str, ...]): The command to run
112
+ """
113
+ if echo:
114
+ if cwd:
115
+ print("$", "cd", cwd, "&&", list2cmdline(args))
116
+ else:
117
+ print("$", list2cmdline(args))
118
+ output: str = run(
119
+ args,
120
+ stdout=PIPE,
121
+ stderr=DEVNULL,
122
+ check=True,
123
+ cwd=cwd or None,
124
+ ).stdout.decode("utf-8", errors="ignore")
125
+ if echo:
126
+ print(output)
127
+ return output
128
+
129
+
130
+ def get_qualified_name(function: Callable[..., Any]) -> str:
131
+ name: str = getattr(function, "__name__", "")
132
+ if name:
133
+ module: str = getattr(function, "__module__", "")
134
+ if module not in (
135
+ "builtins",
136
+ "__builtin__",
137
+ "__main__",
138
+ "__init__",
139
+ "",
140
+ ):
141
+ name = f"{module}.{name}"
142
+ return name
143
+
144
+
145
+ def deprecated(message: str = "") -> Callable[..., Callable[..., Any]]:
146
+ """
147
+ This decorator marks a function as deprecated, and issues a
148
+ deprecation warning when the function is called.
149
+ """
150
+
151
+ def decorating_function(
152
+ function: Callable[..., Any]
153
+ ) -> Callable[..., Any]:
154
+
155
+ @wraps(function)
156
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
157
+ name: str = get_qualified_name(function)
158
+ warn(
159
+ (
160
+ (
161
+ f"{name} is deprecated: {message}"
162
+ if message
163
+ else f"{name} is deprecated"
164
+ )
165
+ if name
166
+ else message
167
+ ),
168
+ DeprecationWarning,
169
+ stacklevel=2,
170
+ )
171
+ return function(*args, **kwargs)
172
+
173
+ return wrapper
174
+
175
+ return decorating_function
@@ -6,15 +6,14 @@ from collections import deque
6
6
  from configparser import ConfigParser, SectionProxy
7
7
  from enum import Enum, auto
8
8
  from glob import iglob
9
- from importlib.metadata import Distribution, PackageNotFoundError, PackagePath
9
+ from importlib.metadata import Distribution, PackageNotFoundError
10
10
  from importlib.metadata import distribution as _get_distribution
11
11
  from importlib.metadata import distributions as _get_distributions
12
12
  from itertools import chain
13
13
  from pathlib import Path
14
14
  from runpy import run_path
15
- from shutil import move, rmtree
16
- from subprocess import CalledProcessError
17
- from tempfile import mkdtemp
15
+ from shutil import rmtree
16
+ from subprocess import CalledProcessError, list2cmdline
18
17
  from types import ModuleType
19
18
  from typing import (
20
19
  IO,
@@ -39,6 +38,7 @@ from packaging.utils import canonicalize_name
39
38
  from ._utilities import (
40
39
  append_exception_text,
41
40
  check_output,
41
+ deprecated,
42
42
  get_exception_text,
43
43
  iter_distinct,
44
44
  )
@@ -172,63 +172,6 @@ def cache_clear() -> None:
172
172
  get_requirement_string_distribution_name.cache_clear()
173
173
 
174
174
 
175
- def _iter_find_dist_info(directory: Path, name: str) -> Iterable[Path]:
176
- """
177
- Find all *.dist-info directories for a project in the specified directory
178
- (there shouldn't be more than one, but pip issues can cause there to
179
- be on occasions).
180
- """
181
- yield from filter(
182
- Path.is_dir,
183
- directory.glob(f"{name.replace('-', '_')}" "-*.dist-info"),
184
- )
185
-
186
-
187
- def _move_dist_info_to_temp_directory(dist_info_directory: Path) -> Path:
188
- """
189
- Move the contents of *.dist-info directories for a project into a temporary
190
- directory, and return the path to that temp directory.
191
- """
192
- temp_directory: Path = Path(mkdtemp())
193
- file_path: Path
194
- for file_path in dist_info_directory.iterdir():
195
- move(str(file_path), temp_directory.joinpath(file_path.name))
196
- rmtree(dist_info_directory)
197
- return temp_directory
198
-
199
-
200
- def _merge_directories(
201
- source_directory: Path, target_directory: Path, overwrite: bool = False
202
- ) -> None:
203
- source_file_path: Path
204
- target_file_path: Path
205
- if not target_directory.exists():
206
- target_directory.mkdir()
207
- for source_file_path in source_directory.iterdir():
208
- target_file_path = target_directory.joinpath(source_file_path.name)
209
- if overwrite or (not target_file_path.exists()):
210
- move(str(source_file_path), target_file_path)
211
- rmtree(source_directory)
212
-
213
-
214
- def _get_distribution_dist_info_path(
215
- distribution: Distribution,
216
- ) -> Optional[Path]:
217
- """
218
- Get the dist-info directory for a distribution
219
- (a safer method for accessing the equivalent of `distribution._path`)
220
- """
221
- return distribution._path # type: ignore
222
- if distribution.files is None:
223
- return None
224
- package_path: PackagePath
225
- for package_path in distribution.files:
226
- directory = Path(package_path.locate()).parent
227
- if directory.name.endswith(".dist-info"):
228
- return directory
229
- return None
230
-
231
-
232
175
  def refresh_editable_distributions() -> None:
233
176
  """
234
177
  Update distribution information for editable installs
@@ -236,31 +179,7 @@ def refresh_editable_distributions() -> None:
236
179
  name: str
237
180
  location: str
238
181
  for name, location in get_editable_distributions_locations().items():
239
- distribution: Distribution = _get_distribution(name)
240
- dist_info_path: Optional[Path] = _get_distribution_dist_info_path(
241
- distribution
242
- )
243
- if dist_info_path is None:
244
- continue
245
- dist_info_directory: Path = dist_info_path.parent
246
- location_path: Path = Path(location)
247
- # Find pre-existing dist-info directories, and rename them so that
248
- # the new dist-info directory doesn't overwrite the old one, then
249
- # merge the two directories, replacing old files with new files
250
- # when they exist in both
251
- temp_directory: Path = _move_dist_info_to_temp_directory(
252
- dist_info_path
253
- )
254
- try:
255
- setup_egg_info(location_path)
256
- if dist_info_directory != location_path:
257
- setup_dist_info(location_path, dist_info_directory)
258
- finally:
259
- _merge_directories(
260
- temp_directory,
261
- dist_info_path,
262
- overwrite=False,
263
- )
182
+ _install_requirement_string(location, name=name, editable=True)
264
183
 
265
184
 
266
185
  @functools.lru_cache()
@@ -652,6 +571,7 @@ def _setup_location(
652
571
  os.chdir(current_directory)
653
572
 
654
573
 
574
+ @deprecated()
655
575
  def setup_dist_egg_info(directory: str) -> None:
656
576
  """
657
577
  Refresh dist-info and egg-info for the editable package installed in
@@ -673,6 +593,7 @@ def get_editable_distribution_location(name: str) -> str:
673
593
  return get_editable_distributions_locations().get(normalize_name(name), "")
674
594
 
675
595
 
596
+ @deprecated()
676
597
  def setup_dist_info(
677
598
  directory: Union[str, Path], output_dir: Union[str, Path] = ""
678
599
  ) -> None:
@@ -685,13 +606,13 @@ def setup_dist_info(
685
606
  directory = directory.absolute()
686
607
  if not directory.is_dir():
687
608
  directory = directory.parent
688
- if isinstance(output_dir, Path):
689
- output_dir = str(output_dir)
690
- return _setup_location(
609
+ if isinstance(output_dir, str) and output_dir:
610
+ output_dir = Path(output_dir)
611
+ _setup_location(
691
612
  directory,
692
613
  (
693
614
  ("-q", "dist_info")
694
- + (("--output-dir", output_dir) if output_dir else ()),
615
+ + (("--output-dir", str(output_dir)) if output_dir else ()),
695
616
  ),
696
617
  )
697
618
 
@@ -699,7 +620,7 @@ def setup_dist_info(
699
620
  def setup_egg_info(directory: Union[str, Path], egg_base: str = "") -> None:
700
621
  """
701
622
  Refresh egg-info for the editable package installed in
702
- `directory`
623
+ `directory` (only applicable for packages using a `setup.py` script)
703
624
  """
704
625
  if isinstance(directory, str):
705
626
  directory = Path(directory)
@@ -715,7 +636,7 @@ def setup_egg_info(directory: Union[str, Path], egg_base: str = "") -> None:
715
636
  dist_info_path: Path = Path(dist_info)
716
637
  if not dist_info_path.joinpath("RECORD").is_file():
717
638
  rmtree(dist_info_path)
718
- return _setup_location(
639
+ _setup_location(
719
640
  directory,
720
641
  (("-q", "egg_info") + (("--egg-base", egg_base) if egg_base else ()),),
721
642
  )
@@ -808,46 +729,61 @@ def _install_requirement_string(
808
729
  name: str = "",
809
730
  editable: bool = False,
810
731
  ) -> None:
811
- uncaught_error: Optional[Exception] = None
812
- flags: Tuple[str, ...]
813
- for flags in ((),) + ((("--force-reinstall",),) if editable else ()):
814
- if editable:
815
- flags += ("-e",)
816
- try:
817
- check_output(
818
- (
819
- sys.executable,
820
- "-m",
821
- "pip",
822
- "install",
823
- "--no-deps",
824
- "--no-compile",
825
- "--no-build-isolation",
826
- )
827
- + flags
828
- + (requirement_string,)
829
- )
830
- uncaught_error = None
831
- break
832
- except CalledProcessError as error:
833
- if (uncaught_error is None) or (not flags):
834
- uncaught_error = error
835
- if uncaught_error is not None:
836
- append_exception_text(
837
- uncaught_error,
732
+ """
733
+ Install a requirement string with no dependencies, compilation, build
734
+ isolation, etc.
735
+ """
736
+ command: Tuple[str, ...] = (
737
+ sys.executable,
738
+ "-m",
739
+ "pip",
740
+ "install",
741
+ "--no-deps",
742
+ "--no-compile",
743
+ "--no-build-isolation",
744
+ )
745
+ if editable:
746
+ command += (
747
+ "-e",
748
+ requirement_string,
749
+ "--config-settings",
750
+ "editable_mode=compat",
751
+ )
752
+ else:
753
+ command += (requirement_string,)
754
+ try:
755
+ check_output(command)
756
+ except CalledProcessError as error:
757
+ message: str = (
838
758
  (
839
- (
840
- f"\nCould not install {name}"
841
- if name == requirement_string
842
- else (
843
- f"\nCould not install {name} from {requirement_string}"
844
- )
759
+ f"\n{list2cmdline(command)}" f"\nCould not install {name}"
760
+ if name == requirement_string
761
+ else (
762
+ f"\n{list2cmdline(command)}"
763
+ f"\nCould not install {name} from "
764
+ f"{requirement_string}"
845
765
  )
846
- if name
847
- else (f"\nCould not install {requirement_string}")
848
- ),
766
+ )
767
+ if name
768
+ else (
769
+ f"\n{list2cmdline(command)}"
770
+ f"\nCould not install {requirement_string}"
771
+ )
849
772
  )
850
- raise uncaught_error
773
+ if not editable:
774
+ append_exception_text(
775
+ error,
776
+ message,
777
+ )
778
+ raise error
779
+ try:
780
+ check_output(command + ("--force-reinstall",))
781
+ except CalledProcessError as retry_error:
782
+ append_exception_text(
783
+ retry_error,
784
+ message,
785
+ )
786
+ raise retry_error
851
787
 
852
788
 
853
789
  def _install_requirement(
@@ -1000,6 +936,7 @@ def _iter_requirement_names(
1000
936
  return requirement_names
1001
937
 
1002
938
 
939
+ @deprecated()
1003
940
  def _iter_requirement_strings_required_distribution_names(
1004
941
  requirement_strings: Iterable[str],
1005
942
  echo: bool = False,
@@ -1033,6 +970,7 @@ def _iter_requirement_strings_required_distribution_names(
1033
970
  )
1034
971
 
1035
972
 
973
+ @deprecated()
1036
974
  def get_requirements_required_distribution_names(
1037
975
  requirements: Iterable[str] = (),
1038
976
  echo: bool = False,
@@ -1075,6 +1013,7 @@ def get_requirements_required_distribution_names(
1075
1013
  )
1076
1014
 
1077
1015
 
1016
+ @deprecated()
1078
1017
  def iter_distribution_location_file_paths(location: str) -> Iterable[str]:
1079
1018
  location = os.path.abspath(location)
1080
1019
  name: str = get_setup_distribution_name(location)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: dependence
3
- Version: 0.3.1
3
+ Version: 0.3.5
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
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = dependence
3
- version = 0.3.1
3
+ version = 0.3.5
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
File without changes