Skip to content

Commit b8bd8d8

Browse files
Add requirement.constraint key to support package-wise constraints (#97)
Resolve: #87 This PR adds a new key in meta.yaml file: `requirement.constraint`. In the build time, pyodide-build will generate a new constraints.txt file in the build directory, and update the PIP_CONSTRAINT env variable to point that file. The new constarints.txt file contains `host_constraints_file + constraints defined in the recipe`. - [x] changelog --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 95c3850 commit b8bd8d8

File tree

6 files changed

+81
-1
lines changed

6 files changed

+81
-1
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## Unreleased
99

10+
## [0.29.3] - 2025/02/04
11+
1012
### Added
1113

1214
- Added new configuration variable `default_cross_build_env_url`.
1315
[#85](https://github.com/pyodide/pyodide-build/pull/85)
1416

17+
- Added a new recipe key `requirement.constraint` to set the package-level constraints.
18+
[#97](https://github.com/pyodide/pyodide-build/pull/97)
19+
1520
## [0.29.2] - 2024/11/29
1621

1722
### Fixed

pyodide_build/recipe/builder.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
RUST_BUILD_PRELUDE,
2323
BuildArgs,
2424
get_build_environment_vars,
25+
get_build_flag,
2526
get_pyodide_root,
2627
pyodide_tags,
2728
replace_so_abi_tags,
@@ -337,6 +338,34 @@ def _download_and_extract(self) -> None:
337338
shutil.move(self.build_dir / extract_dir_name, self.src_extract_dir)
338339
self.src_dist_dir.mkdir(parents=True, exist_ok=True)
339340

341+
def _create_constraints_file(self) -> str:
342+
"""
343+
Creates a pip constraints file by concatenating global constraints (PIP_CONSTRAINT)
344+
with constraints specific to this package.
345+
346+
returns the path to the new constraints file.
347+
"""
348+
try:
349+
host_constraints = get_build_flag("PIP_CONSTRAINT")
350+
except ValueError:
351+
host_constraints = ""
352+
353+
constraints = self.recipe.requirements.constraint
354+
if not constraints:
355+
# nothing to override
356+
return host_constraints
357+
358+
host_constraints_file = Path(host_constraints)
359+
new_constraints_file = self.build_dir / "constraints.txt"
360+
if host_constraints_file.is_file():
361+
shutil.copy(host_constraints_file, new_constraints_file)
362+
363+
with new_constraints_file.open("a") as f:
364+
for constraint in constraints:
365+
f.write(constraint + "\n")
366+
367+
return str(new_constraints_file)
368+
340369
def _compile(
341370
self,
342371
bash_runner: BashRunnerWithSharedEnvironment,
@@ -383,6 +412,8 @@ def _compile(
383412
)
384413
build_env = runner.env
385414

415+
build_env["PIP_CONSTRAINT"] = str(self._create_constraints_file())
416+
386417
pypabuild.build(
387418
self.src_extract_dir, self.src_dist_dir, build_env, config_settings
388419
)

pyodide_build/recipe/spec.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ class _RequirementsSpec(BaseModel):
124124
run: list[str] = []
125125
host: list[str] = []
126126
executable: list[str] = []
127+
constraint: list[str] = []
127128
model_config = ConfigDict(extra="forbid")
128129

129130

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package:
2+
name: pkg_test_constraint
3+
version: "1.0.0"
4+
requirements:
5+
constraint:
6+
- numpy < 2.0
7+
- pytest == 7.0
8+
source:
9+
path: src
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[build-system]
2+
requires = ["setuptools>=42", "wheel"]
3+
build-backend = "setuptools.build_meta"
4+
5+
[project]
6+
name = "pkg_test_constraint"
7+
version = "1.0.0"
8+
authors = []

pyodide_build/tests/recipe/test_builder.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import pytest
99

1010
from pyodide_build import common
11-
from pyodide_build.build_env import BuildArgs
11+
from pyodide_build.build_env import BuildArgs, get_build_flag
1212
from pyodide_build.recipe import builder as _builder
1313
from pyodide_build.recipe.builder import (
1414
RecipeBuilder,
@@ -216,6 +216,32 @@ def rlist(input_dir):
216216
assert n_moved == 3
217217

218218

219+
def test_create_constraints_file_no_override(tmp_path, dummy_xbuildenv):
220+
builder = RecipeBuilder.get_builder(
221+
recipe=RECIPE_DIR
222+
/ "pkg_test_executable", # constraints not set, so no override
223+
build_args=BuildArgs(),
224+
build_dir=tmp_path,
225+
)
226+
227+
path = builder._create_constraints_file()
228+
assert path == get_build_flag("PIP_CONSTRAINT")
229+
230+
231+
def test_create_constraints_file_override(tmp_path, dummy_xbuildenv):
232+
builder = RecipeBuilder.get_builder(
233+
recipe=RECIPE_DIR / "pkg_test_constraint",
234+
build_args=BuildArgs(),
235+
build_dir=tmp_path,
236+
)
237+
238+
path = builder._create_constraints_file()
239+
assert path == str(tmp_path / "constraints.txt")
240+
241+
data = Path(path).read_text().strip().split("\n")
242+
assert data[-3:] == ["numpy < 2.0", "pytest == 7.0"], data
243+
244+
219245
class MockSourceSpec(_SourceSpec):
220246
@pydantic.model_validator(mode="after")
221247
def _check_patches_extra(self) -> Self:

0 commit comments

Comments
 (0)