diff --git a/README.md b/README.md index fac6db9..c07a8e6 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ It tries to find a diff tool in the following order: - `meld` - `diff -r` -It currently uses the `pip` executable found in path to download or build. +It currently uses the `uv` or `pip` executable found in path to download or build. ## License diff --git a/src/pip_wheel_diff/__main__.py b/src/pip_wheel_diff/__main__.py index d6b9388..1245e65 100644 --- a/src/pip_wheel_diff/__main__.py +++ b/src/pip_wheel_diff/__main__.py @@ -2,55 +2,41 @@ # # SPDX-License-Identifier: MIT -import shutil import subprocess import sys import tempfile -import zipfile from pathlib import Path +from .clean import clean_unpacked_wheel +from .diff_tool import diff_tool +from .pip import has_pip, obtain_with_pip +from .uv import has_uv, obtain_with_uv -def _build_wheel(spec: str, path: Path) -> None: - with tempfile.TemporaryDirectory() as tmpdir: - cmd = ["pip", "wheel", "--use-pep517", "--no-deps", spec, "--wheel-dir", tmpdir] - subprocess.run(cmd, check=True) # noqa: S603 - wheelfile = next(Path(tmpdir).glob("*.whl")) - zipfile.ZipFile(wheelfile).extractall(path) +if has_uv(): + obtain = obtain_with_uv +elif has_pip(): + obtain = obtain_with_pip +else: + msg = "Nor uv, nor pip were found, please install one of them." + raise SystemExit(msg) -def _clean_unpacked_wheel(path: Path) -> None: - distinfo_path = next(path.glob("*.dist-info")) - # remove RECORD file, it's not useful for diffing - distinfo_path.joinpath("RECORD").unlink() - # rename dist-info to name without version for comparability - name = distinfo_path.stem.split("-")[0] - distinfo_path.rename(path / f"{name}.dist-info") - - -def _diff_tool(v1path: Path, v2path: Path): - if shutil.which("meld"): - cmd = ["meld", v1path, v2path] - else: - cmd = ["diff", "-r", v1path, v2path] - subprocess.run(cmd, check=True) # noqa: S603 - - -def main(spec1: str, spec2: str): +def main(spec1: str, spec2: str) -> int: try: with tempfile.TemporaryDirectory() as tmpdir: tmppath = Path(tmpdir) # build 1 v1path = tmppath / "v1" v1path.mkdir() - _build_wheel(spec1, v1path) - _clean_unpacked_wheel(v1path) + obtain(spec1, v1path) + clean_unpacked_wheel(v1path) # build 2 v2path = tmppath / "v2" v2path.mkdir() - _build_wheel(spec2, v2path) - _clean_unpacked_wheel(v2path) + obtain(spec2, v2path) + clean_unpacked_wheel(v2path) # diff - _diff_tool(v1path, v2path) + diff_tool(v1path, v2path) except subprocess.CalledProcessError as e: return e.returncode return 0 diff --git a/src/pip_wheel_diff/clean.py b/src/pip_wheel_diff/clean.py new file mode 100644 index 0000000..5e6a59b --- /dev/null +++ b/src/pip_wheel_diff/clean.py @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2023-present Stéphane Bidoul +# +# SPDX-License-Identifier: MIT + +from pathlib import Path + + +def clean_unpacked_wheel(path: Path) -> None: + distinfo_path = next(path.glob("*.dist-info")) + # remove metadata files that are not useful for diffing + distinfo_path.joinpath("RECORD").unlink(missing_ok=True) + distinfo_path.joinpath("direct_url.json").unlink(missing_ok=True) + # rename dist-info to name without version for comparability + name = distinfo_path.stem.split("-")[0] + distinfo_path.rename(path / f"{name}.dist-info") diff --git a/src/pip_wheel_diff/diff_tool.py b/src/pip_wheel_diff/diff_tool.py new file mode 100644 index 0000000..4bafdc7 --- /dev/null +++ b/src/pip_wheel_diff/diff_tool.py @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2023-present Stéphane Bidoul +# +# SPDX-License-Identifier: MIT + +import shutil +import subprocess +from pathlib import Path + + +def diff_tool(v1path: Path, v2path: Path): + if shutil.which("meld"): + cmd = ["meld", v1path, v2path] + else: + cmd = ["diff", "-r", v1path, v2path] + subprocess.run(cmd, check=True) # noqa: S603 diff --git a/src/pip_wheel_diff/pip.py b/src/pip_wheel_diff/pip.py new file mode 100644 index 0000000..d503ace --- /dev/null +++ b/src/pip_wheel_diff/pip.py @@ -0,0 +1,21 @@ +# SPDX-FileCopyrightText: 2023-present Stéphane Bidoul +# +# SPDX-License-Identifier: MIT + +import shutil +import subprocess +import tempfile +import zipfile +from pathlib import Path + + +def has_pip() -> bool: + return bool(shutil.which("pip")) + + +def obtain_with_pip(spec: str, path: Path) -> None: + with tempfile.TemporaryDirectory() as tmpdir: + cmd = ["pip", "wheel", "--use-pep517", "--no-deps", spec, "--wheel-dir", tmpdir] + subprocess.run(cmd, check=True) # noqa: S603 + wheelfile = next(Path(tmpdir).glob("*.whl")) + zipfile.ZipFile(wheelfile).extractall(path) diff --git a/src/pip_wheel_diff/uv.py b/src/pip_wheel_diff/uv.py new file mode 100644 index 0000000..5db18be --- /dev/null +++ b/src/pip_wheel_diff/uv.py @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2024-present Stéphane Bidoul +# +# SPDX-License-Identifier: MIT + +import shutil +import subprocess +import tempfile +from pathlib import Path + +from .clean import clean_unpacked_wheel +from .diff_tool import diff_tool + + +def has_uv() -> bool: + return bool(shutil.which("uv")) + + +def obtain_with_uv(spec: str, path: Path) -> None: + cmd = ["uv", "pip", "install", "--no-deps", "--target", path, spec] + subprocess.run(cmd, check=True) # noqa: S603