|
1 | 1 | """Utility class to inspect an extracted wheel directory"""
|
2 | 2 | import configparser
|
| 3 | +import email |
3 | 4 | import glob
|
4 | 5 | import os
|
5 | 6 | import stat
|
6 | 7 | import zipfile
|
7 | 8 | from typing import Dict, Optional, Set
|
8 | 9 |
|
| 10 | +import installer |
9 | 11 | import pkg_resources
|
10 |
| -import pkginfo |
11 | 12 |
|
12 | 13 |
|
13 | 14 | def current_umask() -> int:
|
@@ -37,57 +38,44 @@ def path(self) -> str:
|
37 | 38 |
|
38 | 39 | @property
|
39 | 40 | def name(self) -> str:
|
40 |
| - return str(self.metadata.name) |
| 41 | + # TODO Also available as installer.sources.WheelSource.distribution |
| 42 | + return str(self.metadata['Name']) |
41 | 43 |
|
42 | 44 | @property
|
43 |
| - def metadata(self) -> pkginfo.Wheel: |
44 |
| - return pkginfo.get_metadata(self.path) |
| 45 | + def metadata(self) -> email.message.Message: |
| 46 | + with WheelFile.open(self.path) as wheel_source: |
| 47 | + metadata_file = wheel_source.read_dist_info("METADATA") |
| 48 | + metadata = installer.utils.parse_metadata_file(metadata_file) |
| 49 | + return metadata |
45 | 50 |
|
46 |
| - def entry_points(self) -> Dict[str, str]: |
| 51 | + @property |
| 52 | + def version(self) -> str: |
| 53 | + # TODO Also available as installer.sources.WheelSource.version |
| 54 | + return str(self.metadata["Version"]) |
| 55 | + |
| 56 | + def entry_points(self) -> Dict[str, Tuple[str, str]]: |
47 | 57 | """Returns the entrypoints defined in the current wheel
|
48 | 58 |
|
49 | 59 | See https://packaging.python.org/specifications/entry-points/ for more info
|
50 | 60 |
|
51 | 61 | Returns:
|
52 |
| - Dict[str, str]: A mappying of the entry point's name to it's method |
| 62 | + Dict[str, Tuple[str, str]]: A mapping of the entry point's name to it's module and attribute |
53 | 63 | """
|
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(): |
| 64 | + with WheelFile.open(self.path) as wheel_source: |
| 65 | + entry_points_file = wheel_source.read_dist_info("entry_points.txt") |
| 66 | + if entry_points_file is None: |
68 | 67 | return dict()
|
69 | 68 |
|
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 |
84 |
| - |
85 |
| - return dict() |
| 69 | + d = dict() |
| 70 | + entry_points = installer.utils.parse_entrypoints(entry_points_file) |
| 71 | + for script, module, attribute, kind in entry_points: |
| 72 | + if kind == "console": |
| 73 | + d[script] = (module, attribute) |
86 | 74 |
|
87 | 75 | def dependencies(self, extras_requested: Optional[Set[str]] = None) -> Set[str]:
|
88 | 76 | dependency_set = set()
|
89 | 77 |
|
90 |
| - for wheel_req in self.metadata.requires_dist: |
| 78 | + for wheel_req in self.metadata.get_all('Requires-Dist'): |
91 | 79 | req = pkg_resources.Requirement(wheel_req) # type: ignore
|
92 | 80 |
|
93 | 81 | if req.marker is None or any(
|
|
0 commit comments