Skip to content

Commit 47f3017

Browse files
committed
fix context type issues
1 parent 5796e82 commit 47f3017

File tree

12 files changed

+624
-2326
lines changed

12 files changed

+624
-2326
lines changed

crates/rattler_build_jinja/src/variable.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,36 @@ impl Variable {
6262
pub fn from_string(value: &str) -> Self {
6363
Variable(Value::from_safe_string(value.to_string()))
6464
}
65+
66+
/// Try to extract as a boolean value using serde deserialization
67+
pub fn as_bool(&self) -> Option<bool> {
68+
bool::deserialize(self.0.clone()).ok()
69+
}
70+
71+
/// Try to extract as an i64 value using serde deserialization
72+
pub fn as_i64(&self) -> Option<i64> {
73+
i64::deserialize(self.0.clone()).ok()
74+
}
75+
76+
/// Check if this variable is a number
77+
pub fn is_number(&self) -> bool {
78+
self.0.is_number()
79+
}
80+
81+
/// Try to extract as a string slice
82+
pub fn as_str(&self) -> Option<&str> {
83+
self.0.as_str()
84+
}
85+
86+
/// Check if this is a sequence/list
87+
pub fn is_sequence(&self) -> bool {
88+
self.0.len().is_some() && self.0.as_str().is_none()
89+
}
90+
91+
/// Try to iterate over the variable if it's a sequence
92+
pub fn try_iter(&self) -> Result<impl Iterator<Item = Value> + '_, minijinja::Error> {
93+
self.0.try_iter()
94+
}
6595
}
6696

6797
impl Display for Variable {

py-rattler-build/Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

py-rattler-build/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ tokio = { version = "1.47.1", features = [
3636
rattler_conda_types = "0.40.0"
3737
rattler_config = "0.2.11"
3838
rattler_networking = { version = "0.25.15" }
39+
rattler_solve = "3.0.9"
40+
rattler_virtual_packages = "2.2.5"
3941
clap = "4.5.48"
4042
url = "2.5.7"
4143
chrono = "0.4.42"

py-rattler-build/pixi.lock

Lines changed: 0 additions & 2293 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

py-rattler-build/rattler_build/render.py

Lines changed: 117 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@
1010

1111
from rattler_build.stage0 import MultiOutputRecipe, SingleOutputRecipe
1212

13+
# Import for type hints only - avoid circular import
14+
if TYPE_CHECKING:
15+
from rattler_build.tool_config import ToolConfiguration
16+
17+
# Type for context values - can be strings, numbers, bools, or lists
18+
ContextValue = Union[str, int, float, bool, List[Union[str, int, float, bool]]]
19+
1320
# Try to import TypeAlias for better type hint support
1421
try:
1522
from typing import TypeAlias
@@ -18,6 +25,7 @@
1825

1926
if TYPE_CHECKING:
2027
from rattler_build.variant_config import VariantConfig
28+
2129
# For type checking, use Any placeholders
2230
_RenderConfig = Any
2331
_RenderedVariant = Any
@@ -166,7 +174,7 @@ def __init__(
166174
recipe_path=recipe_path,
167175
)
168176

169-
def set_context(self, key: str, value: Any) -> None:
177+
def set_context(self, key: str, value: ContextValue) -> None:
170178
"""Add an extra context variable for Jinja rendering.
171179
172180
Args:
@@ -175,7 +183,7 @@ def set_context(self, key: str, value: Any) -> None:
175183
"""
176184
self._config.set_context(key, value)
177185

178-
def get_context(self, key: str) -> Optional[Any]:
186+
def get_context(self, key: str) -> Optional[ContextValue]:
179187
"""Get an extra context variable.
180188
181189
Args:
@@ -186,7 +194,7 @@ def get_context(self, key: str) -> Optional[Any]:
186194
"""
187195
return self._config.get_context(key)
188196

189-
def get_all_context(self) -> Dict[str, Any]:
197+
def get_all_context(self) -> Dict[str, ContextValue]:
190198
"""Get all extra context variables as a dictionary."""
191199
return self._config.get_all_context()
192200

@@ -263,7 +271,7 @@ class RenderedVariant:
263271
... print(f"Build string: {variant.recipe().build().string()}")
264272
"""
265273

266-
def __init__(self, inner: Any):
274+
def __init__(self, inner: _RenderedVariant):
267275
"""Create a RenderedVariant from the Rust object."""
268276
self._inner = inner
269277

@@ -313,6 +321,48 @@ def pin_subpackages(self) -> Dict[str, PinSubpackageInfo]:
313321
inner_dict = self._inner.pin_subpackages()
314322
return {name: PinSubpackageInfo(info) for name, info in inner_dict.items()}
315323

324+
def run_build(
325+
self,
326+
tool_config: Optional["ToolConfiguration"] = None,
327+
output_dir: Optional[Union[str, Path]] = None,
328+
channel: Optional[List[str]] = None,
329+
**kwargs: Any,
330+
) -> None:
331+
"""Build this rendered variant.
332+
333+
This method builds a single rendered variant directly without needing
334+
to go back through the Stage0 recipe.
335+
336+
Args:
337+
tool_config: Optional ToolConfiguration to use for the build.
338+
output_dir: Directory to store the built package. Defaults to current directory.
339+
channel: List of channels to use for resolving dependencies.
340+
**kwargs: Additional arguments passed to build (e.g., keep_build, test, etc.)
341+
342+
Example:
343+
>>> from rattler_build.stage0 import Recipe
344+
>>> from rattler_build.variant_config import VariantConfig
345+
>>> from rattler_build.render import render_recipe
346+
>>>
347+
>>> recipe = Recipe.from_yaml(yaml_string)
348+
>>> rendered = render_recipe(recipe, VariantConfig())
349+
>>> # Build just the first variant
350+
>>> rendered[0].run_build(output_dir="./output")
351+
"""
352+
from . import rattler_build as _rb
353+
354+
# Extract the inner ToolConfiguration if provided
355+
tool_config_inner = tool_config._inner if (tool_config and hasattr(tool_config, "_inner")) else tool_config
356+
357+
# Build this single variant
358+
_rb.build_from_rendered_variants_py(
359+
rendered_variants=[self._inner],
360+
tool_config=tool_config_inner,
361+
output_dir=Path(output_dir) if output_dir else None,
362+
channel=channel,
363+
**kwargs,
364+
)
365+
316366
def __repr__(self) -> str:
317367
return repr(self._inner)
318368

@@ -390,7 +440,7 @@ def render_recipe(
390440
if isinstance(recipe, Path):
391441
# Definitely a file path
392442
parsed = Recipe.from_file(recipe)
393-
elif recipe.endswith('.yaml') or recipe.endswith('.yml') or '/' in recipe or '\\' in recipe:
443+
elif recipe.endswith(".yaml") or recipe.endswith(".yml") or "/" in recipe or "\\" in recipe:
394444
# String that looks like a file path
395445
parsed = Recipe.from_file(recipe)
396446
else:
@@ -408,7 +458,12 @@ def render_recipe(
408458
variant_config = VC.from_file(variant_config)
409459
else:
410460
# Check if it's a file path or YAML string
411-
if variant_config.endswith('.yaml') or variant_config.endswith('.yml') or '/' in variant_config or '\\' in variant_config:
461+
if (
462+
variant_config.endswith(".yaml")
463+
or variant_config.endswith(".yml")
464+
or "/" in variant_config
465+
or "\\" in variant_config
466+
):
412467
variant_config = VC.from_file(variant_config)
413468
else:
414469
variant_config = VC.from_yaml(variant_config)
@@ -428,10 +483,66 @@ def render_recipe(
428483
return all_rendered
429484

430485

486+
def build_rendered_variants(
487+
rendered_variants: List[RenderedVariant],
488+
tool_config: Optional["ToolConfiguration"] = None,
489+
output_dir: Optional[Union[str, Path]] = None,
490+
channel: Optional[List[str]] = None,
491+
**kwargs: Any,
492+
) -> None:
493+
"""Build multiple rendered variants.
494+
495+
This is a convenience function for building multiple rendered variants
496+
in one call, useful when you want to build all variants from a recipe.
497+
498+
Args:
499+
rendered_variants: List of RenderedVariant objects to build
500+
tool_config: Optional ToolConfiguration to use for the build.
501+
output_dir: Directory to store the built packages. Defaults to current directory.
502+
channel: List of channels to use for resolving dependencies.
503+
**kwargs: Additional arguments passed to build (e.g., keep_build, test, etc.)
504+
505+
Example:
506+
>>> from rattler_build.stage0 import Recipe
507+
>>> from rattler_build.variant_config import VariantConfig
508+
>>> from rattler_build.render import render_recipe, build_rendered_variants
509+
>>>
510+
>>> # Parse and render recipe
511+
>>> recipe = Recipe.from_yaml(yaml_string)
512+
>>> variant_config = VariantConfig.from_yaml('''
513+
... python:
514+
... - "3.9"
515+
... - "3.10"
516+
... - "3.11"
517+
... ''')
518+
>>> rendered = render_recipe(recipe, variant_config)
519+
>>>
520+
>>> # Build all variants at once
521+
>>> build_rendered_variants(rendered, output_dir="./output")
522+
>>>
523+
>>> # Or build a subset
524+
>>> build_rendered_variants(rendered[:2], output_dir="./output")
525+
"""
526+
from . import rattler_build as _rb
527+
528+
# Extract the inner ToolConfiguration if provided
529+
tool_config_inner = tool_config._inner if (tool_config and hasattr(tool_config, "_inner")) else tool_config
530+
531+
# Build all variants
532+
_rb.build_from_rendered_variants_py(
533+
rendered_variants=[v._inner for v in rendered_variants],
534+
tool_config=tool_config_inner,
535+
output_dir=Path(output_dir) if output_dir else None,
536+
channel=channel,
537+
**kwargs,
538+
)
539+
540+
431541
__all__ = [
432542
"RenderConfig",
433543
"RenderedVariant",
434544
"HashInfo",
435545
"PinSubpackageInfo",
436546
"render_recipe",
547+
"build_rendered_variants",
437548
]

py-rattler-build/rattler_build/stage0.py

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
from typing import Any, Dict, List, Optional, Union, TYPE_CHECKING
1111

1212
if TYPE_CHECKING:
13+
from rattler_build.tool_config import ToolConfiguration
14+
1315
# For type checking, use Any as placeholder since we don't have stubs
1416
_Stage0Recipe = Any
1517
_SingleOutputRecipe = Any
@@ -232,10 +234,10 @@ def render(self, variant_config: Any = None, render_config: Any = None) -> List[
232234
def run_build(
233235
self,
234236
variant_config: Any = None,
235-
tool_config: Any = None,
237+
tool_config: Optional["ToolConfiguration"] = None,
236238
output_dir: Union[str, Path, None] = None,
237239
channel: Optional[List[str]] = None,
238-
**kwargs: Any
240+
**kwargs: Any,
239241
) -> None:
240242
"""
241243
Build this recipe.
@@ -267,15 +269,15 @@ def run_build(
267269
rendered_variants = self.render(variant_config)
268270

269271
# Extract the inner ToolConfiguration if provided
270-
tool_config_inner = tool_config._inner if hasattr(tool_config, '_inner') else tool_config
272+
tool_config_inner = tool_config._inner if hasattr(tool_config, "_inner") else tool_config
271273

272274
# Build from the rendered variants
273275
_rb.build_from_rendered_variants_py(
274276
rendered_variants=[v._inner for v in rendered_variants],
275277
tool_config=tool_config_inner,
276278
output_dir=Path(output_dir) if output_dir else None,
277279
channel=channel,
278-
**kwargs
280+
**kwargs,
279281
)
280282

281283
def to_dict(self) -> Dict[str, Any]:
@@ -364,10 +366,10 @@ def render(self, variant_config: Any = None, render_config: Any = None) -> List[
364366
def run_build(
365367
self,
366368
variant_config: Any = None,
367-
tool_config: Any = None,
369+
tool_config: Optional["ToolConfiguration"] = None,
368370
output_dir: Union[str, Path, None] = None,
369371
channel: Optional[List[str]] = None,
370-
**kwargs: Any
372+
**kwargs: Any,
371373
) -> None:
372374
"""
373375
Build this multi-output recipe.
@@ -399,15 +401,15 @@ def run_build(
399401
rendered_variants = self.render(variant_config)
400402

401403
# Extract the inner ToolConfiguration if provided
402-
tool_config_inner = tool_config._inner if hasattr(tool_config, '_inner') else tool_config
404+
tool_config_inner = tool_config._inner if hasattr(tool_config, "_inner") else tool_config
403405

404406
# Build from the rendered variants
405407
_rb.build_from_rendered_variants_py(
406408
rendered_variants=[v._inner for v in rendered_variants],
407409
tool_config=tool_config_inner,
408410
output_dir=Path(output_dir) if output_dir else None,
409411
channel=channel,
410-
**kwargs
412+
**kwargs,
411413
)
412414

413415
def to_dict(self) -> Dict[str, Any]:

0 commit comments

Comments
 (0)