Skip to content

Commit

Permalink
Bump pydantic version used in pyodide-build to v2 (#4676)
Browse files Browse the repository at this point in the history
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
ryanking13 and pre-commit-ci[bot] committed May 10, 2024
1 parent 902453a commit afe7215
Show file tree
Hide file tree
Showing 9 changed files with 86 additions and 99 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ repos:
- numpy
- build
- pytest
- pydantic<2
- pydantic>=2.0
- unearth
- resolvelib
- rich
- auditwheel_emscripten
- pyodide-lock==0.1.0a4
- pyodide-lock==0.1.0a5
- micropip
- id: mypy
name: mypy-tests
Expand Down
2 changes: 1 addition & 1 deletion pyodide-build/pyodide_build/buildall.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def tests_path(self) -> Path | None:
class Package(BasePackage):
def __init__(self, pkgdir: Path, config: MetaConfig):
self.pkgdir = pkgdir
self.meta = config.copy(deep=True)
self.meta = config.model_copy(deep=True)

self.name = self.meta.package.name
self.version = self.meta.package.version
Expand Down
108 changes: 47 additions & 61 deletions pyodide-build/pyodide_build/io.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from pathlib import Path
from typing import Any, Literal
from typing import Literal, Self

import pydantic
from pydantic import BaseModel, Field
from pydantic import BaseModel, ConfigDict, Field


class _PackageSpec(BaseModel):
Expand All @@ -11,9 +11,7 @@ class _PackageSpec(BaseModel):
top_level: list[str] = Field([], alias="top-level")
tag: list[str] = Field([])
disabled: bool = Field(False, alias="_disabled")

class Config:
extra = pydantic.Extra.forbid
model_config = ConfigDict(extra="forbid")


class _SourceSpec(BaseModel):
Expand All @@ -23,22 +21,21 @@ class _SourceSpec(BaseModel):
sha256: str | None = None
patches: list[str] = []
extras: list[tuple[str, str]] = []
model_config = ConfigDict(extra="forbid")

class Config:
extra = pydantic.Extra.forbid

@pydantic.root_validator
def _check_url_has_hash(cls, values: dict[str, Any]) -> dict[str, Any]:
if values["url"] is not None and values["sha256"] is None:
@pydantic.model_validator(mode="after")
def _check_url_has_hash(self) -> Self:
if self.url is not None and self.sha256 is None:
raise ValueError(
"If source is downloaded from url, it must have a 'source/sha256' hash."
)
return values

@pydantic.root_validator
def _check_in_tree_url(cls, values: dict[str, Any]) -> dict[str, Any]:
in_tree = values["path"] is not None
from_url = values["url"] is not None
return self

@pydantic.model_validator(mode="after")
def _check_in_tree_url(self) -> Self:
in_tree = self.path is not None
from_url = self.url is not None

# cpython_modules is a special case, it is not in the tree
# TODO: just copy the file into the tree?
Expand All @@ -49,30 +46,27 @@ def _check_in_tree_url(cls, values: dict[str, Any]) -> dict[str, Any]:
raise ValueError(
"Source section should not have both a 'url' and a 'path' key"
)
return values

@pydantic.root_validator
def _check_patches_extra(cls, values: dict[str, Any]) -> dict[str, Any]:
patches = values["patches"]
extras = values["extras"]
in_tree = values["path"] is not None
from_url = values["url"] is not None
return self

url_is_wheel = from_url and values["url"].endswith(".whl")
@pydantic.model_validator(mode="after")
def _check_patches_extra(self) -> Self:
in_tree = self.path is not None
url_is_wheel = self.url and self.url.endswith(".whl")

if in_tree and (patches or extras):
if in_tree and (self.patches or self.extras):
raise ValueError(
"If source is in tree, 'source/patches' and 'source/extras' keys "
"are not allowed"
)

if url_is_wheel and (patches or extras):
if url_is_wheel and (self.patches or self.extras):
raise ValueError(
"If source is a wheel, 'source/patches' and 'source/extras' "
"keys are not allowed"
)

return values
return self


_ExportTypes = Literal["pyinit", "requested", "whole_archive"]
Expand All @@ -97,18 +91,16 @@ class _BuildSpec(BaseModel):
vendor_sharedlib: bool = Field(False, alias="vendor-sharedlib")
cross_build_env: bool = Field(False, alias="cross-build-env")
cross_build_files: list[str] = Field([], alias="cross-build-files")
model_config = ConfigDict(extra="forbid")

class Config:
extra = pydantic.Extra.forbid

@pydantic.root_validator
def _check_config(cls, values: dict[str, Any]) -> dict[str, Any]:
static_library = values["package_type"] == "static_library"
shared_library = values["package_type"] == "shared_library"
cpython_module = values["package_type"] == "cpython_module"
@pydantic.model_validator(mode="after")
def _check_config(self) -> Self:
static_library = self.package_type == "static_library"
shared_library = self.package_type == "shared_library"
cpython_module = self.package_type == "cpython_module"

if not (static_library or shared_library or cpython_module):
return values
return self

allowed_keys = {
"package_type",
Expand All @@ -117,39 +109,34 @@ def _check_config(cls, values: dict[str, Any]) -> dict[str, Any]:
"unvendor_tests",
}

typ = values["package_type"]
for key, val in values.items():
if val and key not in allowed_keys:
typ = self.package_type
for key in self.model_fields_set:
if key not in allowed_keys:
raise ValueError(
f"If building a {typ}, 'build/{key}' key is not allowed."
)
return values

return self


class _RequirementsSpec(BaseModel):
run: list[str] = []
host: list[str] = []
executable: list[str] = []

class Config:
extra = pydantic.Extra.forbid
model_config = ConfigDict(extra="forbid")


class _TestSpec(BaseModel):
imports: list[str] = []

class Config:
extra = pydantic.Extra.forbid
model_config = ConfigDict(extra="forbid")


class _AboutSpec(BaseModel):
home: str | None = None
PyPI: str | None = None
summary: str | None = None
license: str | None = None

class Config:
extra = pydantic.Extra.forbid
model_config = ConfigDict(extra="forbid")


class _ExtraSpec(BaseModel):
Expand All @@ -164,9 +151,7 @@ class MetaConfig(BaseModel):
test: _TestSpec = _TestSpec()
about: _AboutSpec = _AboutSpec()
extra: _ExtraSpec = _ExtraSpec()

class Config:
extra = pydantic.Extra.forbid
model_config = ConfigDict(extra="forbid")

@classmethod
def from_yaml(cls, path: Path) -> "MetaConfig":
Expand Down Expand Up @@ -198,18 +183,18 @@ def to_yaml(self, path: Path) -> None:
import yaml

with open(path, "w") as f:
yaml.dump(self.dict(by_alias=True, exclude_unset=True), f)
yaml.dump(self.model_dump(by_alias=True, exclude_unset=True), f)

@pydantic.root_validator
def _check_wheel_host_requirements(cls, values: dict[str, Any]) -> dict[str, Any]:
@pydantic.model_validator(mode="after")
def _check_wheel_host_requirements(self) -> Self:
"""Check that if sources is a wheel it shouldn't have host dependencies."""
if "source" not in values:
if self.source.path is None and self.source.url is None:
raise ValueError(
'either "path" or "url" must be provided in the "source" section'
)

source_url = values["source"].url
requirements_host = values["requirements"].host
source_url = self.source.url
requirements_host = self.requirements.host

if source_url is not None and source_url.endswith(".whl"):
if len(requirements_host):
Expand All @@ -228,12 +213,13 @@ def _check_wheel_host_requirements(cls, values: dict[str, Any]) -> dict[str, Any
"unvendor_tests",
"package_type",
}
for key, val in values["build"].dict().items():
if val and key not in allowed_keys:
for key in self.build.model_fields_set:
if key not in allowed_keys:
raise ValueError(
f"If source is a wheel, 'build/{key}' key is not allowed"
)
return values

return self

def is_rust_package(self) -> bool:
"""
Expand Down
13 changes: 6 additions & 7 deletions pyodide-build/pyodide_build/pypabuild.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from itertools import chain
from pathlib import Path
from tempfile import TemporaryDirectory
from typing import cast
from typing import Literal, cast

from build import BuildBackendException, ConfigSettingsType
from build.env import DefaultIsolatedEnv
Expand Down Expand Up @@ -136,7 +136,7 @@ def _build_in_isolated_env(
build_env: Mapping[str, str],
srcdir: Path,
outdir: str,
distribution: str,
distribution: Literal["sdist", "wheel"],
config_settings: ConfigSettingsType,
) -> str:
# For debugging: The following line disables removal of the isolated venv.
Expand All @@ -157,7 +157,7 @@ def _build_in_isolated_env(
installed_requires_for_build = False
try:
build_reqs = builder.get_requires_for_build(
distribution, # type: ignore[arg-type]
distribution,
)
except BuildBackendException:
pass
Expand All @@ -168,13 +168,13 @@ def _build_in_isolated_env(
with common.replace_env(build_env):
if not installed_requires_for_build:
build_reqs = builder.get_requires_for_build(
distribution, # type: ignore[arg-type]
distribution,
config_settings,
)
install_reqs(env, build_reqs)

return builder.build(
distribution, # type: ignore[arg-type]
distribution,
outdir,
config_settings,
)
Expand Down Expand Up @@ -292,11 +292,10 @@ def build(
build_env: Mapping[str, str],
config_settings: ConfigSettingsType,
) -> str:
distribution = "wheel"
try:
with _handle_build_error():
built = _build_in_isolated_env(
build_env, srcdir, str(outdir), distribution, config_settings
build_env, srcdir, str(outdir), "wheel", config_settings
)
print("{bold}{green}Successfully built {}{reset}".format(built, **_STYLES))
return built
Expand Down
10 changes: 5 additions & 5 deletions pyodide-build/pyodide_build/recipe.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,21 +63,21 @@ def load_recipes(
for name_or_tag in names_or_tags:
# 1. package name
if name_or_tag in available_recipes:
recipes[name_or_tag] = available_recipes[name_or_tag].copy(deep=True)
recipes[name_or_tag] = available_recipes[name_or_tag].model_copy(deep=True)

# 2. tag
elif (
name_or_tag.startswith("tag:")
and (tag := name_or_tag.removeprefix("tag:")) in tagged_recipes
):
for recipe in tagged_recipes[tag]:
recipes[recipe.package.name] = recipe.copy(deep=True)
recipes[recipe.package.name] = recipe.model_copy(deep=True)

# 3. meta packages
elif name_or_tag == "*": # all packages
recipes.update(
{
name: package.copy(deep=True)
name: package.model_copy(deep=True)
for name, package in available_recipes.items()
}
)
Expand All @@ -91,13 +91,13 @@ def load_recipes(
f" use 'tag:{name_or_tag}' instead."
)
for recipe in tagged_recipes[name_or_tag]:
recipes[recipe.package.name] = recipe.copy(deep=True)
recipes[recipe.package.name] = recipe.model_copy(deep=True)
else:
raise ValueError(f"Unknown package name or tag: {name_or_tag}")

if load_always_tag:
always_recipes = tagged_recipes.get("always", [])
for recipe in always_recipes:
recipes[recipe.package.name] = recipe.copy(deep=True)
recipes[recipe.package.name] = recipe.model_copy(deep=True)

return recipes
12 changes: 7 additions & 5 deletions pyodide-build/pyodide_build/tests/test_buildpkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import subprocess
import time
from pathlib import Path
from typing import Self

import pydantic
import pytest
Expand Down Expand Up @@ -172,6 +173,12 @@ def rlist(input_dir):
assert n_moved == 3


class MockSourceSpec(_SourceSpec):
@pydantic.model_validator(mode="after")
def _check_patches_extra(self) -> Self:
return self


def test_needs_rebuild(tmpdir):
pkg_root = Path(tmpdir)
buildpath = pkg_root / "build"
Expand All @@ -183,11 +190,6 @@ def test_needs_rebuild(tmpdir):
src_path = pkg_root / "src"
src_path_file = src_path / "file"

class MockSourceSpec(_SourceSpec):
@pydantic.root_validator
def _check_patches_extra(cls, values):
return values

source_metadata = MockSourceSpec(
patches=[
str(patch_file),
Expand Down

0 comments on commit afe7215

Please sign in to comment.