From ff515673f580af1dbc3a8f5c9c852fbcbdf42c14 Mon Sep 17 00:00:00 2001 From: Tim Paine <3105306+timkpaine@users.noreply.github.com> Date: Fri, 10 Jul 2026 14:41:10 -0400 Subject: [PATCH 1/2] Rename Dep marker to Dependency and silence dep-slot serializer warning Signed-off-by: Tim Paine <3105306+timkpaine@users.noreply.github.com> --- ccflow/_flow_model_binding.py | 36 ++++----- .../examples/flow_model/flow_model_example.py | 6 +- .../flow_model_hydra_builder_demo.py | 6 +- ccflow/flow_model.py | 26 ++++--- ccflow/tests/test_flow_model.py | 74 +++++++++---------- docs/wiki/Flow-Model.md | 24 +++--- 6 files changed, 89 insertions(+), 83 deletions(-) diff --git a/ccflow/_flow_model_binding.py b/ccflow/_flow_model_binding.py index 17fbfcd..06d86a2 100644 --- a/ccflow/_flow_model_binding.py +++ b/ccflow/_flow_model_binding.py @@ -61,7 +61,7 @@ def __repr__(self) -> str: class _DepMarker: def __repr__(self) -> str: - return "Dep" + return "Dependency" class FromContext: @@ -81,11 +81,11 @@ def __class_getitem__(cls, item): return Annotated[item, _LazyMarker()] -class Dep: +class Dependency: """Marker used in ``@Flow.model`` signatures for explicit dependency leaves.""" def __new__(cls, *args, **kwargs): - raise TypeError("Dep is an annotation marker; use Dep[T] in @Flow.model signatures.") + raise TypeError("Dependency is an annotation marker; use Dependency[T] in @Flow.model signatures.") def __class_getitem__(cls, item): return Annotated[item, _DepMarker()] @@ -463,8 +463,8 @@ def _parse_annotation(annotation: Any) -> _ParsedAnnotation: raise TypeError("FromContext is an annotation marker; use FromContext[T] in @Flow.model signatures.") if annotation is Lazy: raise TypeError("Lazy is an annotation marker; use Lazy[T] in @Flow.model signatures.") - if annotation is Dep: - raise TypeError("Dep is an annotation marker; use Dep[T] in @Flow.model signatures.") + if annotation is Dependency: + raise TypeError("Dependency is an annotation marker; use Dependency[T] in @Flow.model signatures.") return _ParsedAnnotation( base=annotation, @@ -482,7 +482,7 @@ def _strip_annotated(annotation: Any) -> Any: def _pop_dep_marker(annotation: Any) -> Tuple[Any, bool]: - """Remove only the outer Dep marker while preserving other Annotated metadata.""" + """Remove only the outer Dependency marker while preserving other Annotated metadata.""" if get_origin(annotation) is not Annotated: return annotation, False @@ -493,7 +493,7 @@ def _pop_dep_marker(annotation: Any) -> Tuple[Any, bool]: base = args[0] if not metadata: return base, has_dep - # Keep non-Dep metadata, such as pydantic Field constraints, on the annotation + # Keep non-Dependency metadata, such as pydantic Field constraints, on the annotation # used to validate literals and resolved dependency results. Build the tuple # first and subscript ``Annotated`` with it: ``Annotated[base, *metadata]`` is # 3.11+-only syntax, and ``Annotated.__class_getitem__`` was removed in 3.14, @@ -509,31 +509,31 @@ def _annotation_contains_dep(annotation: Any) -> bool: def _validate_dep_annotation(annotation: Any, *, in_dep: bool = False, dep_allowed: bool = False) -> None: - """Validate the deliberately small Dep marker language. + """Validate the deliberately small Dependency marker language. - Dep marks exact substitution slots. It is allowed inside container values, - but not inside another Dep, not in dict keys, and not mixed with Lazy or - FromContext markers inside a Dep slot. + Dependency marks exact substitution slots. It is allowed inside container values, + but not inside another Dependency, not in dict keys, and not mixed with Lazy or + FromContext markers inside a Dependency slot. """ annotation, has_dep = _pop_dep_marker(annotation) if has_dep: if not dep_allowed: - raise TypeError("Dep[...] is only supported in regular parameter container values.") + raise TypeError("Dependency[...] is only supported in regular parameter container values.") if in_dep: - raise TypeError("Dep[...] cannot contain another Dep[...] marker.") + raise TypeError("Dependency[...] cannot contain another Dependency[...] marker.") _validate_dep_annotation(annotation, in_dep=True, dep_allowed=False) return if in_dep and get_origin(annotation) is Annotated: metadata = get_args(annotation)[1:] if any(isinstance(item, (_LazyMarker, _FromContextMarker)) for item in metadata): - raise TypeError("Dep[...] cannot contain Lazy[...] or FromContext[...] markers.") + raise TypeError("Dependency[...] cannot contain Lazy[...] or FromContext[...] markers.") origin = get_origin(annotation) args = get_args(annotation) if origin in _UNION_ORIGINS and any(_annotation_contains_dep(arg) for arg in args): - raise TypeError("Dep[...] is not supported inside union annotations.") + raise TypeError("Dependency[...] is not supported inside union annotations.") if origin is list and len(args) == 1: _validate_dep_annotation(args[0], in_dep=in_dep, dep_allowed=True) return @@ -545,7 +545,7 @@ def _validate_dep_annotation(annotation: Any, *, in_dep: bool = False, dep_allow if origin is dict and len(args) == 2: key_annotation, value_annotation = args if _annotation_contains_dep(key_annotation): - raise TypeError("Dep[...] is not supported in dict keys.") + raise TypeError("Dependency[...] is not supported in dict keys.") _validate_dep_annotation(value_annotation, in_dep=in_dep, dep_allowed=True) return @@ -655,9 +655,9 @@ def _analyze_flow_function( raise TypeError(f"Parameter '{param.name}' cannot combine Lazy[...] and FromContext[...].") if (parsed.is_dep or _annotation_contains_dep(parsed.base)) and (parsed.is_lazy or parsed.is_from_context): marker = "Lazy" if parsed.is_lazy else "FromContext" - raise TypeError(f"Parameter '{param.name}' cannot combine Dep[...] and {marker}[...].") + raise TypeError(f"Parameter '{param.name}' cannot combine Dependency[...] and {marker}[...].") if parsed.is_dep: - raise TypeError("Dep[...] is only supported in regular parameter container values.") + raise TypeError("Dependency[...] is only supported in regular parameter container values.") _validate_dep_annotation(parsed.base) has_dep_slots = _annotation_contains_dep(parsed.base) has_default = param.default is not inspect.Parameter.empty diff --git a/ccflow/examples/flow_model/flow_model_example.py b/ccflow/examples/flow_model/flow_model_example.py index 9085ff8..ced61de 100644 --- a/ccflow/examples/flow_model/flow_model_example.py +++ b/ccflow/examples/flow_model/flow_model_example.py @@ -6,7 +6,7 @@ 1. define stages as plain Python functions, 2. compose stages by passing upstream models as ordinary arguments, 3. rewrite contextual inputs on one dependency edge with `.flow.with_context(...)`, -4. use `Dep[...]` for model leaves inside regular container inputs, +4. use `Dependency[...]` for model leaves inside regular container inputs, 5. execute the configured graph with `model.flow.compute(...)`. Run with: @@ -15,7 +15,7 @@ from datetime import date, timedelta -from ccflow import DateRangeContext, Dep, Flow, FromContext +from ccflow import DateRangeContext, Dependency, Flow, FromContext def _format_input_names(inputs: dict[str, object]) -> str: @@ -54,7 +54,7 @@ def count_visitors( @Flow.model(context_type=DateRangeContext) def visitor_delta( - counts: list[Dep[int]], + counts: list[Dependency[int]], label: str, start_date: FromContext[date], end_date: FromContext[date], diff --git a/ccflow/examples/flow_model/flow_model_hydra_builder_demo.py b/ccflow/examples/flow_model/flow_model_hydra_builder_demo.py index df3a78e..58e38bf 100644 --- a/ccflow/examples/flow_model/flow_model_hydra_builder_demo.py +++ b/ccflow/examples/flow_model/flow_model_hydra_builder_demo.py @@ -10,7 +10,7 @@ - keep runtime context (`start_date`, `end_date`) as runtime inputs, - use a plain Python builder function for graph construction, -- use `Dep[...]` when a regular container input holds upstream models, +- use `Dependency[...]` when a regular container input holds upstream models, - let Hydra instantiate that builder and register the returned model. Run with: @@ -20,7 +20,7 @@ from datetime import date, timedelta from pathlib import Path -from ccflow import CallableModel, DateRangeContext, Dep, Flow, FromContext, ModelRegistry +from ccflow import CallableModel, DateRangeContext, Dependency, Flow, FromContext, ModelRegistry CONFIG_PATH = Path(__file__).with_name("config") / "flow_model_hydra_builder_demo.yaml" @@ -65,7 +65,7 @@ def count_visitors(location: str, start_date: FromContext[date], end_date: FromC @Flow.model(context_type=DateRangeContext) def visitor_delta( - counts: list[Dep[int]], + counts: list[Dependency[int]], label: str, start_date: FromContext[date], end_date: FromContext[date], diff --git a/ccflow/flow_model.py b/ccflow/flow_model.py index 1f4c50c..94f22a5 100644 --- a/ccflow/flow_model.py +++ b/ccflow/flow_model.py @@ -8,7 +8,7 @@ context instead of model construction. * ``Lazy[T]`` marks a dependency that should be passed as a thunk and evaluated only if user code calls it. -* ``Dep[T]`` marks a nested regular-parameter slot that can be a literal ``T`` +* ``Dependency[T]`` marks a nested regular-parameter slot that can be a literal ``T`` or a ``CallableModel`` dependency returning ``T``. * ``model.flow.compute(...)`` and ``model.flow.with_context(...)`` provide the ergonomic execution and contextual binding API. @@ -90,7 +90,7 @@ from ._flow_model_binding import ( _UNION_ORIGINS, _UNSET, - Dep, + Dependency, FromContext, Lazy, _analyze_flow_context_transform, @@ -115,7 +115,7 @@ __all__ = ( "FlowAPI", "BoundModel", - "Dep", + "Dependency", "FlowInspection", "InputSpec", "FromContext", @@ -435,7 +435,7 @@ def _is_model_dependency(value: Any) -> bool: def _strip_outer_non_dep_annotated(annotation: Any) -> Any: - """Strip outer Annotated layers only until an explicit Dep marker is found.""" + """Strip outer Annotated layers only until an explicit Dependency marker is found.""" while get_origin(annotation) is Annotated: _, has_dep = _pop_dep_marker(annotation) @@ -1081,7 +1081,7 @@ def _walk_dep_marked_value( on_dict: Callable[[Any, List[Tuple[Any, Any]], Any, Any, Any, Tuple[Any, ...]], Any], path: Tuple[Any, ...] = (), ) -> Any: - """Walk only the container grammar where Dep markers are valid.""" + """Walk only the container grammar where Dependency markers are valid.""" annotation = _strip_outer_non_dep_annotated(annotation) dep_base, is_dep_slot = _pop_dep_marker(annotation) @@ -1183,7 +1183,7 @@ def _dep_slot_prefers_serialized_dependency(annotation: Any) -> bool: def _validate_dep_marked_value(name: str, value: Any, annotation: Any, source: str, path: Tuple[Any, ...] = ()) -> Any: - """Validate construction values that use explicit nested Dep markers.""" + """Validate construction values that use explicit nested Dependency markers.""" def validate_dep_slot(item: Any, dep_base: Any, item_path: Tuple[Any, ...]) -> Any: if _is_model_dependency(item): @@ -1233,7 +1233,7 @@ def validate_dict( def _resolve_dep_marked_value(name: str, value: Any, annotation: Any, context: ContextBase, path: Tuple[Any, ...] = ()) -> Any: - """Resolve CallableModel leaves that appear at explicit Dep marker slots.""" + """Resolve CallableModel leaves that appear at explicit Dependency marker slots.""" def resolve_dep_slot(item: Any, dep_base: Any, item_path: Tuple[Any, ...]) -> Any: if _is_model_dependency(item): @@ -1255,7 +1255,7 @@ def resolve_dep_slot(item: Any, dep_base: Any, item_path: Tuple[Any, ...]) -> An def _dep_marked_dependency_entries(value: Any, annotation: Any, context: ContextBase) -> GraphDepList: - """Collect dependency graph edges from explicit Dep marker slots.""" + """Collect dependency graph edges from explicit Dependency marker slots.""" def dep_slot_edges(item: Any, _dep_base: Any, _path: Tuple[Any, ...]) -> GraphDepList: if not _is_model_dependency(item): @@ -1288,7 +1288,7 @@ def _dep_marked_dependency_specs( *, trim_context: bool, ) -> Tuple[DependencySpec, ...]: - """Collect inspect-visible dependency edges from explicit Dep marker slots.""" + """Collect inspect-visible dependency edges from explicit Dependency marker slots.""" def dep_slot_specs(item: Any, _dep_base: Any, item_path: Tuple[Any, ...]) -> Tuple[DependencySpec, ...]: if not _is_model_dependency(item): @@ -1323,7 +1323,7 @@ def merge_items(items: Any) -> Tuple[DependencySpec, ...]: def _dep_marked_identity_value(value: Any, annotation: Any, context: ContextBase) -> Any: - """Return an identity payload with explicit Dep leaves replaced by dependencies.""" + """Return an identity payload with explicit Dependency leaves replaced by dependencies.""" def dep_slot_identity(item: Any, _dep_base: Any, _path: Tuple[Any, ...]) -> Any: if _is_model_dependency(item): @@ -3289,6 +3289,12 @@ def _generated_field_annotation(param: _FlowModelParam) -> Any: annotation = param.validation_annotation elif param.is_lazy: annotation = CallableModel + elif param.has_dep_slots: + # Dependency-marked containers may hold CallableModel leaves alongside + # literals. The leaf types are validated by ccflow, not pydantic, so use + # Any here to keep SkipValidation from emitting serializer warnings when a + # model leaf is serialized against the literal leaf type. + annotation = Any elif param.annotation is Any or param.annotation is inspect.Parameter.empty: annotation = Any else: diff --git a/ccflow/tests/test_flow_model.py b/ccflow/tests/test_flow_model.py index 939a0ae..afcbdc5 100644 --- a/ccflow/tests/test_flow_model.py +++ b/ccflow/tests/test_flow_model.py @@ -24,7 +24,7 @@ CallableModel, ContextBase, DateRangeContext, - Dep, + Dependency, EvaluatorBase, Flow, FlowContext, @@ -396,7 +396,7 @@ def source(value: FromContext[int], offset: int) -> int: return value + offset @Flow.model - def total(values: list[Dep[int]]) -> int: + def total(values: list[Dependency[int]]) -> int: calls["total"] += 1 return sum(values) @@ -414,7 +414,7 @@ def row_source(value: FromContext[int]) -> list[int]: return [value, value + 1] @Flow.model - def total(rows: list[Dep[list[int]]]) -> int: + def total(rows: list[Dependency[list[int]]]) -> int: return sum(sum(row) for row in rows) model = total(rows=([4, 5], row_source())) @@ -429,7 +429,7 @@ def list_source(value: FromContext[int]) -> list[int]: return [value, value * 2] @Flow.model - def total(values: list[Dep[int]]) -> int: + def total(values: list[Dependency[int]]) -> int: return sum(values) model = total(values=list_source()) @@ -445,7 +445,7 @@ def list_source(value: FromContext[int]) -> list[int]: return [value, value * 2] @Flow.model - def total(values: list[Dep[int]]) -> int: + def total(values: list[Dependency[int]]) -> int: return sum(values) for payload in ( @@ -469,7 +469,7 @@ def dict_source(value: FromContext[int]) -> dict[str, Any]: return {"x": value} @Flow.model - def consume(values: dict[str, Dep[Any]]) -> int: + def consume(values: dict[str, Dependency[Any]]) -> int: return values["x"] for payload in ( @@ -495,7 +495,7 @@ def list_source(value: FromContext[int]) -> list[int]: return [value, value * 2] @Flow.model - def total(values: list[Dep[int]]) -> int: + def total(values: list[Dependency[int]]) -> int: return sum(values) registry = ModelRegistry.root().clear() @@ -515,7 +515,7 @@ def source(value: FromContext[int], offset: int) -> int: return value + offset @Flow.model - def total(values: list[Dep[int]]) -> int: + def total(values: list[Dependency[int]]) -> int: return sum(values) model = total(values=[source(offset=5).model_dump(mode="python")]) @@ -532,7 +532,7 @@ def source(value: FromContext[int | None], offset: int) -> int | None: return value + offset @Flow.model - def total(values: list[Dep[int | None]]) -> int: + def total(values: list[Dependency[int | None]]) -> int: return sum(value or 0 for value in values) model = total(values=[source(offset=5), None, 2]) @@ -544,7 +544,7 @@ def total(values: list[Dep[int | None]]) -> int: def test_dep_marker_any_slot_keeps_non_restorable_serialized_looking_dicts(): @Flow.model - def collect(values: list[Dep[Any]]) -> list[Any]: + def collect(values: list[Dependency[Any]]) -> list[Any]: return values payloads = [ @@ -563,7 +563,7 @@ def source(value: FromContext[int], offset: int) -> int: return value + offset @Flow.model - def total(values: list[Dep[int]]) -> int: + def total(values: list[Dependency[int]]) -> int: return sum(values) payload = source(offset=5).model_dump(mode="python") @@ -580,7 +580,7 @@ def source(value: FromContext[int], offset: int) -> int: return value + offset @Flow.model - def collect(values: list[Dep[Any]]) -> list[Any]: + def collect(values: list[Dependency[Any]]) -> list[Any]: return values payload = source(offset=5).model_dump(mode="python", by_alias=by_alias) @@ -598,7 +598,7 @@ def source(value: FromContext[int], offset: int) -> int: return value + offset @Flow.model - def consume(items: list[Dep[dict[str, Any]]]) -> str: + def consume(items: list[Dependency[dict[str, Any]]]) -> str: return type(items[0]).__name__ payload = source(offset=5).model_dump(mode="python", by_alias=by_alias) @@ -614,7 +614,7 @@ def source(value: FromContext[int], offset: int) -> int: return value + offset @Flow.model - def total(values: list[Dep[int]]) -> int: + def total(values: list[Dependency[int]]) -> int: return sum(values) registry = ModelRegistry.root().clear() @@ -628,7 +628,7 @@ def total(values: list[Dep[int]]) -> int: def test_dep_marker_rejects_unknown_or_incompatible_registry_leaf_dependency(): @Flow.model - def total(values: list[Dep[int]]) -> int: + def total(values: list[Dependency[int]]) -> int: return sum(values) registry = ModelRegistry.root().clear() @@ -648,7 +648,7 @@ def source(value: FromContext[int], offset: int) -> int: return value + offset @Flow.model - def total(pair: tuple[Dep[int], str], values: dict[str, Dep[int]], many: tuple[Dep[int], ...]) -> int: + def total(pair: tuple[Dependency[int], str], values: dict[str, Dependency[int]], many: tuple[Dependency[int], ...]) -> int: return pair[0] + sum(values.values()) + sum(many) model = total( @@ -667,7 +667,7 @@ def source(value: FromContext[int], offset: int) -> int: return value + offset @Flow.model - def total(pair: tuple[Dep[int], str], values: dict[str, Dep[int]], many: tuple[Dep[int], ...]) -> int: + def total(pair: tuple[Dependency[int], str], values: dict[str, Dependency[int]], many: tuple[Dependency[int], ...]) -> int: return pair[0] + sum(values.values()) + sum(many) model = total( @@ -698,7 +698,7 @@ def middle(x: int) -> int: return x @Flow.model - def total(values: list[Dep[int]]) -> int: + def total(values: list[Dependency[int]]) -> int: return sum(values) model = total(values=[middle(x=leaf()), 2]) @@ -719,7 +719,7 @@ def source(value: FromContext[int], offset: int) -> int: return value + offset @Flow.model - def total(pair: tuple[Dep[int], str]) -> int: + def total(pair: tuple[Dependency[int], str]) -> int: return pair[0] with pytest.raises(TypeError, match="Field 'pair'"): @@ -737,7 +737,7 @@ def source(value: FromContext[int]) -> int: return value * 10 @Flow.model - def total(values: list[Dep[int]], bonus: FromContext[int]) -> int: + def total(values: list[Dependency[int]], bonus: FromContext[int]) -> int: calls["total"] += 1 return sum(values) + bonus @@ -759,7 +759,7 @@ def source(value: FromContext[int]) -> int: return value @Flow.model - def total(values: list[Dep[Annotated[int, Field(gt=0)]]]) -> int: + def total(values: list[Dependency[Annotated[int, Field(gt=0)]]]) -> int: return sum(values) assert total(values=["1", source()]).flow.compute(value=2).value == 3 @@ -781,7 +781,7 @@ def row_source(value: FromContext[int]) -> list[int]: return [value] @Flow.model - def row_total(rows: list[Dep[list[int]]]) -> int: + def row_total(rows: list[Dependency[list[int]]]) -> int: return sum(sum(row) for row in rows) with pytest.raises(TypeError, match="Field 'rows"): @@ -790,55 +790,55 @@ def row_total(rows: list[Dep[list[int]]]) -> int: with pytest.raises(TypeError, match="container values"): @Flow.model - def top_level(values: Dep[int]) -> int: + def top_level(values: Dependency[int]) -> int: return values - with pytest.raises(TypeError, match="cannot contain another Dep"): + with pytest.raises(TypeError, match="cannot contain another Dependency"): @Flow.model - def nested(values: list[Dep[list[Dep[int]]]]) -> int: + def nested(values: list[Dependency[list[Dependency[int]]]]) -> int: return sum(sum(value) for value in values) with pytest.raises(TypeError, match="dict keys"): @Flow.model - def dict_key(values: dict[Dep[str], int]) -> int: + def dict_key(values: dict[Dependency[str], int]) -> int: return sum(values.values()) with pytest.raises(TypeError, match="container values"): @Flow.model - def set_values(values: set[Dep[int]]) -> int: + def set_values(values: set[Dependency[int]]) -> int: return sum(values) with pytest.raises(TypeError, match="union"): @Flow.model - def optional_values(values: list[Dep[int]] | None) -> int: + def optional_values(values: list[Dependency[int]] | None) -> int: return 0 if values is None else sum(values) with pytest.raises(TypeError, match="Lazy.*FromContext|FromContext.*Lazy"): @Flow.model - def dep_from_context(values: list[Dep[FromContext[int]]]) -> int: + def dep_from_context(values: list[Dependency[FromContext[int]]]) -> int: return sum(values) with pytest.raises(TypeError, match="Lazy.*FromContext|FromContext.*Lazy"): @Flow.model - def dep_lazy(values: list[Dep[Lazy[int]]]) -> int: + def dep_lazy(values: list[Dependency[Lazy[int]]]) -> int: return sum(value() for value in values) - with pytest.raises(TypeError, match="cannot combine Dep.*FromContext"): + with pytest.raises(TypeError, match="cannot combine Dependency.*FromContext"): @Flow.model - def outer_from_context(values: FromContext[list[Dep[int]]]) -> int: + def outer_from_context(values: FromContext[list[Dependency[int]]]) -> int: return sum(values) - with pytest.raises(TypeError, match="cannot combine Dep.*Lazy"): + with pytest.raises(TypeError, match="cannot combine Dependency.*Lazy"): @Flow.model - def outer_lazy(values: Lazy[list[Dep[int]]]) -> int: + def outer_lazy(values: Lazy[list[Dependency[int]]]) -> int: return sum(values()) @@ -848,7 +848,7 @@ def source(value: FromContext[int]) -> int: return value class Plain(CallableModel): - values: list[Dep[int]] + values: list[Dependency[int]] @Flow.call def __call__(self, context: FlowContext) -> GenericResult[int]: @@ -1449,7 +1449,7 @@ def source(value: FromContext[int], offset: int) -> int: return value + offset @Flow.model - def total(values: list[Dep[int]]) -> int: + def total(values: list[Dependency[int]]) -> int: return sum(values) model = total(values=[source(offset=2), 3]) @@ -1740,7 +1740,7 @@ def source(value: FromContext[int], offset: int) -> int: return value + offset @Flow.model - def total(values: list[Dep[int]]) -> int: + def total(values: list[Dependency[int]]) -> int: return sum(values) model = total(values=[source(offset=1), 2]) diff --git a/docs/wiki/Flow-Model.md b/docs/wiki/Flow-Model.md index 4ad6020..bd74f96 100644 --- a/docs/wiki/Flow-Model.md +++ b/docs/wiki/Flow-Model.md @@ -117,16 +117,16 @@ assert model.flow.compute(value=7, b=12).value == 24 Direct `CallableModel` values bound to regular parameters are treated as upstream dependencies. Other literal values are bound inputs. Containers are ordinary literal values unless a nested position is explicitly marked with -`Dep[T]`. +`Dependency[T]`. ### Explicit Container Dependencies -`Dep[T]` marks an exact nested slot where a value may be either a literal `T` +`Dependency[T]` marks an exact nested slot where a value may be either a literal `T` or a `CallableModel` dependency whose unwrapped result validates as `T`. The function body still receives the resolved underlying value. ```python -from ccflow import Dep, Flow, FromContext +from ccflow import Dependency, Flow, FromContext @Flow.model @@ -135,7 +135,7 @@ def source(value: FromContext[int], offset: int) -> int: @Flow.model -def total(values: list[Dep[int]]) -> int: +def total(values: list[Dependency[int]]) -> int: return sum(values) @@ -155,7 +155,7 @@ total(values=source_list()) # valid: source_list supplies the whole list ``` So `list[int]` accepts a literal list or a model returning `list[int]`, while -`list[Dep[int]]` additionally accepts model leaves inside the literal list. +`list[Dependency[int]]` additionally accepts model leaves inside the literal list. Union annotations are allowed inside the marked slot: @@ -166,25 +166,25 @@ def maybe_source(value: FromContext[int | None]) -> int | None: @Flow.model -def total(values: list[Dep[int | None]]) -> int: +def total(values: list[Dependency[int | None]]) -> int: return sum(value or 0 for value in values) total(values=[maybe_source(), None, 2]) # valid: each list item is int | None ``` -`Dep[...]` is intentionally narrow: +`Dependency[...]` is intentionally narrow: - it is interpreted only for regular `@Flow.model` parameters, - it is supported inside `list`, `tuple`, and `dict` values, -- top-level `Dep[...]` is rejected because direct whole-parameter dependencies +- top-level `Dependency[...]` is rejected because direct whole-parameter dependencies already cover that case, - union annotations may appear inside the marked slot, such as - `list[Dep[int | None]]`, -- union annotations may not wrap a `Dep` marker, including optional container - forms like `list[Dep[int]] | None`, + `list[Dependency[int | None]]`, +- union annotations may not wrap a `Dependency` marker, including optional container + forms like `list[Dependency[int]] | None`, - it is not supported in dict keys, -- nested `Dep[...]` markers are rejected, +- nested `Dependency[...]` markers are rejected, - it does not add automatic behavior to handwritten `CallableModel` fields. ### Contextual Parameters From 31763eac625a9f8cd724b7d68c1999576b70eecd Mon Sep 17 00:00:00 2001 From: Tim Paine <3105306+timkpaine@users.noreply.github.com> Date: Fri, 10 Jul 2026 14:51:01 -0400 Subject: [PATCH 2/2] Align wiki with copier template: remove stale top-level contribute duplicates Signed-off-by: Tim Paine <3105306+timkpaine@users.noreply.github.com> --- docs/wiki/Build-from-Source.md | 103 --------------------------- docs/wiki/Contribute.md | 15 ---- docs/wiki/Local-Development-Setup.md | 52 -------------- 3 files changed, 170 deletions(-) delete mode 100644 docs/wiki/Build-from-Source.md delete mode 100644 docs/wiki/Contribute.md delete mode 100644 docs/wiki/Local-Development-Setup.md diff --git a/docs/wiki/Build-from-Source.md b/docs/wiki/Build-from-Source.md deleted file mode 100644 index 404cac2..0000000 --- a/docs/wiki/Build-from-Source.md +++ /dev/null @@ -1,103 +0,0 @@ -`ccflow` is written in Python. While prebuilt wheels are provided for end users, it is also straightforward to build `ccflow` from either the Python [source distribution](https://packaging.python.org/en/latest/specifications/source-distribution-format/) or the GitHub repository. - -- [Make commands](#make-commands) -- [Prerequisites](#prerequisites) -- [Clone](#clone) -- [Install Python dependencies](#install-python-dependencies) -- [Build](#build) -- [Lint and Autoformat](#lint-and-autoformat) -- [Testing](#testing) - -## Make commands - -As a convenience, `ccflow` uses a `Makefile` for commonly used commands. You can print the main available commands by running `make` with no arguments - -```bash -> make - -build build the library -clean clean the repository -fix run autofixers -install install library -lint run lints -test run the tests -``` - -## Prerequisites - -`ccflow` has a few system-level dependencies which you can install from your machine package manager. Other package managers like `conda`, `nix`, etc, should also work fine. - -## Clone - -Clone the repo with: - -```bash -git clone https://github.com/Point72/ccflow.git -cd ccflow -``` - -## Install Python dependencies - -Python build and develop dependencies are specified in the `pyproject.toml`, but you can manually install them: - -```bash -make requirements -``` - -Note that these dependencies would otherwise be installed normally as part of [PEP517](https://peps.python.org/pep-0517/) / [PEP518](https://peps.python.org/pep-0518/). - -## Build - -Build the python project in the usual manner: - -```bash -make build -``` - -## Lint and Autoformat - -`ccflow` has linting and auto formatting. - -| Language | Linter | Autoformatter | Description | -| :------- | :---------- | :------------ | :---------- | -| Python | `ruff` | `ruff` | Style | -| Markdown | `mdformat` | `mdformat` | Style | -| Markdown | `codespell` | | Spelling | - -**Python Linting** - -```bash -make lint-py -``` - -**Python Autoformatting** - -```bash -make fix-py -``` - -**Documentation Linting** - -```bash -make lint-docs -``` - -**Documentation Autoformatting** - -```bash -make fix-docs -``` - -## Testing - -`ccflow` has extensive Python tests. The tests can be run via `pytest`. First, install the Python development dependencies with - -```bash -make develop -``` - -**Python** - -```bash -make test -``` diff --git a/docs/wiki/Contribute.md b/docs/wiki/Contribute.md deleted file mode 100644 index ae149bb..0000000 --- a/docs/wiki/Contribute.md +++ /dev/null @@ -1,15 +0,0 @@ -Contributions are welcome on this project. We distribute under the terms of the [Apache 2.0 license](https://github.com/Point72/ccflow/blob/main/LICENSE). - -> [!NOTE] -> -> `ccflow` requires [Developer Certificate of Origin](https://en.wikipedia.org/wiki/Developer_Certificate_of_Origin) for all contributions. -> This is enforced by a [Probot GitHub App](https://probot.github.io/apps/dco/), which checks that commits are "signed". -> Read [instructions to configure commit signing](Local-Development-Setup#configure-commit-signing). - -For **bug reports** or **small feature requests**, please open an issue on our [issues page](https://github.com/Point72/ccflow/issues). - -For **questions** or to discuss **larger changes or features**, please use our [discussions page](https://github.com/Point72/ccflow/discussions). - -For **contributions**, please see our [developer documentation](Local-Development-Setup). We have `help wanted` and `good first issue` tags on our issues page, so these are a great place to start. - -For **documentation updates**, make PRs that update the pages in `/docs/wiki`. The documentation is pushed to the GitHub wiki automatically through a GitHub workflow. Note that direct updates to this wiki will be overwritten. diff --git a/docs/wiki/Local-Development-Setup.md b/docs/wiki/Local-Development-Setup.md deleted file mode 100644 index 262c7a2..0000000 --- a/docs/wiki/Local-Development-Setup.md +++ /dev/null @@ -1,52 +0,0 @@ -- [Step 1: Build from Source](#step-1-build-from-source) -- [Step 2: Configuring Git and GitHub for Development](#step-2-configuring-git-and-github-for-development) - - [Create your fork](#create-your-fork) - - [Configure remotes](#configure-remotes) - - [Authenticating with GitHub](#authenticating-with-github) -- [Guidelines](#guidelines) - -## Step 1: Build from Source - -To work on `ccflow`, you are going to need to build it from source. See -[Build from Source](Build-from-Source) for -detailed build instructions. - -Once you've built `ccflow` from a `git` clone, you will also need to -configure `git` and your GitHub account for `ccflow` development. - -## Step 2: Configuring Git and GitHub for Development - -### Create your fork - -The first step is to create a personal fork of `ccflow`. To do so, click -the "fork" button at https://github.com/Point72/ccflow, or just navigate -[here](https://github.com/Point72/ccflow/fork) in your browser. Set the -owner of the repository to your personal GitHub account if it is not -already set that way and click "Create fork". - -### Configure remotes - -Next, you should set some names for the `git` remotes corresponding to -main Point72 repository and your fork. See the [GitHub Docs](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/configuring-a-remote-repository-for-a-fork) for more information. - -### Authenticating with GitHub - -If you have not already configured `ssh` access to GitHub, you can find -instructions to do so -[here](https://docs.github.com/en/authentication/connecting-to-github-with-ssh), -including instructions to create an SSH key if you have not done -so. Authenticating with SSH is usually the easiest route. If you are working in -an environment that does not allow SSH connections to GitHub, you can look into -[configuring a hardware -passkey](https://docs.github.com/en/authentication/authenticating-with-a-passkey/about-passkeys) -or adding a [personal access -token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) -to avoid the need to type in your password every time you push to your fork. - -## Guidelines - -After developing a change locally, ensure that both [lints](Build-from-Source#lint-and-autoformat) and [tests](Build-from-Source#testing) pass. Commits should be squashed into logical units, and all commits must be signed (e.g. with the `-s` git flag). CSP requires [Developer Certificate of Origin](https://en.wikipedia.org/wiki/Developer_Certificate_of_Origin) for all contributions. - -If your work is still in-progress, open a [draft pull request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests). Otherwise, open a normal pull request. It might take a few days for a maintainer to review and provide feedback, so please be patient. If a maintainer asks for changes, please make said changes and squash your commits if necessary. If everything looks good to go, a maintainer will approve and merge your changes for inclusion in the next release. - -Please note that non substantive changes, large changes without prior discussion, etc, are not accepted and pull requests may be closed.