some more for standard lib
This commit is contained in:
@@ -21,8 +21,12 @@ from .ops import (
|
|||||||
ConstantInput,
|
ConstantInput,
|
||||||
CountOutput,
|
CountOutput,
|
||||||
ItemOutput,
|
ItemOutput,
|
||||||
|
MappingOutput,
|
||||||
MaybeItemOutput,
|
MaybeItemOutput,
|
||||||
|
PickPathInput,
|
||||||
PickKeyInput,
|
PickKeyInput,
|
||||||
|
ProjectFieldsInput,
|
||||||
|
RenameFieldsInput,
|
||||||
RuntimeErrorInput,
|
RuntimeErrorInput,
|
||||||
SequenceInput,
|
SequenceInput,
|
||||||
TruthyInput,
|
TruthyInput,
|
||||||
@@ -37,7 +41,10 @@ from .ops import (
|
|||||||
last_item,
|
last_item,
|
||||||
last_item_or_none,
|
last_item_or_none,
|
||||||
length,
|
length,
|
||||||
|
pick_path,
|
||||||
pick_key,
|
pick_key,
|
||||||
|
project_fields,
|
||||||
|
rename_fields,
|
||||||
runtime_error,
|
runtime_error,
|
||||||
truthy,
|
truthy,
|
||||||
)
|
)
|
||||||
@@ -64,8 +71,12 @@ __all__ = [
|
|||||||
"CountOutput",
|
"CountOutput",
|
||||||
"GraphPath",
|
"GraphPath",
|
||||||
"ItemOutput",
|
"ItemOutput",
|
||||||
|
"MappingOutput",
|
||||||
"MaybeItemOutput",
|
"MaybeItemOutput",
|
||||||
"PickKeyInput",
|
"PickKeyInput",
|
||||||
|
"PickPathInput",
|
||||||
|
"ProjectFieldsInput",
|
||||||
|
"RenameFieldsInput",
|
||||||
"RuntimeErrorInput",
|
"RuntimeErrorInput",
|
||||||
"NodeReturn",
|
"NodeReturn",
|
||||||
"NodeSpec",
|
"NodeSpec",
|
||||||
@@ -100,6 +111,9 @@ __all__ = [
|
|||||||
"last_item_or_none",
|
"last_item_or_none",
|
||||||
"length",
|
"length",
|
||||||
"pick_key",
|
"pick_key",
|
||||||
|
"pick_path",
|
||||||
|
"project_fields",
|
||||||
|
"rename_fields",
|
||||||
"runtime_error",
|
"runtime_error",
|
||||||
"node",
|
"node",
|
||||||
"outcome",
|
"outcome",
|
||||||
|
|||||||
@@ -15,14 +15,21 @@ from .sequences import (
|
|||||||
from .values import (
|
from .values import (
|
||||||
CoalesceInput,
|
CoalesceInput,
|
||||||
ConstantInput,
|
ConstantInput,
|
||||||
|
MappingOutput,
|
||||||
|
PickPathInput,
|
||||||
PickKeyInput,
|
PickKeyInput,
|
||||||
|
ProjectFieldsInput,
|
||||||
|
RenameFieldsInput,
|
||||||
RuntimeErrorInput,
|
RuntimeErrorInput,
|
||||||
TruthyInput,
|
TruthyInput,
|
||||||
ValueOutput,
|
ValueOutput,
|
||||||
coalesce,
|
coalesce,
|
||||||
constant,
|
constant,
|
||||||
default_if_none,
|
default_if_none,
|
||||||
|
pick_path,
|
||||||
pick_key,
|
pick_key,
|
||||||
|
project_fields,
|
||||||
|
rename_fields,
|
||||||
runtime_error,
|
runtime_error,
|
||||||
truthy,
|
truthy,
|
||||||
)
|
)
|
||||||
@@ -33,8 +40,12 @@ __all__ = [
|
|||||||
"ConstantInput",
|
"ConstantInput",
|
||||||
"CountOutput",
|
"CountOutput",
|
||||||
"ItemOutput",
|
"ItemOutput",
|
||||||
|
"MappingOutput",
|
||||||
"MaybeItemOutput",
|
"MaybeItemOutput",
|
||||||
|
"PickPathInput",
|
||||||
"PickKeyInput",
|
"PickKeyInput",
|
||||||
|
"ProjectFieldsInput",
|
||||||
|
"RenameFieldsInput",
|
||||||
"RuntimeErrorInput",
|
"RuntimeErrorInput",
|
||||||
"SequenceInput",
|
"SequenceInput",
|
||||||
"TruthyInput",
|
"TruthyInput",
|
||||||
@@ -49,7 +60,10 @@ __all__ = [
|
|||||||
"last_item",
|
"last_item",
|
||||||
"last_item_or_none",
|
"last_item_or_none",
|
||||||
"length",
|
"length",
|
||||||
|
"pick_path",
|
||||||
"pick_key",
|
"pick_key",
|
||||||
|
"project_fields",
|
||||||
|
"rename_fields",
|
||||||
"runtime_error",
|
"runtime_error",
|
||||||
"truthy",
|
"truthy",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -33,6 +33,33 @@ class PickKeyInput(BaseModel):
|
|||||||
key: str
|
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):
|
class TruthyInput(BaseModel):
|
||||||
"""Input model for routing by Python truthiness."""
|
"""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))
|
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(
|
@node(
|
||||||
name="authoring.truthy",
|
name="authoring.truthy",
|
||||||
input_model=TruthyInput,
|
input_model=TruthyInput,
|
||||||
|
|||||||
@@ -18,7 +18,10 @@ from wf_authoring import (
|
|||||||
last_item_or_none,
|
last_item_or_none,
|
||||||
length,
|
length,
|
||||||
node,
|
node,
|
||||||
|
pick_path,
|
||||||
pick_key,
|
pick_key,
|
||||||
|
project_fields,
|
||||||
|
rename_fields,
|
||||||
runtime_error,
|
runtime_error,
|
||||||
truthy,
|
truthy,
|
||||||
)
|
)
|
||||||
@@ -43,6 +46,9 @@ AUTHORING_STD_SPECS: tuple[NodeSpec[Any, Any], ...] = (
|
|||||||
default_if_none,
|
default_if_none,
|
||||||
constant,
|
constant,
|
||||||
pick_key,
|
pick_key,
|
||||||
|
pick_path,
|
||||||
|
project_fields,
|
||||||
|
rename_fields,
|
||||||
truthy,
|
truthy,
|
||||||
runtime_error,
|
runtime_error,
|
||||||
first_item,
|
first_item,
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ from wf_authoring import (
|
|||||||
last_item,
|
last_item,
|
||||||
last_item_or_none,
|
last_item_or_none,
|
||||||
length,
|
length,
|
||||||
|
pick_path,
|
||||||
pick_key,
|
pick_key,
|
||||||
|
project_fields,
|
||||||
|
rename_fields,
|
||||||
runtime_error,
|
runtime_error,
|
||||||
state_path,
|
state_path,
|
||||||
truthy,
|
truthy,
|
||||||
@@ -250,6 +253,45 @@ def test_pick_key_returns_none_when_missing() -> None:
|
|||||||
assert result == {"outcome": "ok", "output": {"value": 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:
|
def test_truthy_routes_truthy_and_falsey_outcomes() -> None:
|
||||||
registry = build_registry(truthy)
|
registry = build_registry(truthy)
|
||||||
ctx = RuntimeContext(current_node_id="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.default_if_none",
|
||||||
"wf.std.constant",
|
"wf.std.constant",
|
||||||
"wf.std.pick_key",
|
"wf.std.pick_key",
|
||||||
|
"wf.std.pick_path",
|
||||||
|
"wf.std.project_fields",
|
||||||
|
"wf.std.rename_fields",
|
||||||
"wf.std.truthy",
|
"wf.std.truthy",
|
||||||
"wf.std.runtime_error",
|
"wf.std.runtime_error",
|
||||||
"wf.std.first_item",
|
"wf.std.first_item",
|
||||||
|
|||||||
Reference in New Issue
Block a user