some more for standard lib
This commit is contained in:
@@ -21,8 +21,12 @@ from .ops import (
|
||||
ConstantInput,
|
||||
CountOutput,
|
||||
ItemOutput,
|
||||
MappingOutput,
|
||||
MaybeItemOutput,
|
||||
PickPathInput,
|
||||
PickKeyInput,
|
||||
ProjectFieldsInput,
|
||||
RenameFieldsInput,
|
||||
RuntimeErrorInput,
|
||||
SequenceInput,
|
||||
TruthyInput,
|
||||
@@ -37,7 +41,10 @@ from .ops import (
|
||||
last_item,
|
||||
last_item_or_none,
|
||||
length,
|
||||
pick_path,
|
||||
pick_key,
|
||||
project_fields,
|
||||
rename_fields,
|
||||
runtime_error,
|
||||
truthy,
|
||||
)
|
||||
@@ -64,8 +71,12 @@ __all__ = [
|
||||
"CountOutput",
|
||||
"GraphPath",
|
||||
"ItemOutput",
|
||||
"MappingOutput",
|
||||
"MaybeItemOutput",
|
||||
"PickKeyInput",
|
||||
"PickPathInput",
|
||||
"ProjectFieldsInput",
|
||||
"RenameFieldsInput",
|
||||
"RuntimeErrorInput",
|
||||
"NodeReturn",
|
||||
"NodeSpec",
|
||||
@@ -100,6 +111,9 @@ __all__ = [
|
||||
"last_item_or_none",
|
||||
"length",
|
||||
"pick_key",
|
||||
"pick_path",
|
||||
"project_fields",
|
||||
"rename_fields",
|
||||
"runtime_error",
|
||||
"node",
|
||||
"outcome",
|
||||
|
||||
@@ -15,14 +15,21 @@ from .sequences import (
|
||||
from .values import (
|
||||
CoalesceInput,
|
||||
ConstantInput,
|
||||
MappingOutput,
|
||||
PickPathInput,
|
||||
PickKeyInput,
|
||||
ProjectFieldsInput,
|
||||
RenameFieldsInput,
|
||||
RuntimeErrorInput,
|
||||
TruthyInput,
|
||||
ValueOutput,
|
||||
coalesce,
|
||||
constant,
|
||||
default_if_none,
|
||||
pick_path,
|
||||
pick_key,
|
||||
project_fields,
|
||||
rename_fields,
|
||||
runtime_error,
|
||||
truthy,
|
||||
)
|
||||
@@ -33,8 +40,12 @@ __all__ = [
|
||||
"ConstantInput",
|
||||
"CountOutput",
|
||||
"ItemOutput",
|
||||
"MappingOutput",
|
||||
"MaybeItemOutput",
|
||||
"PickPathInput",
|
||||
"PickKeyInput",
|
||||
"ProjectFieldsInput",
|
||||
"RenameFieldsInput",
|
||||
"RuntimeErrorInput",
|
||||
"SequenceInput",
|
||||
"TruthyInput",
|
||||
@@ -49,7 +60,10 @@ __all__ = [
|
||||
"last_item",
|
||||
"last_item_or_none",
|
||||
"length",
|
||||
"pick_path",
|
||||
"pick_key",
|
||||
"project_fields",
|
||||
"rename_fields",
|
||||
"runtime_error",
|
||||
"truthy",
|
||||
]
|
||||
|
||||
@@ -33,6 +33,33 @@ class PickKeyInput(BaseModel):
|
||||
key: str
|
||||
|
||||
|
||||
class PickPathInput(BaseModel):
|
||||
"""Input model for selecting a nested mapping value by dotted path."""
|
||||
|
||||
mapping: dict[str, Any]
|
||||
path: str
|
||||
|
||||
|
||||
class MappingOutput(BaseModel):
|
||||
"""Output model for ops that emit a mapping."""
|
||||
|
||||
mapping: dict[str, Any]
|
||||
|
||||
|
||||
class ProjectFieldsInput(BaseModel):
|
||||
"""Input model for selecting named fields from a mapping."""
|
||||
|
||||
mapping: dict[str, Any]
|
||||
fields: list[str]
|
||||
|
||||
|
||||
class RenameFieldsInput(BaseModel):
|
||||
"""Input model for remapping existing mapping keys."""
|
||||
|
||||
mapping: dict[str, Any]
|
||||
renames: dict[str, str]
|
||||
|
||||
|
||||
class TruthyInput(BaseModel):
|
||||
"""Input model for routing by Python truthiness."""
|
||||
|
||||
@@ -87,6 +114,54 @@ def pick_key(input: PickKeyInput) -> ValueOutput:
|
||||
return ValueOutput(value=input.mapping.get(input.key))
|
||||
|
||||
|
||||
@node(
|
||||
name="authoring.pick_path",
|
||||
input_model=PickPathInput,
|
||||
output_model=ValueOutput,
|
||||
description="Select a nested mapping value by dotted path, returning None if missing.",
|
||||
)
|
||||
def pick_path(input: PickPathInput) -> ValueOutput:
|
||||
"""Select a nested mapping value by dotted path, returning None if missing."""
|
||||
current: Any = input.mapping
|
||||
for part in input.path.split("."):
|
||||
if not isinstance(current, dict):
|
||||
return ValueOutput(value=None)
|
||||
current = current.get(part)
|
||||
if current is None:
|
||||
return ValueOutput(value=None)
|
||||
return ValueOutput(value=current)
|
||||
|
||||
|
||||
@node(
|
||||
name="authoring.project_fields",
|
||||
input_model=ProjectFieldsInput,
|
||||
output_model=MappingOutput,
|
||||
description="Return only the requested existing fields from a mapping.",
|
||||
)
|
||||
def project_fields(input: ProjectFieldsInput) -> MappingOutput:
|
||||
"""Return only the requested existing fields from a mapping."""
|
||||
return MappingOutput(
|
||||
mapping={field: input.mapping[field] for field in input.fields if field in input.mapping}
|
||||
)
|
||||
|
||||
|
||||
@node(
|
||||
name="authoring.rename_fields",
|
||||
input_model=RenameFieldsInput,
|
||||
output_model=MappingOutput,
|
||||
description="Rename existing mapping fields and omit missing source keys.",
|
||||
)
|
||||
def rename_fields(input: RenameFieldsInput) -> MappingOutput:
|
||||
"""Rename existing mapping fields and omit missing source keys."""
|
||||
return MappingOutput(
|
||||
mapping={
|
||||
target: input.mapping[source]
|
||||
for source, target in input.renames.items()
|
||||
if source in input.mapping
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@node(
|
||||
name="authoring.truthy",
|
||||
input_model=TruthyInput,
|
||||
|
||||
@@ -18,7 +18,10 @@ from wf_authoring import (
|
||||
last_item_or_none,
|
||||
length,
|
||||
node,
|
||||
pick_path,
|
||||
pick_key,
|
||||
project_fields,
|
||||
rename_fields,
|
||||
runtime_error,
|
||||
truthy,
|
||||
)
|
||||
@@ -43,6 +46,9 @@ AUTHORING_STD_SPECS: tuple[NodeSpec[Any, Any], ...] = (
|
||||
default_if_none,
|
||||
constant,
|
||||
pick_key,
|
||||
pick_path,
|
||||
project_fields,
|
||||
rename_fields,
|
||||
truthy,
|
||||
runtime_error,
|
||||
first_item,
|
||||
|
||||
@@ -17,7 +17,10 @@ from wf_authoring import (
|
||||
last_item,
|
||||
last_item_or_none,
|
||||
length,
|
||||
pick_path,
|
||||
pick_key,
|
||||
project_fields,
|
||||
rename_fields,
|
||||
runtime_error,
|
||||
state_path,
|
||||
truthy,
|
||||
@@ -250,6 +253,45 @@ def test_pick_key_returns_none_when_missing() -> None:
|
||||
assert result == {"outcome": "ok", "output": {"value": None}}
|
||||
|
||||
|
||||
def test_pick_path_selects_nested_value() -> None:
|
||||
registry = build_registry(pick_path)
|
||||
|
||||
result = registry["authoring.pick_path"](
|
||||
{"mapping": {"result": {"status": "done"}}, "path": "result.status"},
|
||||
RuntimeContext(current_node_id="pick_path"),
|
||||
)
|
||||
|
||||
assert result == {"outcome": "ok", "output": {"value": "done"}}
|
||||
|
||||
|
||||
def test_project_fields_selects_named_keys() -> None:
|
||||
registry = build_registry(project_fields)
|
||||
|
||||
result = registry["authoring.project_fields"](
|
||||
{"mapping": {"status": "done", "message": "ok", "debug": True}, "fields": ["status", "message"]},
|
||||
RuntimeContext(current_node_id="project_fields"),
|
||||
)
|
||||
|
||||
assert result == {
|
||||
"outcome": "ok",
|
||||
"output": {"mapping": {"status": "done", "message": "ok"}},
|
||||
}
|
||||
|
||||
|
||||
def test_rename_fields_remaps_existing_keys() -> None:
|
||||
registry = build_registry(rename_fields)
|
||||
|
||||
result = registry["authoring.rename_fields"](
|
||||
{"mapping": {"provider_status": "done", "provider_message": "ok"}, "renames": {"provider_status": "status", "provider_message": "message"}},
|
||||
RuntimeContext(current_node_id="rename_fields"),
|
||||
)
|
||||
|
||||
assert result == {
|
||||
"outcome": "ok",
|
||||
"output": {"mapping": {"status": "done", "message": "ok"}},
|
||||
}
|
||||
|
||||
|
||||
def test_truthy_routes_truthy_and_falsey_outcomes() -> None:
|
||||
registry = build_registry(truthy)
|
||||
ctx = RuntimeContext(current_node_id="truthy")
|
||||
|
||||
@@ -125,6 +125,9 @@ def test_wf_std_source_contains_authoring_ops() -> None:
|
||||
"wf.std.default_if_none",
|
||||
"wf.std.constant",
|
||||
"wf.std.pick_key",
|
||||
"wf.std.pick_path",
|
||||
"wf.std.project_fields",
|
||||
"wf.std.rename_fields",
|
||||
"wf.std.truthy",
|
||||
"wf.std.runtime_error",
|
||||
"wf.std.first_item",
|
||||
|
||||
Reference in New Issue
Block a user