docworkspace 0.2.8__tar.gz → 0.2.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {docworkspace-0.2.8 → docworkspace-0.2.9}/PKG-INFO +1 -1
- {docworkspace-0.2.8 → docworkspace-0.2.9}/pyproject.toml +1 -1
- {docworkspace-0.2.8 → docworkspace-0.2.9}/src/docworkspace/__init__.py +1 -1
- {docworkspace-0.2.8 → docworkspace-0.2.9}/src/docworkspace/workspace/analysis.py +14 -1
- {docworkspace-0.2.8 → docworkspace-0.2.9}/tests/test_workspace.py +32 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/uv.lock +1 -1
- {docworkspace-0.2.8 → docworkspace-0.2.9}/.github/workflows/ci.yml +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/.github/workflows/release.yml +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/.gitignore +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/PUBLISH.md +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/README.md +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/pytest.ini +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/src/docworkspace/node/__init__.py +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/src/docworkspace/node/core.py +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/src/docworkspace/node/io.py +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/src/docworkspace/workspace/__init__.py +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/src/docworkspace/workspace/core.py +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/src/docworkspace/workspace/io.py +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/tests/conftest.py +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/tests/test_fastapi_integration.py +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/tests/test_node.py +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/tests/test_node_io.py +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/tests/test_simple_operations.py +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/tests/test_workspace_io_absolute_paths.py +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/tests/test_workspace_serialization_types.py +0 -0
- {docworkspace-0.2.8 → docworkspace-0.2.9}/tests/test_workspace_shim.py +0 -0
|
@@ -7,5 +7,5 @@ serialization, analysis, and graph helpers in dedicated submodules.
|
|
|
7
7
|
from .node import DerivedColumnMeta, Node # package exposing Node
|
|
8
8
|
from .workspace import Workspace # shim -> workspace.core.Workspace
|
|
9
9
|
|
|
10
|
-
__version__ = "0.2.
|
|
10
|
+
__version__ = "0.2.9"
|
|
11
11
|
__all__ = ["Workspace", "Node", "DerivedColumnMeta"]
|
|
@@ -34,7 +34,20 @@ def graph_json(workspace: "Workspace") -> Dict[str, object]:
|
|
|
34
34
|
edges_payload: List[Dict[str, str]] = []
|
|
35
35
|
|
|
36
36
|
for node in workspace.nodes.values():
|
|
37
|
-
|
|
37
|
+
try:
|
|
38
|
+
nodes_payload.append(node.info())
|
|
39
|
+
except Exception as exc:
|
|
40
|
+
# Per-node fallback: one broken node (e.g. missing source file,
|
|
41
|
+
# undeserializable lazy plan) must not take down the whole graph.
|
|
42
|
+
nodes_payload.append(
|
|
43
|
+
{
|
|
44
|
+
"id": node.id,
|
|
45
|
+
"name": getattr(node, "name", node.id),
|
|
46
|
+
"operation": getattr(node, "operation", "unknown"),
|
|
47
|
+
"child_ids": [c.id for c in getattr(node, "children", [])],
|
|
48
|
+
"error": f"{type(exc).__name__}: {exc}",
|
|
49
|
+
}
|
|
50
|
+
)
|
|
38
51
|
|
|
39
52
|
for child in node.children:
|
|
40
53
|
edges_payload.append({"source": node.id, "target": child.id})
|
|
@@ -431,6 +431,38 @@ class TestWorkspaceGraphOperations:
|
|
|
431
431
|
for field in required_fields:
|
|
432
432
|
assert field in node_data
|
|
433
433
|
|
|
434
|
+
def test_workspace_graph_survives_broken_node_info(self):
|
|
435
|
+
"""One node failing `info()` must not break the whole graph payload."""
|
|
436
|
+
workspace = Workspace("graph_resilience")
|
|
437
|
+
good_node = Node(
|
|
438
|
+
data=pl.DataFrame({"x": [1, 2, 3]}).lazy(),
|
|
439
|
+
name="good",
|
|
440
|
+
workspace=workspace,
|
|
441
|
+
)
|
|
442
|
+
bad_node = Node(
|
|
443
|
+
data=pl.DataFrame({"y": [4, 5]}).lazy(),
|
|
444
|
+
name="bad",
|
|
445
|
+
workspace=workspace,
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
# Simulate a broken lazy plan / missing source file: info() raises.
|
|
449
|
+
def _boom() -> dict:
|
|
450
|
+
raise RuntimeError("source parquet missing")
|
|
451
|
+
|
|
452
|
+
bad_node.info = _boom # type: ignore[method-assign]
|
|
453
|
+
|
|
454
|
+
graph_data = workspace.graph_json()
|
|
455
|
+
|
|
456
|
+
nodes_by_id = {n["id"]: n for n in graph_data["nodes"]}
|
|
457
|
+
assert good_node.id in nodes_by_id
|
|
458
|
+
assert bad_node.id in nodes_by_id
|
|
459
|
+
# Healthy node still carries its real info.
|
|
460
|
+
assert "shape" in nodes_by_id[good_node.id]
|
|
461
|
+
# Broken node carries an error envelope plus identity fields.
|
|
462
|
+
assert nodes_by_id[bad_node.id]["name"] == "bad"
|
|
463
|
+
assert "error" in nodes_by_id[bad_node.id]
|
|
464
|
+
assert "RuntimeError" in nodes_by_id[bad_node.id]["error"]
|
|
465
|
+
|
|
434
466
|
def test_workspace_with_initial_data_loading(self):
|
|
435
467
|
"""Test explicit initial data loading after creating an empty workspace."""
|
|
436
468
|
# Test with DataFrame converted to LazyFrame before creating a Node.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|