Skip to content

Commit

Permalink
feat: add typing and formatting (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
baszalmstra committed Jul 16, 2024
1 parent 49f8914 commit a21c100
Show file tree
Hide file tree
Showing 13 changed files with 101 additions and 101 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ jobs:
run: |
pixi install
- name: format
run: pixi run fmt-check

- name: lint
run: pixi run lint-check

- name: run tests
run: |
pixi run -e test pytest
Expand Down
2 changes: 1 addition & 1 deletion pixi.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5554,7 +5554,7 @@ packages:
name: rattler-build-conda-compat
version: 0.0.6
path: .
sha256: 7f8fd4a0d48bead9533b4678df50af556b799dbd65c42a1f28a6164f1cb6ce11
sha256: 3474bc3e1d38767b691b1c2ab527e002c30baa2d329b2ab2c91258a7e546474a
requires_python: '>=3.8'
editable: true
- kind: conda
Expand Down
4 changes: 3 additions & 1 deletion pixi.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ ruff = "*"
[feature.test.tasks]
test = "pytest"
snapshot_update = "pytest --snapshot-update"
lint = "ruff check src tests"
lint = "ruff check src"
lint-check = "ruff check --check --diff src"
fmt = "ruff format src tests"
fmt-check = "ruff format --check --diff src tests"

[environments]
test = ["test"]
24 changes: 24 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,30 @@ license = { file = "LICENSE.txt" }

requires-python = ">=3.8"

[tool.ruff]
target-version = "py38"
line-length = 100

[tool.ruff.format]
quote-style = "double"
indent-style = "space"

[tool.ruff.lint]
select = ["ALL"]
ignore = [
"E501", # https://docs.astral.sh/ruff/rules/line-too-long/
"D", # https://docs.astral.sh/ruff/rules/#pydocstyle-d
"COM812", # https://docs.astral.sh/ruff/rules/missing-trailing-comma/
"ISC001", # https://docs.astral.sh/ruff/rules/single-line-implicit-string-concatenation/
"T201", # https://docs.astral.sh/ruff/rules/print/
"A003", # https://docs.astral.sh/ruff/rules/builtin-attribute-shadowing/
]
exclude = [
"src/rattler_build_conda_compat/lint.py",
"src/rattler_build_conda_compat/loader.py",
"src/rattler_build_conda_compat/render.py",
"src/rattler_build_conda_compat/utils.py",
]

[tool.pyright]
venvPath = ".pixi/envs"
Expand Down
22 changes: 12 additions & 10 deletions src/rattler_build_conda_compat/conditional_list.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from typing import Any, Callable, Generator, Generic, Optional, TypeVar, Union
from __future__ import annotations

from typing import Any, Callable, Generator, Generic, TypeVar, Union

T = TypeVar("T")

Expand All @@ -13,7 +15,7 @@ class IfStatement(Generic[T]):


def visit_conditional_list(
value: ConditionalList[T], evaluator: Optional[Callable[[Any], bool]] = None
value: ConditionalList[T], evaluator: Callable[[Any], bool] | None = None
) -> Generator[T, None, None]:
"""
A function that yields individual branches of a conditional list.
Expand All @@ -28,19 +30,19 @@ def visit_conditional_list(
A generator that yields the individual branches.
"""

def yield_from_list(value):
def yield_from_list(value: list[T] | T) -> Generator[T, None, None]:
if isinstance(value, list):
yield from value
else:
yield value

value = value if isinstance(value, list) else [value]

for value in value:
if isinstance(value, dict):
if (expr := value.get("if", None)) is not None:
then = value.get("then")
otherwise = value.get("else")
for element in value:
if isinstance(element, dict):
if (expr := element.get("if", None)) is not None:
then = element.get("then")
otherwise = element.get("else")
if evaluator:
if evaluator(expr):
yield from yield_from_list(then)
Expand All @@ -51,6 +53,6 @@ def yield_from_list(value):
if otherwise:
yield from yield_from_list(otherwise)
else:
yield value
yield element
else:
yield value
yield element
59 changes: 16 additions & 43 deletions src/rattler_build_conda_compat/lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,7 @@

from rattler_build_conda_compat.loader import load_yaml

SCHEMA_URL = (
"https://raw.githubusercontent.com/prefix-dev/recipe-format/main/schema.json"
)
SCHEMA_URL = "https://raw.githubusercontent.com/prefix-dev/recipe-format/main/schema.json"

REQUIREMENTS_ORDER = ["build", "host", "run"]

Expand Down Expand Up @@ -69,9 +67,7 @@ def lint_about_contents(about_section, lints):
for about_item in ["homepage", "license", "summary"]:
# if the section doesn't exist, or is just empty, lint it.
if not about_section.get(about_item, ""):
lints.append(
"The {} item is expected in the about section." "".format(about_item)
)
lints.append("The {} item is expected in the about section." "".format(about_item))


def lint_recipe_maintainers(maintainers_section, lints):
Expand All @@ -81,10 +77,7 @@ def lint_recipe_maintainers(maintainers_section, lints):
"the `extra/recipe-maintainers` section."
)

if not (
isinstance(maintainers_section, Sequence)
and not isinstance(maintainers_section, str)
):
if not (isinstance(maintainers_section, Sequence) and not isinstance(maintainers_section, str)):
lints.append("Recipe maintainers should be a json list.")


Expand All @@ -105,8 +98,9 @@ def lint_recipe_tests(test_section=dict(), outputs_section=list()):
has_outputs_test = True
else:
no_test_hints.append(
"It looks like the '{}' output doesn't "
"have any tests.".format(section.get("name", "???"))
"It looks like the '{}' output doesn't " "have any tests.".format(
section.get("name", "???")
)
)
if has_outputs_test:
hints.extend(no_test_hints)
Expand Down Expand Up @@ -146,9 +140,7 @@ def lint_package_version(package_section: dict, context_section: dict):
package_ver = str(package_section.get("version"))
context_ver = str(context_section.get("version"))
ver = (
package_ver
if package_ver is not None and not package_ver.startswith("$")
else context_ver
package_ver if package_ver is not None and not package_ver.startswith("$") else context_ver
)

try:
Expand All @@ -160,9 +152,7 @@ def lint_package_version(package_section: dict, context_section: dict):

def lint_files_have_hash(sources_section: list, lints: list):
for source_section in sources_section:
if "url" in source_section and not (
{"sha1", "sha256", "md5"} & set(source_section.keys())
):
if "url" in source_section and not ({"sha1", "sha256", "md5"} & set(source_section.keys())):
lints.append(
"When defining a source/url please add a sha256, sha1 "
"or md5 checksum (sha256 preferably)."
Expand Down Expand Up @@ -208,9 +198,7 @@ def lint_legacy_patterns(requirements_section):
return lints


def lint_usage_of_selectors_for_noarch(
noarch_value, build_section, requirements_section
):
def lint_usage_of_selectors_for_noarch(noarch_value, build_section, requirements_section):
lints = []
for section in requirements_section:
section_requirements = requirements_section[section]
Expand Down Expand Up @@ -307,9 +295,7 @@ def lint_non_noarch_dont_constrain_python_and_rbase(requirements_section):
filtered_run_reqs = [req for req in run_reqs if req.startswith(f"{language}")]

if filtered_host_reqs and not filtered_run_reqs:
lints.append(
f"If {language} is a host requirement, it should be a run requirement."
)
lints.append(f"If {language} is a host requirement, it should be a run requirement.")

for reqs in [filtered_host_reqs, filtered_run_reqs]:
if language not in reqs:
Expand Down Expand Up @@ -401,9 +387,7 @@ def hint_noarch_usage(build_section, requirement_section: dict):
no_arch_possible = False

for _, section_requirements in requirement_section.items():
if any(
isinstance(requirement, dict) for requirement in section_requirements
):
if any(isinstance(requirement, dict) for requirement in section_requirements):
no_arch_possible = False
break

Expand Down Expand Up @@ -480,15 +464,11 @@ def run_conda_forge_specific(

url = None
if isinstance(sources_section, dict):
if str(sources_section.get("url")).startswith(
"https://pypi.io/packages/source/"
):
if str(sources_section.get("url")).startswith("https://pypi.io/packages/source/"):
url = sources_section["url"]
else:
for source_section in sources_section:
if str(source_section.get("url")).startswith(
"https://pypi.io/packages/source/"
):
if str(source_section.get("url")).startswith("https://pypi.io/packages/source/"):
url = source_section["url"]

if url:
Expand Down Expand Up @@ -519,21 +499,14 @@ def run_conda_forge_specific(

# 3: if the recipe dir is inside the example dir
if recipe_dir is not None and "recipes/example/" in recipe_dir:
lints.append(
"Please move the recipe out of the example dir and " "into its own dir."
)
lints.append("Please move the recipe out of the example dir and " "into its own dir.")

# 4: Do not delete example recipe
if is_staged_recipes and recipe_dir is not None:
example_meta_fname = os.path.abspath(
os.path.join(recipe_dir, "..", "example", "meta.yaml")
)
example_meta_fname = os.path.abspath(os.path.join(recipe_dir, "..", "example", "meta.yaml"))

if not os.path.exists(example_meta_fname):
msg = (
"Please do not delete the example recipe found in "
"`recipes/example/meta.yaml`."
)
msg = "Please do not delete the example recipe found in " "`recipes/example/meta.yaml`."

if msg not in lints:
lints.append(msg)
Expand Down
9 changes: 4 additions & 5 deletions src/rattler_build_conda_compat/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ def render_recipes(self, variants) -> List[Dict]:
platform_and_arch = f"{self.config.platform}-{self.config.arch}"

try:
with tempfile.NamedTemporaryFile(
mode="w+"
) as outfile, tempfile.NamedTemporaryFile(mode="w") as variants_file:
with tempfile.NamedTemporaryFile(mode="w+") as outfile, tempfile.NamedTemporaryFile(
mode="w"
) as variants_file:
# dump variants in our variants that will be used to generate recipe
if variants:
yaml.dump(variants, variants_file, default_flow_style=False)
Expand Down Expand Up @@ -107,8 +107,7 @@ def get_used_vars(self, force_top_level=False, force_global=False):
return set()

used_vars = [
var.replace("-", "_")
for var in self.meta["build_configuration"]["variant"].keys()
var.replace("-", "_") for var in self.meta["build_configuration"]["variant"].keys()
]

# in conda-build target-platform is not returned as part of yaml vars
Expand Down
12 changes: 3 additions & 9 deletions src/rattler_build_conda_compat/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,16 +130,12 @@ def find_recipe(path):
if os.path.isfile(path):
if os.path.basename(path) in VALID_METAS:
return path
raise OSError(
"{} is not a valid meta file ({})".format(path, ", ".join(VALID_METAS))
)
raise OSError("{} is not a valid meta file ({})".format(path, ", ".join(VALID_METAS)))

results = list(rec_glob(path, VALID_METAS, ignores=(".AppleDouble",)))

if not results:
raise OSError(
"No meta files ({}) found in {}".format(", ".join(VALID_METAS), path)
)
raise OSError("No meta files ({}) found in {}".format(", ".join(VALID_METAS), path))

if len(results) == 1:
return results[0]
Expand All @@ -156,9 +152,7 @@ def find_recipe(path):
)
return os.path.join(path, metas[0])

raise OSError(
"More than one meta files ({}) found in {}".format(", ".join(VALID_METAS), path)
)
raise OSError("More than one meta files ({}) found in {}".format(", ".join(VALID_METAS), path))


def has_recipe(recipe_dir: Path) -> bool:
Expand Down
25 changes: 12 additions & 13 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import pytest
from os import mkdir
from pathlib import Path

import pytest


@pytest.fixture
@pytest.fixture()
def python_recipe(tmpdir):
recipe_dir = tmpdir / "recipe"
mkdir(recipe_dir)
Expand All @@ -16,10 +17,10 @@ def python_recipe(tmpdir):
variants_yaml: Path = recipe_dir / "variants.yaml"
variants_yaml.write_text(variants, encoding="utf8")

yield recipe_dir
return recipe_dir


@pytest.fixture
@pytest.fixture()
def env_recipe(tmpdir):
recipe_dir = tmpdir / "recipe"
mkdir(recipe_dir)
Expand All @@ -28,33 +29,31 @@ def env_recipe(tmpdir):
recipe_yaml: Path = recipe_dir / "recipe.yaml"
recipe_yaml.write_text(env_recipe, encoding="utf8")

yield recipe_dir
return recipe_dir


@pytest.fixture
@pytest.fixture()
def unix_namespace():
namespace = {"linux-64": True, "unix": True}

return namespace
return {"linux-64": True, "unix": True}


@pytest.fixture
@pytest.fixture()
def recipe_dir(tmpdir):
py_recipe = Path("tests/data/py_recipe.yaml").read_text()
recipe_dir = tmpdir / "recipe"
mkdir(recipe_dir)

(recipe_dir / "recipe.yaml").write_text(py_recipe, encoding="utf8")

yield recipe_dir
return recipe_dir


@pytest.fixture
@pytest.fixture()
def old_recipe_dir(tmpdir):
recipe_dir = tmpdir / "recipe"
mkdir(recipe_dir)

meta = Path(recipe_dir / "meta.yaml")
meta.touch()

yield recipe_dir
return recipe_dir
Loading

0 comments on commit a21c100

Please sign in to comment.