Skip to content

Commit 55bde97

Browse files
Add the node's function name to NodeSnapshot (#5663)
* Add the node's function name to NodeSnapshot Signed-off-by: Jitendra Gundaniya <jitendra_gundaniya@mckinsey.com> * Make func_name keyword-only in NodeSnapshot and add tests for its behavior Signed-off-by: Jitendra Gundaniya <jitendra_gundaniya@mckinsey.com> * Enforce func_name as a keyword-only argument in NodeSnapshot and add a test for TypeError Signed-off-by: Jitendra Gundaniya <jitendra_gundaniya@mckinsey.com> --------- Signed-off-by: Jitendra Gundaniya <jitendra_gundaniya@mckinsey.com> Co-authored-by: Ravi Kumar Pilla <ravi_kumar_pilla@mckinsey.com>
1 parent aec6a84 commit 55bde97

9 files changed

Lines changed: 42 additions & 4 deletions

File tree

RELEASE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Upcoming Release
22
## Major features and improvements
3+
* Added the node function name to project inspection snapshots as `NodeSnapshot.func_name`.
4+
35
## Bug fixes and other changes
46
## Documentation changes
57
## Community contributions

docs/extend/serving.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ curl http://127.0.0.1:8000/snapshot
8181
"nodes": [
8282
{
8383
"name": "split_data_node",
84+
"func_name": "split_data",
8485
"inputs": ["example_iris_data"],
8586
"outputs": ["X_train", "X_test"],
8687
"tags": [],

docs/inspect/inspect-project.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,14 @@ default_pipeline = next(p for p in snapshot.pipelines if p.name == "__default__"
9090

9191
for node in default_pipeline.nodes:
9292
print(node.name)
93+
print(" func_name:", node.func_name)
9394
print(" namespace:", node.namespace)
9495
print(" inputs: ", node.inputs)
9596
print(" outputs: ", node.outputs)
9697
print(" tags: ", node.tags)
9798
```
9899

99-
Each node is represented as a [`NodeSnapshot`][kedro.inspection.models.NodeSnapshot] with `name`, `namespace`, `inputs`, `outputs`, and `tags` fields.
100+
Each node is represented as a [`NodeSnapshot`][kedro.inspection.models.NodeSnapshot] with `name`, `func_name`, `namespace`, `inputs`, `outputs`, and `tags` fields.
100101

101102
## How to inspect catalog datasets
102103

kedro/inspection/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,15 @@ class NodeSnapshot:
5757
5858
Attributes:
5959
name: Fully-qualified node name (includes namespace prefix if present).
60+
func_name: Readable name of the node's underlying function.
6061
namespace: Node namespace, or ``None`` if the node has no namespace.
6162
tags: Sorted list of tags assigned to the node.
6263
inputs: Ordered list of input dataset names.
6364
outputs: Ordered list of output dataset names.
6465
"""
6566

6667
name: str
68+
func_name: str = field(kw_only=True)
6769
namespace: str | None = None
6870
tags: list[str] = field(default_factory=list)
6971
inputs: list[str] = field(default_factory=list)

kedro/inspection/snapshot.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ def _node_to_snapshot(node: Node) -> NodeSnapshot:
7979
"""
8080
return NodeSnapshot(
8181
name=node.name,
82+
func_name=node._func_name, # Matches Node.__str__ and registry describe.
8283
namespace=node.namespace,
8384
tags=sorted(node.tags),
8485
inputs=node.inputs,

tests/inspection/test_helper.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313

1414
def _make_node_snapshot(inputs: list[str], outputs: list[str]) -> NodeSnapshot:
15-
return NodeSnapshot(name="n", inputs=inputs, outputs=outputs)
15+
return NodeSnapshot(name="n", func_name="identity", inputs=inputs, outputs=outputs)
1616

1717

1818
def _make_pipeline_snapshot(nodes: list[NodeSnapshot]) -> PipelineSnapshot:

tests/inspection/test_node_pipeline_snapshot.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
from __future__ import annotations
44

5+
from functools import partial
6+
57
import pytest
68

79
from kedro.inspection.models import NodeSnapshot, PipelineSnapshot
@@ -42,18 +44,28 @@ def simple_pipeline(simple_node):
4244

4345
class TestNodeSnapshot:
4446
def test_instantiation_defaults(self):
45-
snapshot = NodeSnapshot(name="my_node")
47+
snapshot = NodeSnapshot(name="my_node", func_name="my_func")
4648
assert snapshot.name == "my_node"
49+
assert snapshot.func_name == "my_func"
4750
assert snapshot.namespace is None
4851
assert snapshot.tags == []
4952
assert snapshot.inputs == []
5053
assert snapshot.outputs == []
5154

55+
def test_func_name_is_keyword_only(self):
56+
snapshot = NodeSnapshot("my_node", "my_namespace", func_name="my_func")
57+
assert snapshot.func_name == "my_func"
58+
assert snapshot.namespace == "my_namespace"
59+
60+
with pytest.raises(TypeError, match="func_name"):
61+
NodeSnapshot("my_node", "my_namespace")
62+
5263

5364
class TestNodeToSnapshot:
5465
def test_populates_all_fields(self, simple_node):
5566
snapshot = _node_to_snapshot(simple_node)
5667
assert snapshot.name == simple_node.name
68+
assert snapshot.func_name == "_identity"
5769
assert snapshot.namespace == simple_node.namespace
5870
assert snapshot.inputs == simple_node.inputs
5971
assert snapshot.outputs == simple_node.outputs
@@ -69,10 +81,26 @@ def test_namespace_populated(self, namespaced_node):
6981
def test_returns_node_snapshot_instance(self, simple_node):
7082
assert isinstance(_node_to_snapshot(simple_node), NodeSnapshot)
7183

84+
def test_named_partial_func_name_warns(self):
85+
partial_node = node(
86+
partial(_identity),
87+
inputs="raw",
88+
outputs="processed",
89+
name="partial_node",
90+
)
91+
92+
with pytest.warns(UserWarning, match="made from a 'partial' function"):
93+
snapshot = _node_to_snapshot(partial_node)
94+
95+
assert snapshot.name == "partial_node"
96+
assert snapshot.func_name == "<partial>"
97+
7298

7399
class TestPipelineSnapshot:
74100
def test_instantiation(self):
75-
node_snap = NodeSnapshot(name="n", inputs=["a"], outputs=["b"])
101+
node_snap = NodeSnapshot(
102+
name="n", func_name="identity", inputs=["a"], outputs=["b"]
103+
)
76104
snapshot = PipelineSnapshot(name="my_pipe", nodes=[node_snap])
77105
assert snapshot.name == "my_pipe"
78106
assert snapshot.nodes == [node_snap]

tests/inspection/test_project_snapshot.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ def metadata_snapshot():
4343
def node_snapshot():
4444
return NodeSnapshot(
4545
name="node_a",
46+
func_name="process_companies",
4647
inputs=["companies"],
4748
outputs=["processed_companies"],
4849
)

tests/server/test_snapshot_endpoint.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def _make_snapshot() -> ProjectSnapshot:
2323
nodes=[
2424
NodeSnapshot(
2525
name="my_node",
26+
func_name="process_data",
2627
namespace="ns",
2728
tags=["tag1"],
2829
inputs=["raw_data"],
@@ -76,6 +77,7 @@ def test_snapshot_response_contains_all_fields(self, mocker, make_http_server):
7677
assert len(pipelines) == 1
7778
assert pipelines[0]["name"] == "__default__"
7879
assert pipelines[0]["nodes"][0]["name"] == "my_node"
80+
assert pipelines[0]["nodes"][0]["func_name"] == "process_data"
7981

8082
assert "raw_data" in payload["datasets"]
8183
assert payload["datasets"]["raw_data"]["type"] == "pandas.CSVDataset"

0 commit comments

Comments
 (0)