diff --git a/conda_forge_tick/update_recipe/v2/__init__.py b/conda_forge_tick/update_recipe/v2/__init__.py index e69de29bb2..f097d437c5 100644 --- a/conda_forge_tick/update_recipe/v2/__init__.py +++ b/conda_forge_tick/update_recipe/v2/__init__.py @@ -0,0 +1,4 @@ +from .build_number import update_build_number +from .version import update_version + +__all__ = ["update_build_number", "update_version"] diff --git a/conda_forge_tick/update_recipe/v2/build_number.py b/conda_forge_tick/update_recipe/v2/build_number.py index 8776eca29c..11f39a7b66 100644 --- a/conda_forge_tick/update_recipe/v2/build_number.py +++ b/conda_forge_tick/update_recipe/v2/build_number.py @@ -2,7 +2,9 @@ import logging from typing import TYPE_CHECKING, Any, Literal -from conda_forge_tick.update_recipe.v2.yaml import _load_yaml, _dump_yaml_to_str + +from conda_forge_tick.update_recipe.v2.yaml import _dump_yaml_to_str, _load_yaml + if TYPE_CHECKING: from pathlib import Path @@ -11,7 +13,9 @@ HashType = Literal["md5", "sha256"] -def _update_build_number_in_context(recipe: dict[str, Any], new_build_number: int) -> bool: +def _update_build_number_in_context( + recipe: dict[str, Any], new_build_number: int +) -> bool: for key in recipe.get("context", {}): if key.startswith("build_") or key == "build": recipe["context"][key] = new_build_number @@ -19,7 +23,9 @@ def _update_build_number_in_context(recipe: dict[str, Any], new_build_number: in return False -def _update_build_number_in_recipe(recipe: dict[str, Any], new_build_number: int) -> bool: +def _update_build_number_in_recipe( + recipe: dict[str, Any], new_build_number: int +) -> bool: is_modified = False if "build" in recipe and "number" in recipe["build"]: recipe["build"]["number"] = new_build_number diff --git a/conda_forge_tick/update_recipe/v2/context.py b/conda_forge_tick/update_recipe/v2/context.py index cd8bc1e2b9..cb86d80662 100644 --- a/conda_forge_tick/update_recipe/v2/context.py +++ b/conda_forge_tick/update_recipe/v2/context.py @@ -1,6 +1,9 @@ import jinja2 -def load_recipe_context(context: dict[str, str], jinja_env: jinja2.Environment) -> dict[str, str]: + +def load_recipe_context( + context: dict[str, str], jinja_env: jinja2.Environment +) -> dict[str, str]: """ Load all string values from the context dictionary as Jinja2 templates. Use linux-64 as default target_platform, build_platform, and mpi. diff --git a/conda_forge_tick/update_recipe/v2/jinja/__init__.py b/conda_forge_tick/update_recipe/v2/jinja/__init__.py index e69de29bb2..7d3fad5a0a 100644 --- a/conda_forge_tick/update_recipe/v2/jinja/__init__.py +++ b/conda_forge_tick/update_recipe/v2/jinja/__init__.py @@ -0,0 +1,3 @@ +from .jinja import jinja_env + +__all__ = ["jinja_env"] diff --git a/conda_forge_tick/update_recipe/v2/jinja/filters.py b/conda_forge_tick/update_recipe/v2/jinja/filters.py index dcf3af12f3..2580adf4e5 100644 --- a/conda_forge_tick/update_recipe/v2/jinja/filters.py +++ b/conda_forge_tick/update_recipe/v2/jinja/filters.py @@ -1,6 +1,6 @@ from __future__ import annotations -from conda_forge_tick.jinja.utils import _MissingUndefined +from conda_forge_tick.update_recipe.v2.jinja.utils import _MissingUndefined def _version_to_build_string(some_string: str | _MissingUndefined) -> str: diff --git a/conda_forge_tick/update_recipe/v2/jinja/jinja.py b/conda_forge_tick/update_recipe/v2/jinja/jinja.py index 7034acc8a0..b25e1189d1 100644 --- a/conda_forge_tick/update_recipe/v2/jinja/jinja.py +++ b/conda_forge_tick/update_recipe/v2/jinja/jinja.py @@ -1,12 +1,16 @@ from __future__ import annotations -from typing import Any, TypedDict +from typing import TypedDict import jinja2 -import yaml +from jinja2.sandbox import SandboxedEnvironment -from conda_forge_tick.jinja.filters import _bool, _split, _version_to_build_string -from conda_forge_tick.jinja.objects import ( +from conda_forge_tick.update_recipe.v2.jinja.filters import ( + _bool, + _split, + _version_to_build_string, +) +from conda_forge_tick.update_recipe.v2.jinja.objects import ( _stub_compatible_pin, _stub_is_linux, _stub_is_unix, @@ -15,20 +19,21 @@ _stub_subpackage_pin, _StubEnv, ) -from conda_forge_tick.jinja.utils import _MissingUndefined -from conda_forge_tick.loader import load_yaml +from conda_forge_tick.update_recipe.v2.jinja.utils import _MissingUndefined + +# from conda_forge_tick.update_recipe.v2.loader import load_yaml class RecipeWithContext(TypedDict, total=False): context: dict[str, str] -def jinja_env() -> jinja2.Environment: +def jinja_env() -> SandboxedEnvironment: """ Create a `rattler-build` specific Jinja2 environment with modified syntax. Target platform, build platform, and mpi are set to linux-64 by default. """ - env = jinja2.sandbox.SandboxedEnvironment( + env = SandboxedEnvironment( variable_start_string="${{", variable_end_string="}}", trim_blocks=True, @@ -71,7 +76,9 @@ def jinja_env() -> jinja2.Environment: return env -def load_recipe_context(context: dict[str, str], jinja_env: jinja2.Environment) -> dict[str, str]: +def load_recipe_context( + context: dict[str, str], jinja_env: jinja2.Environment +) -> dict[str, str]: """ Load all string values from the context dictionary as Jinja2 templates. Use linux-64 as default target_platform, build_platform, and mpi. @@ -87,30 +94,30 @@ def load_recipe_context(context: dict[str, str], jinja_env: jinja2.Environment) return context -def render_recipe_with_context(recipe_content: RecipeWithContext) -> dict[str, Any]: - """ - Render the recipe using known values from context section. - Unknown values are not evaluated and are kept as it is. - Target platform, build platform, and mpi are set to linux-64 by default. - - Examples: - --- - ```python - >>> from pathlib import Path - >>> from conda_forge_tick.loader import load_yaml - >>> recipe_content = load_yaml((Path().resolve() / "tests" / "data" / "eval_recipe_using_context.yaml").read_text()) - >>> evaluated_context = render_recipe_with_context(recipe_content) - >>> assert "my_value-${{ not_present_value }}" == evaluated_context["build"]["string"] - >>> - ``` - """ - env = jinja_env() - context = recipe_content.get("context", {}) - # render out the context section and retrieve dictionary - context_variables = load_recipe_context(context, env) - - # render the rest of the document with the values from the context - # and keep undefined expressions _as is_. - template = env.from_string(yaml.dump(recipe_content)) - rendered_content = template.render(context_variables) - return load_yaml(rendered_content) +# def render_recipe_with_context(recipe_content: RecipeWithContext) -> dict[str, Any]: +# """ +# Render the recipe using known values from context section. +# Unknown values are not evaluated and are kept as it is. +# Target platform, build platform, and mpi are set to linux-64 by default. + +# Examples: +# --- +# ```python +# >>> from pathlib import Path +# >>> from conda_forge_tick.loader import load_yaml +# >>> recipe_content = load_yaml((Path().resolve() / "tests" / "data" / "eval_recipe_using_context.yaml").read_text()) +# >>> evaluated_context = render_recipe_with_context(recipe_content) +# >>> assert "my_value-${{ not_present_value }}" == evaluated_context["build"]["string"] +# >>> +# ``` +# """ +# env = jinja_env() +# context = recipe_content.get("context", {}) +# # render out the context section and retrieve dictionary +# context_variables = load_recipe_context(context, env) + +# # render the rest of the document with the values from the context +# # and keep undefined expressions _as is_. +# template = env.from_string(yaml.dump(recipe_content)) +# rendered_content = template.render(context_variables) +# return load_yaml(rendered_content) diff --git a/conda_forge_tick/update_recipe/v2/source.py b/conda_forge_tick/update_recipe/v2/source.py index 8339bfe897..7e6b5d66ac 100644 --- a/conda_forge_tick/update_recipe/v2/source.py +++ b/conda_forge_tick/update_recipe/v2/source.py @@ -1,9 +1,14 @@ import typing -from typing import Union, List, TypedDict, Any, Iterator, NotRequired, Mapping -from conda_forge_tick.update_recipe.v2.conditional_list import visit_conditional_list, ConditionalList +from typing import Any, Iterator, List, Mapping, NotRequired, TypedDict, Union + +from conda_forge_tick.update_recipe.v2.conditional_list import ( + ConditionalList, + visit_conditional_list, +) OptionalUrlList = Union[str, List[str], None] + class Source(TypedDict): url: NotRequired[str | list[str]] sha256: NotRequired[str] @@ -29,8 +34,7 @@ def get_all_sources(recipe: Mapping[Any, Any]) -> Iterator[Source]: # Try getting all url top-level sources if sources is not None: source_list = visit_conditional_list(sources, None) - for source in source_list: - yield source + yield from source_list outputs = recipe.get("outputs", None) if outputs is None: @@ -43,5 +47,4 @@ def get_all_sources(recipe: Mapping[Any, Any]) -> Iterator[Source]: if sources is None: continue source_list = visit_conditional_list(sources, None) - for source in source_list: - yield source + yield from source_list diff --git a/conda_forge_tick/update_recipe/v2/version.py b/conda_forge_tick/update_recipe/v2/version.py index 8eccdacd54..6d89187a3d 100644 --- a/conda_forge_tick/update_recipe/v2/version.py +++ b/conda_forge_tick/update_recipe/v2/version.py @@ -6,12 +6,12 @@ import re from typing import TYPE_CHECKING, Literal +import requests + from conda_forge_tick.update_recipe.v2.context import load_recipe_context from conda_forge_tick.update_recipe.v2.jinja import jinja_env from conda_forge_tick.update_recipe.v2.source import Source, get_all_sources -from conda_forge_tick.update_recipe.v2.yaml import _load_yaml, _dump_yaml_to_str - -import requests +from conda_forge_tick.update_recipe.v2.yaml import _dump_yaml_to_str, _load_yaml if TYPE_CHECKING: from pathlib import Path @@ -20,6 +20,7 @@ HashType = Literal["md5", "sha256"] + class CouldNotUpdateVersionError(Exception): NO_CONTEXT = "Could not find context in recipe" NO_VERSION = "Could not find version in recipe context" diff --git a/conda_forge_tick/update_recipe/v2/yaml.py b/conda_forge_tick/update_recipe/v2/yaml.py index 4efd901a75..3a748eec63 100644 --- a/conda_forge_tick/update_recipe/v2/yaml.py +++ b/conda_forge_tick/update_recipe/v2/yaml.py @@ -1,22 +1,22 @@ import io -from ruamel.yaml import YAML -from typing import TYPE_CHECKING +from pathlib import Path -if TYPE_CHECKING: - from pathlib import Path +from ruamel.yaml import YAML yaml = YAML() yaml.preserve_quotes = True yaml.width = 4096 yaml.indent(mapping=2, sequence=4, offset=2) + def _load_yaml(file: Path) -> dict: """Load a YAML file.""" with file.open("r") as f: return yaml.load(f) + def _dump_yaml_to_str(data: dict) -> str: """Dump a dictionary to a YAML string.""" with io.StringIO() as f: yaml.dump(data, f) - return f.getvalue() \ No newline at end of file + return f.getvalue() diff --git a/tests/test_recipe_editing_v2.py b/tests/test_recipe_editing_v2.py index 11a02ec0dd..c971e1b292 100644 --- a/tests/test_recipe_editing_v2.py +++ b/tests/test_recipe_editing_v2.py @@ -1,13 +1,15 @@ -import pytest from pathlib import Path -from conda_forge_tick.recipe_editing_v2 import update_build_number, update_version +import pytest + +from conda_forge_tick.update_recipe.v2 import update_build_number, update_version @pytest.fixture def data_dir() -> Path: return Path(__file__).parent / "recipe_v2" + def test_build_number_mod(data_dir: Path) -> None: tests = data_dir / "build_number" result = update_build_number(tests / "test_1/recipe.yaml", 0)