From 2a385dbc8059edadceeb84aa887b31facc6ea272 Mon Sep 17 00:00:00 2001 From: Hood Chatham Date: Sun, 26 Jan 2025 09:38:56 +0000 Subject: [PATCH] Add pinned key to opt recipe out of autoupdates (#89) A feature I've wanted a long time. --- pyodide_build/cli/skeleton.py | 10 +++++-- pyodide_build/recipe/skeleton.py | 7 ++++- pyodide_build/recipe/spec.py | 1 + pyodide_build/tests/recipe/test_skeleton.py | 29 +++++++++++++++++---- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/pyodide_build/cli/skeleton.py b/pyodide_build/cli/skeleton.py index 2c053a9b..3789110b 100644 --- a/pyodide_build/cli/skeleton.py +++ b/pyodide_build/cli/skeleton.py @@ -33,6 +33,11 @@ def new_recipe_pypi( "--update-patched", help="Force update the package even if it contains patches.", ), + update_pinned: bool = typer.Option( + False, + "--update-pinned", + help="Force update the package even if is pinned.", + ), version: str = typer.Option( None, help="The version of the package, if not specified, latest version will be used.", @@ -71,14 +76,15 @@ def new_recipe_pypi( recipe_dir_ = root / "packages" - if update or update_patched: + if update or update_patched or update_pinned: try: skeleton.update_package( recipe_dir_, name, - version, + version=version, source_fmt=source_format, # type: ignore[arg-type] update_patched=update_patched, + update_pinned=update_pinned, ) except skeleton.MkpkgFailedException as e: logger.error("%s update failed: %s", name, e) diff --git a/pyodide_build/recipe/skeleton.py b/pyodide_build/recipe/skeleton.py index 05c42e61..922a6008 100755 --- a/pyodide_build/recipe/skeleton.py +++ b/pyodide_build/recipe/skeleton.py @@ -216,8 +216,10 @@ def make_package( def update_package( root: Path, package: str, + *, version: str | None = None, - update_patched: bool = True, + update_patched: bool = False, + update_pinned: bool = False, source_fmt: Literal["wheel", "sdist"] | None = None, ) -> None: yaml = YAML() @@ -237,6 +239,9 @@ def update_package( if "url" not in yaml_content["source"]: raise MkpkgSkipped(f"{package} is a local package!") + if (not update_pinned) and yaml_content["package"].get("pinned", False): + raise MkpkgSkipped(f"{package} is pinned!") + if yaml_content["source"]["url"].endswith("whl"): old_fmt = "wheel" else: diff --git a/pyodide_build/recipe/spec.py b/pyodide_build/recipe/spec.py index 14cbca03..ba2f5ef9 100644 --- a/pyodide_build/recipe/spec.py +++ b/pyodide_build/recipe/spec.py @@ -13,6 +13,7 @@ class _PackageSpec(BaseModel): top_level: list[str] = Field([], alias="top-level") tag: list[str] = Field([]) disabled: bool = Field(False, alias="_disabled") + pinned: bool = Field(False, alias="pinned") model_config = ConfigDict(extra="forbid") diff --git a/pyodide_build/tests/recipe/test_skeleton.py b/pyodide_build/tests/recipe/test_skeleton.py index 57af4ec9..0e96e1e6 100644 --- a/pyodide_build/tests/recipe/test_skeleton.py +++ b/pyodide_build/tests/recipe/test_skeleton.py @@ -4,7 +4,7 @@ import pytest from packaging import version -import pyodide_build.recipe.skeleton +from pyodide_build.recipe import skeleton from pyodide_build.recipe.spec import MetaConfig # Following tests make real network calls to the PyPI JSON API. @@ -16,7 +16,7 @@ def test_mkpkg(tmpdir, capsys, source_fmt): base_dir = Path(str(tmpdir)) - pyodide_build.recipe.skeleton.make_package(base_dir, "idna", None, source_fmt) + skeleton.make_package(base_dir, "idna", None, source_fmt) assert os.listdir(base_dir) == ["idna"] meta_path = base_dir / "idna" / "meta.yaml" assert meta_path.exists() @@ -57,9 +57,7 @@ def test_mkpkg_update(tmpdir, old_dist_type, new_dist_type): source_fmt = new_dist_type if new_dist_type == "same": source_fmt = None - pyodide_build.recipe.skeleton.update_package( - base_dir, "idna", None, False, source_fmt - ) + skeleton.update_package(base_dir, "idna", source_fmt=source_fmt) db = MetaConfig.from_yaml(meta_path) assert version.parse(db.package.version) > version.parse(db_init.package.version) @@ -70,3 +68,24 @@ def test_mkpkg_update(tmpdir, old_dist_type, new_dist_type): assert db.source.url.endswith(".tar.gz") else: assert db.source.url.endswith(old_ext) + + +def test_mkpkg_update_pinned(tmpdir): + base_dir = Path(str(tmpdir)) + + db_init = MetaConfig( + package={"name": "idna", "version": "2.0", "pinned": True}, + source={ + "sha256": "b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", + "url": "https:///idna-2.0.whl", + }, + test={"imports": ["idna"]}, + ) + + package_dir = base_dir / "idna" + package_dir.mkdir(parents=True) + meta_path = package_dir / "meta.yaml" + db_init.to_yaml(meta_path) + with pytest.raises(skeleton.MkpkgSkipped, match="pinned"): + skeleton.update_package(base_dir, "idna") + skeleton.update_package(base_dir, "idna", update_pinned=True)