|
1 | 1 | """Utility class to inspect an extracted wheel directory""" |
2 | | -import email |
| 2 | +import configparser |
3 | 3 | import glob |
4 | 4 | import os |
5 | 5 | import stat |
6 | 6 | import zipfile |
7 | 7 | from typing import Dict, Optional, Set |
8 | 8 |
|
9 | | -import installer |
10 | 9 | import pkg_resources |
| 10 | +import pkginfo |
11 | 11 |
|
12 | 12 |
|
13 | 13 | def current_umask() -> int: |
@@ -37,46 +37,57 @@ def path(self) -> str: |
37 | 37 |
|
38 | 38 | @property |
39 | 39 | def name(self) -> str: |
40 | | - # TODO Also available as installer.sources.WheelSource.distribution |
41 | | - return str(self.metadata['Name']) |
| 40 | + return str(self.metadata.name) |
42 | 41 |
|
43 | 42 | @property |
44 | | - def metadata(self) -> email.message.Message: |
45 | | - with installer.sources.WheelFile.open(self.path) as wheel_source: |
46 | | - metadata_contents = wheel_source.read_dist_info("METADATA") |
47 | | - metadata = installer.utils.parse_metadata_file(metadata_contents) |
48 | | - return metadata |
| 43 | + def metadata(self) -> pkginfo.Wheel: |
| 44 | + return pkginfo.get_metadata(self.path) |
49 | 45 |
|
50 | | - @property |
51 | | - def version(self) -> str: |
52 | | - # TODO Also available as installer.sources.WheelSource.version |
53 | | - return str(self.metadata["Version"]) |
54 | | - |
55 | | - def entry_points(self) -> Dict[str, tuple[str, str]]: |
| 46 | + def entry_points(self) -> Dict[str, str]: |
56 | 47 | """Returns the entrypoints defined in the current wheel |
57 | 48 |
|
58 | 49 | See https://packaging.python.org/specifications/entry-points/ for more info |
59 | 50 |
|
60 | 51 | Returns: |
61 | | - Dict[str, Tuple[str, str]]: A mapping of the entry point's name to it's module and attribute |
| 52 | + Dict[str, str]: A mappying of the entry point's name to it's method |
62 | 53 | """ |
63 | | - with installer.sources.WheelFile.open(self.path) as wheel_source: |
64 | | - if "entry_points.txt" not in wheel_source.dist_info_filenames: |
| 54 | + with zipfile.ZipFile(self.path, "r") as whl: |
| 55 | + # Calculate the location of the entry_points.txt file |
| 56 | + metadata = self.metadata |
| 57 | + name = "{}-{}".format(metadata.name.replace("-", "_"), metadata.version) |
| 58 | + |
| 59 | + # Note that the zipfile module always uses the forward slash as |
| 60 | + # directory separator, even on Windows, so don't use os.path.join |
| 61 | + # here. Reference for Python 3.10: |
| 62 | + # https://github.com/python/cpython/blob/3.10/Lib/zipfile.py#L355. |
| 63 | + # TODO: use zipfile.Path once 3.8 is our minimum supported version |
| 64 | + entry_points_path = "{}.dist-info/entry_points.txt".format(name) |
| 65 | + |
| 66 | + # If this file does not exist in the wheel, there are no entry points |
| 67 | + if entry_points_path not in whl.namelist(): |
65 | 68 | return dict() |
66 | 69 |
|
67 | | - entry_points_mapping = dict() |
68 | | - entry_points_contents = wheel_source.read_dist_info("entry_points.txt") |
69 | | - entry_points = installer.utils.parse_entrypoints(entry_points_contents) |
70 | | - for script, module, attribute, script_section in entry_points: |
71 | | - if script_section == "console": |
72 | | - entry_points_mapping[script] = (module, attribute) |
| 70 | + # Parse the avaialble entry points |
| 71 | + config = configparser.ConfigParser() |
| 72 | + try: |
| 73 | + config.read_string(whl.read(entry_points_path).decode("utf-8")) |
| 74 | + if "console_scripts" in config.sections(): |
| 75 | + return dict(config["console_scripts"]) |
| 76 | + |
| 77 | + # TODO: It's unclear what to do in a situation with duplicate sections or options. |
| 78 | + # For now, we treat the config file as though it contains no scripts. For more |
| 79 | + # details on the config parser, see: |
| 80 | + # https://docs.python.org/3.7/library/configparser.html#configparser.ConfigParser |
| 81 | + # https://docs.python.org/3.7/library/configparser.html#configparser.Error |
| 82 | + except configparser.Error: |
| 83 | + pass |
73 | 84 |
|
74 | | - return entry_points_mapping |
| 85 | + return dict() |
75 | 86 |
|
76 | 87 | def dependencies(self, extras_requested: Optional[Set[str]] = None) -> Set[str]: |
77 | 88 | dependency_set = set() |
78 | 89 |
|
79 | | - for wheel_req in self.metadata.get_all('Requires-Dist', []): |
| 90 | + for wheel_req in self.metadata.requires_dist: |
80 | 91 | req = pkg_resources.Requirement(wheel_req) # type: ignore |
81 | 92 |
|
82 | 93 | if req.marker is None or any( |
|
0 commit comments