From af3b24b7ffa418c1551688b852d5b162f693b639 Mon Sep 17 00:00:00 2001 From: Wim Glenn Date: Thu, 4 May 2023 16:53:07 -0500 Subject: [PATCH 1/5] add new entry_points attribute to JohnnyDist --- johnnydep/compat.py | 4 ++-- johnnydep/lib.py | 31 +++++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/johnnydep/compat.py b/johnnydep/compat.py index 9181b06..4074f64 100644 --- a/johnnydep/compat.py +++ b/johnnydep/compat.py @@ -62,6 +62,6 @@ def urlretrieve(url, filename, data=None, auth=None): try: - from importlib.metadata import distribution, PackageNotFoundError + from importlib.metadata import distribution, PackageNotFoundError, PathDistribution except ImportError: - from importlib_metadata import distribution, PackageNotFoundError + from importlib_metadata import distribution, PackageNotFoundError, PathDistribution diff --git a/johnnydep/lib.py b/johnnydep/lib.py index 57c8c92..ba2d69d 100644 --- a/johnnydep/lib.py +++ b/johnnydep/lib.py @@ -5,10 +5,10 @@ import os import re import subprocess +import zipfile from collections import defaultdict from shutil import rmtree from tempfile import mkdtemp -from zipfile import ZipFile import anytree import pkginfo @@ -29,6 +29,7 @@ from johnnydep.compat import distribution from johnnydep.compat import oyaml from johnnydep.compat import PackageNotFoundError +from johnnydep.compat import PathDistribution from johnnydep.dot import jd2dot from johnnydep.util import CircularMarker @@ -70,6 +71,7 @@ def __init__(self, req_string, parent=None, index_url=None, env=None, extra_inde self.req = requirements.Requirement(self.name + sep + extras + self.specifier) self.import_names = _discover_import_names(fname) self.metadata = _extract_metadata(fname) + self.entry_points = _discover_entry_points(fname) self._from_fname = os.path.abspath(fname) else: self._from_fname = None @@ -78,7 +80,7 @@ def __init__(self, req_string, parent=None, index_url=None, env=None, extra_inde self.specifier = str(self.req.specifier) log.debug("fetching best wheel") try: - self.import_names, self.metadata = _get_info( + self.import_names, self.metadata, self.entry_points = _get_info( dist_name=req_string, index_url=index_url, env=env, @@ -89,6 +91,7 @@ def __init__(self, req_string, parent=None, index_url=None, env=None, extra_inde raise self.import_names = None self.metadata = {} + self.entry_points = None self.error = err self.extras_requested = sorted(self.req.extras) @@ -420,9 +423,9 @@ def flatten_deps(johnnydist): def _discover_import_names(whl_file): log = logger.bind(whl_file=whl_file) - logger.debug("finding import names") - zipfile = ZipFile(file=whl_file) - namelist = zipfile.namelist() + log.debug("finding import names") + zf = zipfile.ZipFile(file=whl_file) + namelist = zf.namelist() try: [top_level_fname] = [x for x in namelist if x.endswith("top_level.txt")] except ValueError: @@ -447,13 +450,24 @@ def _discover_import_names(whl_file): # found a top level module public_names.append(name) else: - all_names = zipfile.read(top_level_fname).decode("utf-8").strip().splitlines() + all_names = zf.read(top_level_fname).decode("utf-8").strip().splitlines() public_names = [n for n in all_names if not n.startswith("_")] return public_names +def _discover_entry_points(whl_file): + log = logger.bind(whl_file=whl_file) + log.debug("finding entry points") + parts = os.path.basename(whl_file).split("-") + metadata_path = "-".join(parts[:2]) + ".dist-info/" + zf = zipfile.Path(whl_file, metadata_path) + path_dist = PathDistribution(zf) + return path_dist.entry_points + + def _extract_metadata(whl_file): - logger.debug("searching metadata", whl_file=whl_file) + log = logger.bind(whl_file=whl_file) + log.debug("searching metadata", whl_file=whl_file) info = pkginfo.get_metadata(whl_file) if info is None: raise JohnnyError("failed to get metadata") @@ -486,10 +500,11 @@ def _get_info(dist_name, index_url=None, env=None, extra_index_url=None): # downloaded file can be cleaned up immediately import_names = _discover_import_names(dist_path) metadata = _extract_metadata(dist_path) + entry_points = _discover_entry_points(dist_path) finally: log.debug("removing scratch", tmpdir=tmpdir) rmtree(tmpdir, ignore_errors=True) - return import_names, metadata + return import_names, metadata, entry_points # TODO: multi-line progress bar? From 80ae243a49acf0f5448a7d3cafdc40e085f86ddd Mon Sep 17 00:00:00 2001 From: Wim Glenn Date: Thu, 4 May 2023 17:06:19 -0500 Subject: [PATCH 2/5] entry_points test, that will fail until whl is updated to support this --- tests/test_lib.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_lib.py b/tests/test_lib.py index 4c102eb..0714386 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -537,3 +537,14 @@ def test_local_whl_json(make_dist): assert link.startswith("file://") assert link.endswith("loc-0.1.2-py2.py3-none-any.whl") assert result["versions_available"] == ["0.1.1", "0.1.2", "0.1.3"] + + +def test_entry_points(make_dist): + # https://packaging.python.org/en/latest/specifications/entry-points/ + entry_points = {"console_scripts": ["my-script=mypkg.mymod:foo"]} + make_dist(name="example", entry_points=entry_points) + dist = JohnnyDist("example") + [ep] = dist.entry_points + assert ep.name == "my-script" + assert ep.group == "console_scripts" + assert ep.value == "mypkg.mymod:foo" From 343810757a79161c5e3a793b1f30ead452ca3eb3 Mon Sep 17 00:00:00 2001 From: Wim Glenn Date: Thu, 4 May 2023 17:18:53 -0500 Subject: [PATCH 3/5] use zipfile39 backport for older Python versions --- johnnydep/compat.py | 6 ++++++ johnnydep/lib.py | 9 +++++---- setup.py | 1 + 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/johnnydep/compat.py b/johnnydep/compat.py index 4074f64..acb03b3 100644 --- a/johnnydep/compat.py +++ b/johnnydep/compat.py @@ -65,3 +65,9 @@ def urlretrieve(url, filename, data=None, auth=None): from importlib.metadata import distribution, PackageNotFoundError, PathDistribution except ImportError: from importlib_metadata import distribution, PackageNotFoundError, PathDistribution + + +try: + from zipfile import Path as zipfile_path +except ImportError: + from zipfile39 import Path as zipfile_path diff --git a/johnnydep/lib.py b/johnnydep/lib.py index ba2d69d..ed4ad65 100644 --- a/johnnydep/lib.py +++ b/johnnydep/lib.py @@ -5,10 +5,10 @@ import os import re import subprocess -import zipfile from collections import defaultdict from shutil import rmtree from tempfile import mkdtemp +from zipfile import ZipFile import anytree import pkginfo @@ -30,6 +30,7 @@ from johnnydep.compat import oyaml from johnnydep.compat import PackageNotFoundError from johnnydep.compat import PathDistribution +from johnnydep.compat import zipfile_path from johnnydep.dot import jd2dot from johnnydep.util import CircularMarker @@ -424,7 +425,7 @@ def flatten_deps(johnnydist): def _discover_import_names(whl_file): log = logger.bind(whl_file=whl_file) log.debug("finding import names") - zf = zipfile.ZipFile(file=whl_file) + zf = ZipFile(file=whl_file) namelist = zf.namelist() try: [top_level_fname] = [x for x in namelist if x.endswith("top_level.txt")] @@ -460,8 +461,8 @@ def _discover_entry_points(whl_file): log.debug("finding entry points") parts = os.path.basename(whl_file).split("-") metadata_path = "-".join(parts[:2]) + ".dist-info/" - zf = zipfile.Path(whl_file, metadata_path) - path_dist = PathDistribution(zf) + zf_path = zipfile_path(whl_file, metadata_path) + path_dist = PathDistribution(zf_path) return path_dist.entry_points diff --git a/setup.py b/setup.py index 3131e76..bcb6e4f 100644 --- a/setup.py +++ b/setup.py @@ -31,6 +31,7 @@ "wheel >= 0.32.0", "pkginfo >= 1.4.2", "importlib_metadata ; python_version < '3.7'", + "zipfile39 ; python_version < '3.9'", ], entry_points={ "console_scripts": [ From c6109a561dd69e2d642a3cf8f193070fde4b8cc5 Mon Sep 17 00:00:00 2001 From: wim glenn Date: Fri, 5 May 2023 21:53:17 -0500 Subject: [PATCH 4/5] update whl dev dep --- requirements-dev.txt | 2 +- tests/test_lib.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index e94af38..ad3f68c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,5 +6,5 @@ pytest-raisin pytest-socket coverage wheel -whl >= 0.0.3 +whl >= 0.0.4 wimpy == 0.3 # just something we can pin diff --git a/tests/test_lib.py b/tests/test_lib.py index 0714386..c0ef079 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -541,7 +541,7 @@ def test_local_whl_json(make_dist): def test_entry_points(make_dist): # https://packaging.python.org/en/latest/specifications/entry-points/ - entry_points = {"console_scripts": ["my-script=mypkg.mymod:foo"]} + entry_points = {"console_scripts": ["my-script = mypkg.mymod:foo"]} make_dist(name="example", entry_points=entry_points) dist = JohnnyDist("example") [ep] = dist.entry_points From be2e3140805aa0f545df145f36f169fdade6710f Mon Sep 17 00:00:00 2001 From: wim glenn Date: Fri, 5 May 2023 22:39:06 -0500 Subject: [PATCH 5/5] add console_scripts property to CLI --- johnnydep/__init__.py | 2 +- johnnydep/cli.py | 1 + johnnydep/lib.py | 8 +++++++- tests/test_cli.py | 1 + tests/test_lib.py | 1 + 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/johnnydep/__init__.py b/johnnydep/__init__.py index c3d1e5d..f170fa6 100644 --- a/johnnydep/__init__.py +++ b/johnnydep/__init__.py @@ -1,5 +1,5 @@ """Display dependency tree of Python distribution""" -__version__ = "1.18.0" +__version__ = "1.19.0" from johnnydep.lib import * diff --git a/johnnydep/cli.py b/johnnydep/cli.py index 0a393d6..f0b0661 100644 --- a/johnnydep/cli.py +++ b/johnnydep/cli.py @@ -22,6 +22,7 @@ ("requires", "Immediate dependencies"), ("required_by", "Parent(s) in the tree"), ("import_names", "Python imports provided (top-level names only)"), + ("console_scripts", "Entry points in the console_scripts group"), ("homepage", "Project URL"), ("extras_available", "Optional extensions available for the distribution"), ("extras_requested", "Optional extensions parsed from requirement specifier"), diff --git a/johnnydep/lib.py b/johnnydep/lib.py index ed4ad65..9c199e3 100644 --- a/johnnydep/lib.py +++ b/johnnydep/lib.py @@ -253,6 +253,11 @@ def extras_available(self): def project_name(self): return self.metadata.get("name", self.name) + @property + def console_scripts(self): + eps = [ep for ep in self.entry_points or [] if ep.group == "console_scripts"] + return ", ".join(["{} = {}".format(ep.name, ep.value) for ep in eps]) + @property def pinned(self): if self.extras_requested: @@ -453,7 +458,8 @@ def _discover_import_names(whl_file): else: all_names = zf.read(top_level_fname).decode("utf-8").strip().splitlines() public_names = [n for n in all_names if not n.startswith("_")] - return public_names + result = [n.replace("/", ".") for n in public_names] + return result def _discover_entry_points(whl_file): diff --git a/tests/test_cli.py b/tests/test_cli.py index 4320b29..1d75eda 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -148,6 +148,7 @@ def test_all_fields_toml_out(mocker, capsys, make_dist): requires = [] required_by = [] import_names = [ "that",] + console_scripts = "" homepage = "https://www.example.org/default" extras_available = [] extras_requested = [] diff --git a/tests/test_lib.py b/tests/test_lib.py index c0ef079..98997bd 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -548,3 +548,4 @@ def test_entry_points(make_dist): assert ep.name == "my-script" assert ep.group == "console_scripts" assert ep.value == "mypkg.mymod:foo" + assert dist.console_scripts == "my-script = mypkg.mymod:foo"