Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: metadata will render recipe with context #41

Merged
merged 4 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 56 additions & 8 deletions src/rattler_build_conda_compat/render.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
# mypy: ignore-errors

from __future__ import annotations

from collections import OrderedDict
import json
import os
from pathlib import Path
import subprocess
import sys
import tempfile
from typing import Dict, List, Optional
from typing import Any, Dict, List, Optional
import yaml
from ruamel.yaml import YAML
from conda_build.metadata import (
Expand All @@ -19,11 +23,12 @@
validate_spec,
combine_specs,
)
from conda_build.metadata import get_selectors
from conda_build.metadata import get_selectors, check_bad_chrs
from conda_build.config import Config

from rattler_build_conda_compat.loader import parse_recipe_config_file
from rattler_build_conda_compat.utils import find_recipe
from rattler_build_conda_compat.jinja.jinja import render_recipe_with_context
from rattler_build_conda_compat.loader import load_yaml, parse_recipe_config_file
from rattler_build_conda_compat.utils import _get_recipe_metadata, find_recipe


class MetaData(CondaMetaData):
Expand Down Expand Up @@ -59,10 +64,53 @@ def __init__(

self.requirements_path = os.path.join(self.path, "requirements.txt")

def parse_recipe(self):
yaml = YAML()
with open(os.path.join(self.path, self._meta_name), "r") as recipe_yaml:
return yaml.load(recipe_yaml)
def parse_recipe(self) -> dict[str, Any]:
recipe_path: Path = Path(self.path) / self._meta_name

yaml_content = load_yaml(recipe_path.read_text())

return render_recipe_with_context(yaml_content)
baszalmstra marked this conversation as resolved.
Show resolved Hide resolved

def name(self) -> str:
baszalmstra marked this conversation as resolved.
Show resolved Hide resolved
"""
Returns the name of the package.
If recipe has multiple outputs, it will return the name of the `recipe` field.
Otherwise it will return the name of the `package` field.

Raises:
- CondaBuildUserError: If the `name` contains bad characters.

"""
name = _get_recipe_metadata(self.meta, "name")

if not name:
sys.exit(f"Error: package/name missing in: {self.meta_path!r}")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please just raise and let the user determine whether or not the application should exit or not!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This wasn't my implementation - I tried to reuse the same approach as conda_build does - so I decided to not move from sys.exit. Let me know if you think that it's worth to change it

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed.


if name != name.lower():
sys.exit(f"Error: package/name must be lowercase, got: {name!r}")

check_bad_chrs(name, "package/name")
return name

def version(self) -> str:
"""
Returns the version of the package.
If recipe has multiple outputs, it will return the version of the `recipe` field.
Otherwise it will return the version of the `package` field.

Raises:
- CondaBuildUserError: If the `version` contains bad characters.
- ValueError: If the version starts with a period.
"""
version = _get_recipe_metadata(self.meta, "version")

if not version:
sys.exit(f"Error: package/version missing in: {self.meta_path!r}")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ same here

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed


check_bad_chrs(version, "package/version")
if version.startswith("."):
raise ValueError("Fully-rendered version can't start with period - got %s", version)
nichmor marked this conversation as resolved.
Show resolved Hide resolved
return version

def render_recipes(self, variants) -> List[Dict]:
platform_and_arch = f"{self.config.platform}-{self.config.arch}"
Expand Down
17 changes: 16 additions & 1 deletion src/rattler_build_conda_compat/utils.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
from __future__ import annotations
import fnmatch
from logging import getLogger
import os
from pathlib import Path
from typing import Iterable
from typing import Any, Iterable, Literal


VALID_METAS = ("recipe.yaml",)
Expand Down Expand Up @@ -171,3 +172,17 @@ def has_recipe(recipe_dir: Path) -> bool:
return False
except OSError:
return False


_Metadata = Literal["name", "version"]


def _get_recipe_metadata(meta: dict[str, Any], field: _Metadata) -> str:
"""
Get recipe metadata ( name or version ).
It will extract from recipe or package section, depending on the presence of multiple outputs.
"""
if "outputs" in meta:
return meta.get("recipe", {}).get(field, "")
else:
return meta.get("package", {}).get(field, "")
1 change: 1 addition & 0 deletions tests/__snapshots__/test_jinja.ambr
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
- test -f ${PREFIX}/condabin/mamba
recipe:
name: mamba-split
version: 1.5.8
source:
sha256: 6ddaf4b0758eb7ca1250f427bc40c2c3ede43257a60bac54e4320a4de66759a6
url: https://github.com/mamba-org/mamba/archive/refs/tags/2024.03.25.tar.gz
Expand Down
34 changes: 17 additions & 17 deletions tests/__snapshots__/test_rattler_render.ambr
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
# serializer version: 1
# name: test_environ_is_passed_to_rattler_build
list([
CommentedMap({
'package': CommentedMap({
'name': 'py-test',
'version': '1.0.0',
dict({
'about': dict({
}),
'build': CommentedMap({
'skip': CommentedSeq([
'build': dict({
'skip': list([
"env.get('TEST_SHOULD_BE_PASSED') == 'false'",
]),
}),
'requirements': CommentedMap({
'build': CommentedSeq([
"${{ compiler('c') }}",
"${{ compiler('cuda') }}",
'extra': dict({
'final': True,
}),
'package': dict({
'name': 'py-test',
'version': '1.0.0',
}),
'requirements': dict({
'build': list([
'c_compiler_stub',
'cuda_compiler_stub',
]),
'host': CommentedSeq([
'host': list([
'python',
]),
'run': CommentedSeq([
'run': list([
'python',
]),
}),
'about': dict({
}),
'extra': dict({
'final': True,
}),
}),
])
# ---
Expand Down
22 changes: 22 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,25 @@ def old_recipe_dir(tmpdir: Path) -> Path:
meta.touch()

return recipe_dir


@pytest.fixture()
def mamba_recipe() -> Path:
return Path("tests/data/mamba_recipe.yaml")


@pytest.fixture()
def rich_recipe() -> Path:
return Path("tests/data/rich_recipe.yaml")


@pytest.fixture()
def feedstock_dir_with_recipe(tmpdir: Path) -> Path:
feedstock_dir = tmpdir / "feedstock"

feedstock_dir.mkdir()

recipe_dir = feedstock_dir / "recipe"
recipe_dir.mkdir()

return feedstock_dir
1 change: 1 addition & 0 deletions tests/data/mamba_recipe.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ context:

recipe:
name: mamba-split
version: ${{ mamba_version }}

source:
url: https://github.com/mamba-org/mamba/archive/refs/tags/${{ release }}.tar.gz
Expand Down
55 changes: 55 additions & 0 deletions tests/data/rich_recipe.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/prefix-dev/recipe-format/main/schema.json

context:
version: "13.4.2"
name: rich

package:
name: ${{ name}}
version: ${{ version }}

source:
- url:
- https://example.com/rich-${{ version }}.tar.gz # this will give a 404!
- https://pypi.io/packages/source/r/rich/rich-${{ version }}.tar.gz
sha256: d653d6bccede5844304c605d5aac802c7cf9621efd700b46c7ec2b51ea914898

build:
# Thanks to `noarch: python` this package works on all platforms
noarch: python
script:
- python -m pip install . -vv --no-deps --no-build-isolation

requirements:
host:
- pip
- poetry-core >=1.0.0
- python 3.10
run:
# sync with normalized deps from poetry-generated setup.py
- markdown-it-py >=2.2.0
- pygments >=2.13.0,<3.0.0
- python 3.10
- typing_extensions >=4.0.0,<5.0.0

tests:
- package_contents:
site_packages:
- rich
- python:
imports:
- rich

about:
homepage: https://github.com/Textualize/rich
license: MIT
license_file: LICENSE
summary: Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal
description: |
Rich is a Python library for rich text and beautiful formatting in the terminal.

The Rich API makes it easy to add color and style to terminal output. Rich
can also render pretty tables, progress bars, markdown, syntax highlighted
source code, tracebacks, and more — out of the box.
documentation: https://rich.readthedocs.io
repository: https://github.com/Textualize/rich
24 changes: 23 additions & 1 deletion tests/test_rattler_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from typing import TYPE_CHECKING, Any

from rattler_build_conda_compat.loader import parse_recipe_config_file
from rattler_build_conda_compat.render import render
from rattler_build_conda_compat.render import MetaData, render

if TYPE_CHECKING:
from pathlib import Path
Expand Down Expand Up @@ -43,3 +43,25 @@ def test_environ_is_passed_to_rattler_build(env_recipe, snapshot) -> None:

finally:
os.environ.pop("TEST_SHOULD_BE_PASSED", None)


def test_metadata_for_single_output(feedstock_dir_with_recipe: Path, rich_recipe: Path) -> None:
(feedstock_dir_with_recipe / "recipe" / "recipe.yaml").write_text(
rich_recipe.read_text(), encoding="utf8"
)

rattler_metadata = MetaData(feedstock_dir_with_recipe)

assert rattler_metadata.name() == "rich"
assert rattler_metadata.version() == "13.4.2"


def test_metadata_for_multiple_output(feedstock_dir_with_recipe: Path, mamba_recipe: Path) -> None:
(feedstock_dir_with_recipe / "recipe" / "recipe.yaml").write_text(
mamba_recipe.read_text(), encoding="utf8"
)

rattler_metadata = MetaData(feedstock_dir_with_recipe)

assert rattler_metadata.name() == "mamba-split"
assert rattler_metadata.version() == "1.5.8"