From 65a48f0ecfdff98a79a7db7a58028a3fb35bd2d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez-Mondrag=C3=B3n?= Date: Wed, 31 Jan 2024 01:06:09 -0600 Subject: [PATCH 1/4] feat: Add `is_editable` wrapper function --- docs/index.md | 4 ++++ src/pep610/__init__.py | 22 +++++++++++++++++- tests/test_parse.py | 52 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/docs/index.md b/docs/index.md index dccc63a..a1e1e29 100644 --- a/docs/index.md +++ b/docs/index.md @@ -83,3 +83,7 @@ else: ```{eval-rst} .. autofunction:: pep610.read_from_distribution ``` + +```{eval-rst} +.. autofunction:: pep610.is_editable +``` diff --git a/src/pep610/__init__.py b/src/pep610/__init__.py index 19bc8ab..4fac784 100644 --- a/src/pep610/__init__.py +++ b/src/pep610/__init__.py @@ -8,7 +8,7 @@ import typing as t from dataclasses import dataclass from functools import singledispatch -from importlib.metadata import version +from importlib.metadata import distribution, version if sys.version_info < (3, 9): import importlib_resources @@ -338,6 +338,26 @@ def read_from_distribution(dist: Distribution) -> VCSData | ArchiveData | DirDat return None +def is_editable(distribution_name: str) -> bool: + """Is the distribution editable? + + Args: + distribution_name: The distribution name. + + Returns: + Whether the distribution is editable. + """ + dist = distribution(distribution_name) + if ( + (data := read_from_distribution(dist)) + and isinstance(data, DirData) + and data.dir_info.is_editable() + ): + return True + + return False + + def write_to_distribution(dist: PathDistribution, data: dict) -> int: """Write the direct URL data to a distribution. diff --git a/tests/test_parse.py b/tests/test_parse.py index 292abef..2b19f6a 100644 --- a/tests/test_parse.py +++ b/tests/test_parse.py @@ -15,6 +15,7 @@ HashData, VCSData, VCSInfo, + is_editable, read_from_distribution, to_dict, write_to_distribution, @@ -321,3 +322,54 @@ def test_no_file(tmp_path: Path): """Test that a missing file is read back as None.""" dist = Distribution.at(tmp_path) assert read_from_distribution(dist) is None + + +@pytest.mark.parametrize( + ("data", "expected"), + [ + pytest.param( + { + "url": "file:///home/user/project", + "dir_info": {"editable": True}, + }, + True, + id="editable", + ), + pytest.param( + { + "url": "file:///home/user/project", + "dir_info": {"editable": False}, + }, + False, + id="not_editable", + ), + pytest.param( + { + "url": "file:///home/user/project", + "dir_info": {}, + }, + False, + id="no_editable_info", + ), + pytest.param( + { + "url": "https://github.com/pypa/pip.git", + "vcs_info": { + "vcs": "git", + "requested_revision": "1.3.1", + "resolved_revision_type": "tag", + "commit_id": "7921be1537eac1e97bc40179a57f0349c2aee67d", + }, + }, + False, + id="vcs_git", + ), + ], +) +def test_is_editable(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, data: dict, expected: bool): # noqa: FBT001 + """Test the is_editable function.""" + dist = Distribution.at(tmp_path) + write_to_distribution(dist, data) + + monkeypatch.setattr("pep610.distribution", lambda _: dist) + assert is_editable("my_package") is expected From 4a1979a76537182e10a7df7e37036639cdda583f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez-Mondrag=C3=B3n?= Date: Wed, 31 Jan 2024 01:10:35 -0600 Subject: [PATCH 2/4] Add docstring --- src/pep610/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/pep610/__init__.py b/src/pep610/__init__.py index 4fac784..6d5b239 100644 --- a/src/pep610/__init__.py +++ b/src/pep610/__init__.py @@ -331,6 +331,11 @@ def read_from_distribution(dist: Distribution) -> VCSData | ArchiveData | DirDat Returns: The parsed PEP 610 file. + + >>> import importlib.metadata + >>> dist = importlib.metadata.distribution("pep610") + >>> read_from_distribution(dist) # doctest: +SKIP + DirData(url='file:///home/user/pep610', dir_info=DirInfo(editable=False)) """ if contents := dist.read_text("direct_url.json"): return _parse(contents) @@ -346,6 +351,9 @@ def is_editable(distribution_name: str) -> bool: Returns: Whether the distribution is editable. + + >>> is_editable("pep610") # doctest: +SKIP + False """ dist = distribution(distribution_name) if ( From faffc1cb043912240011019653b3cd14193b4286 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez-Mondrag=C3=B3n?= Date: Wed, 31 Jan 2024 01:16:52 -0600 Subject: [PATCH 3/4] Update docstring --- src/pep610/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pep610/__init__.py b/src/pep610/__init__.py index 6d5b239..7b61724 100644 --- a/src/pep610/__init__.py +++ b/src/pep610/__init__.py @@ -344,7 +344,7 @@ def read_from_distribution(dist: Distribution) -> VCSData | ArchiveData | DirDat def is_editable(distribution_name: str) -> bool: - """Is the distribution editable? + """Wrapper around :func:`read_from_distribution` to check if a distribution is editable. Args: distribution_name: The distribution name. From 788ce853ce769ecdc83f16908a8bfa1c475b247c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Ram=C3=ADrez-Mondrag=C3=B3n?= Date: Wed, 31 Jan 2024 01:22:12 -0600 Subject: [PATCH 4/4] Simplify code --- .readthedocs.yaml | 2 +- src/pep610/__init__.py | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 1bd401d..fd319be 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -3,7 +3,7 @@ version: 2 build: os: ubuntu-22.04 tools: - python: "3.11" + python: "3.12" jobs: post_checkout: - git fetch --unshallow || true diff --git a/src/pep610/__init__.py b/src/pep610/__init__.py index 7b61724..5fab371 100644 --- a/src/pep610/__init__.py +++ b/src/pep610/__init__.py @@ -352,18 +352,15 @@ def is_editable(distribution_name: str) -> bool: Returns: Whether the distribution is editable. + Raises: + importlib_metadata.PackageNotFoundError: If the distribution is not found. + >>> is_editable("pep610") # doctest: +SKIP False - """ + """ # noqa: DAR402, RUF100 dist = distribution(distribution_name) - if ( - (data := read_from_distribution(dist)) - and isinstance(data, DirData) - and data.dir_info.is_editable() - ): - return True - - return False + data = read_from_distribution(dist) + return isinstance(data, DirData) and data.dir_info.is_editable() def write_to_distribution(dist: PathDistribution, data: dict) -> int: