Skip to content

Commit d4eaa3c

Browse files
authored
Merge pull request #120 from wimglenn/no-pkginfo
remove dependency on pkginfo
2 parents a0d7ded + a51a535 commit d4eaa3c

File tree

8 files changed

+84
-61
lines changed

8 files changed

+84
-61
lines changed

.github/workflows/ci.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ jobs:
1515
strategy:
1616
matrix:
1717
os: [ubuntu-latest, macos-latest, windows-latest]
18-
python-version: ["2.7", "3.11"]
18+
python-version:
19+
- "2.7"
20+
- "3.7"
21+
- "3.8"
22+
- "3.9"
23+
- "3.10"
24+
- "3.11"
1925

2026
steps:
2127
- uses: "actions/checkout@v3"

johnnydep/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""Display dependency tree of Python distribution"""
22

3-
__version__ = "1.19.0"
3+
__version__ = "1.20.0"
44

55
from johnnydep.lib import *

johnnydep/compat.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,7 @@ def urlretrieve(url, filename, data=None, auth=None):
7070
try:
7171
from zipfile import Path as zipfile_path
7272
except ImportError:
73-
from zipfile39 import Path as zipfile_path
73+
try:
74+
from zipfile38 import Path as zipfile_path
75+
except (ImportError, SyntaxError):
76+
from zipfile39 import Path as zipfile_path

johnnydep/lib.py

Lines changed: 63 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
from zipfile import ZipFile
1212

1313
import anytree
14-
import pkginfo
1514
import toml
1615
import tabulate
1716
import wimpy
@@ -158,18 +157,14 @@ def children(self):
158157

159158
@property
160159
def homepage(self):
161-
for project_url in self.metadata.get("project_urls", []):
160+
for project_url in self.metadata.get("project_url", []):
162161
if project_url.lower().startswith("homepage, "):
163162
_, url = project_url.split(", ", 1)
164163
return url
165164
try:
166165
return self.metadata["home_page"]
167166
except KeyError:
168-
for k in "python.details", "python.project":
169-
try:
170-
return self.metadata["extensions"][k]["project_urls"]["Home"]
171-
except KeyError:
172-
pass
167+
pass
173168
self.log.info("unknown homepage")
174169

175170
@property
@@ -185,7 +180,7 @@ def license(self):
185180
# for a list of valid classifiers:
186181
# requests.get('https://pypi.python.org/pypi', params={':action': 'list_classifiers'}).text.splitlines()
187182
self.log.debug("metadata license is not set, checking trove classifiers")
188-
for classifier in self.metadata.get("classifiers", []):
183+
for classifier in self.metadata.get("classifier", []):
189184
if classifier.startswith("License :: "):
190185
crap, result = classifier.rsplit(" :: ", 1)
191186
break
@@ -243,10 +238,10 @@ def version_latest_in_spec(self):
243238

244239
@property
245240
def extras_available(self):
246-
extras = {x for x in self.metadata.get("provides_extras", []) if x}
241+
extras = {x for x in self.metadata.get("provides_extra", []) if x}
247242
for req_str in self.metadata.get("requires_dist", []):
248243
req = requirements.Requirement(req_str)
249-
extras |= set(re.findall(r'extra == "(.*?)"', str(req.marker)))
244+
extras |= set(re.findall(r"""extra == ['"](.*?)['"]""", str(req.marker)))
250245
return sorted(extras)
251246

252247
@property
@@ -256,7 +251,7 @@ def project_name(self):
256251
@property
257252
def console_scripts(self):
258253
eps = [ep for ep in self.entry_points or [] if ep.group == "console_scripts"]
259-
return ", ".join(["{} = {}".format(ep.name, ep.value) for ep in eps])
254+
return ["{} = {}".format(ep.name, ep.value) for ep in eps]
260255

261256
@property
262257
def pinned(self):
@@ -435,52 +430,78 @@ def _discover_import_names(whl_file):
435430
try:
436431
[top_level_fname] = [x for x in namelist if x.endswith("top_level.txt")]
437432
except ValueError:
438-
log.debug("top_level absent, trying metadata")
439-
metadata = _extract_metadata(whl_file)
440-
try:
441-
public_names = metadata["python.exports"]["modules"] or []
442-
except KeyError:
443-
# this dist was packaged by a dinosaur, exports is not in metadata.
444-
# we gotta do it the hard way ...
445-
public_names = []
446-
for name in namelist:
447-
if ".dist-info/" not in name and ".egg-info/" not in name:
448-
parts = name.split("/")
449-
if len(parts) == 2 and parts[1] == "__init__.py":
450-
# found a top-level package
451-
public_names.append(parts[0])
452-
elif len(parts) == 1:
453-
# TODO: find or make an exhaustive list of file extensions importable
454-
name, ext = os.path.splitext(parts[0])
455-
if ext == ".py" or ext == ".so":
456-
# found a top level module
457-
public_names.append(name)
433+
log.debug("top_level.txt absent, iterating contents")
434+
# we gotta do it the hard way ...
435+
public_names = []
436+
for name in namelist:
437+
if ".dist-info/" not in name and ".egg-info/" not in name:
438+
parts = name.split("/")
439+
if len(parts) == 2 and parts[1] == "__init__.py":
440+
# found a top-level package
441+
public_names.append(parts[0])
442+
elif len(parts) == 1:
443+
# TODO: find or make an exhaustive list of file extensions importable
444+
name, ext = os.path.splitext(parts[0])
445+
if ext == ".py" or ext == ".so":
446+
# found a top level module
447+
public_names.append(name)
458448
else:
459449
all_names = zf.read(top_level_fname).decode("utf-8").strip().splitlines()
460450
public_names = [n for n in all_names if not n.startswith("_")]
461451
result = [n.replace("/", ".") for n in public_names]
462452
return result
463453

464454

465-
def _discover_entry_points(whl_file):
466-
log = logger.bind(whl_file=whl_file)
467-
log.debug("finding entry points")
455+
def _path_dist(whl_file):
468456
parts = os.path.basename(whl_file).split("-")
469457
metadata_path = "-".join(parts[:2]) + ".dist-info/"
470458
zf_path = zipfile_path(whl_file, metadata_path)
471-
path_dist = PathDistribution(zf_path)
459+
return PathDistribution(zf_path)
460+
461+
462+
def _discover_entry_points(whl_file):
463+
log = logger.bind(whl_file=whl_file)
464+
log.debug("finding entry points")
465+
path_dist = _path_dist(whl_file)
472466
return path_dist.entry_points
473467

474468

475469
def _extract_metadata(whl_file):
476470
log = logger.bind(whl_file=whl_file)
477-
log.debug("searching metadata", whl_file=whl_file)
478-
info = pkginfo.get_metadata(whl_file)
479-
if info is None:
480-
raise JohnnyError("failed to get metadata")
481-
data = {k.lower(): v for k,v in vars(info).items()}
482-
data.pop("filename", None)
483-
return data
471+
log.debug("finding metadata", whl_file=whl_file)
472+
path_dist = _path_dist(whl_file)
473+
message = path_dist.metadata
474+
try:
475+
result = message.json
476+
except AttributeError:
477+
# older python
478+
multiple_use_keys = {
479+
"Classifier",
480+
"Platform",
481+
"Requires-External",
482+
"Obsoletes-Dist",
483+
"Supported-Platform",
484+
"Provides-Dist",
485+
"Requires-Dist",
486+
"Project-URL",
487+
"Provides-Extra",
488+
"Dynamic",
489+
}
490+
result = {}
491+
# https://peps.python.org/pep-0566/#json-compatible-metadata
492+
for orig_key in message.keys():
493+
k = orig_key.lower().replace("-", "_")
494+
if k in result:
495+
continue
496+
if orig_key in multiple_use_keys:
497+
result[k] = message.get_all(orig_key)
498+
else:
499+
result[k] = message[orig_key]
500+
if "description" not in result:
501+
payload = message.get_payload()
502+
if payload:
503+
result["description"] = payload
504+
return result
484505

485506

486507
def has_error(dist):

setup.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,16 @@
2222
"structlog",
2323
"tabulate",
2424
"wimpy",
25-
"colorama ; python_version<'3.7' or platform_system=='Windows'", # structlog
25+
"colorama ; python_version < '3.7' or platform_system == 'Windows'", # structlog
2626
"cachetools",
2727
"oyaml",
2828
"toml",
2929
"pip",
3030
"packaging >= 17",
3131
"wheel >= 0.32.0",
32-
"pkginfo >= 1.4.2",
33-
"importlib_metadata ; python_version < '3.7'",
34-
"zipfile39 ; python_version < '3.9'",
32+
"importlib_metadata ; python_version < '3.10'",
33+
"zipfile38 ; python_version < '3.8' and python_version != '2.7'",
34+
"zipfile39 ; python_version == '2.7'",
3535
],
3636
entry_points={
3737
"console_scripts": [

tests/test_cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ def test_all_fields_toml_out(mocker, capsys, make_dist):
148148
requires = []
149149
required_by = []
150150
import_names = [ "that",]
151-
console_scripts = ""
151+
console_scripts = []
152152
homepage = "https://www.example.org/default"
153153
extras_available = []
154154
extras_requested = []

tests/test_gen.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def test_generated_metadata_from_dist_name(make_dist):
2323
"license": "MIT",
2424
"metadata_version": "2.1",
2525
"name": "jdtest",
26-
"platforms": ["default platform"],
26+
"platform": ["default platform"],
2727
"summary": "default text for metadata summary",
2828
"version": "0.1.2",
2929
}
@@ -44,7 +44,7 @@ def test_generated_metadata_from_dist_path(make_dist):
4444
"license": "MIT",
4545
"metadata_version": "2.1",
4646
"name": "jdtest",
47-
"platforms": ["default platform"],
47+
"platform": ["default platform"],
4848
"summary": "default text for metadata summary",
4949
"version": "0.1.2",
5050
}
@@ -100,7 +100,7 @@ def test_old_metadata_20(add_to_index):
100100
"license": "MIT",
101101
"metadata_version": "2.0",
102102
"name": "m20dist",
103-
"platforms": ["default platform"],
103+
"platform": ["default platform"],
104104
"summary": "default text for metadata summary",
105105
"version": "0.1.2",
106106
}

tests/test_lib.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -490,13 +490,6 @@ def test_license_parsing_unknown(make_dist):
490490
assert JohnnyDist("jdtest").license == ""
491491

492492

493-
def test_metadata_cant_be_extracted(make_dist, mocker):
494-
make_dist()
495-
mocker.patch("pkginfo.get_metadata", return_value=None)
496-
with pytest.raises(JohnnyError("failed to get metadata")):
497-
JohnnyDist("jdtest")
498-
499-
500493
def test_ignore_errors(make_dist):
501494
make_dist(name="distA", install_requires=["distB1>=1.0"], version="0.1")
502495
dist = JohnnyDist("distA", ignore_errors=True)
@@ -548,4 +541,4 @@ def test_entry_points(make_dist):
548541
assert ep.name == "my-script"
549542
assert ep.group == "console_scripts"
550543
assert ep.value == "mypkg.mymod:foo"
551-
assert dist.console_scripts == "my-script = mypkg.mymod:foo"
544+
assert dist.console_scripts == ["my-script = mypkg.mymod:foo"]

0 commit comments

Comments
 (0)