stepfunction 0.0.7__py3-none-any.whl → 0.2.0__py3-none-any.whl

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.
File without changes
@@ -2,14 +2,11 @@
2
2
 
3
3
  # Render Configuration
4
4
 
5
- DEFAULT_VISUALIZER_EXTENSION = "gv"
6
- """str: The default extension for the visualizer renderer."""
5
+ DEFAULT_VISUALIZER_DIRECTION = "TD"
6
+ """str: The default flowchart direction for the Mermaid visualizer (e.g. TD, LR)."""
7
7
 
8
- DEFAULT_VISUALIZER_FORMAT = "png"
9
- """str: The default format for the visualizer renderer."""
10
-
11
- DEFAULT_VISUALIZER_RENDERER = "cairo"
12
- """str: The default renderer for the visualizer."""
8
+ DEFAULT_VISUALIZER_EXTENSION = "mmd"
9
+ """str: The default file extension for the visualizer renderer output."""
13
10
 
14
11
  DEFAULT_VISUALIZER_FOLDER = "workflow_renders"
15
12
  """str: The default folder where visualizer renders are stored."""
@@ -22,26 +19,35 @@ DEFAULT_VISUALIZER_STRING_ENCODING = "utf-8"
22
19
  DEFAULT_VISUALIZER_SUCCESS_EDGE_LABEL = "Success"
23
20
  """str: The default edge label for success transitions in the visualizer."""
24
21
 
25
- DEFAULT_VISUALIZER_SUCCESS_EDGE_COLOR = "black"
26
- """str: The default edge color for success transitions in the visualizer."""
27
-
28
22
  DEFAULT_VISUALIZER_FAILURE_EDGE_LABEL = "Failure"
29
23
  """str: The default edge label for failure transitions in the visualizer."""
30
24
 
31
- DEFAULT_VISUALIZER_FAILURE_EDGE_COLOR = "black"
32
- """str: The default edge color for failure transitions in the visualizer."""
33
-
34
25
  DEFAULT_VISUALIZER_STOP_ON_FAILURE_EDGE_LABEL = "Stop on Failure"
35
26
  """str: The default edge label for stop on failure transitions in the visualizer."""
36
27
 
37
28
  DEFAULT_VISUALIZER_STOP_ON_FAILURE_EDGE_COLOR = "red"
38
29
  """str: The default edge color for stop on failure transitions in the visualizer."""
39
30
 
40
- DEFAULT_VISUALIZER_PARALLEL_STEP_EDGE_STYLE = "dashed"
41
- """str: The default edge style for parallel steps in the visualizer."""
31
+ DEFAULT_VISUALIZER_BRANCH_EDGE_LABEL_PREFIX = "Branch"
32
+ """str: The default edge label prefix for branch transitions in the visualizer."""
33
+
34
+ DEFAULT_VISUALIZER_BRANCH_DEFAULT_LABEL = "else"
35
+ """str: The label used for an unconditional (fallback) branch return."""
36
+
37
+ DEFAULT_VISUALIZER_MAX_BRANCH_LABEL_LENGTH = 60
38
+ """int: The maximum length of a branch condition label before it's truncated."""
39
+
40
+ # Node styling (Mermaid classDef names)
41
+
42
+ DEFAULT_VISUALIZER_SUB_STEP_FUNCTION_CLASS = "subStepFunction"
43
+ """str: The Mermaid classDef name applied to sub-step function nodes."""
44
+
45
+ DEFAULT_VISUALIZER_SUB_STEP_FUNCTION_CLASS_STYLE = (
46
+ "fill:#f5f5f5,stroke:#333,stroke-dasharray: 5 5"
47
+ )
48
+ """str: The Mermaid classDef style applied to sub-step function nodes."""
42
49
 
43
- DEFAULT_VISUALIZER_SUB_STEP_FUNCTION_NODE_SHAPE = "boxed"
44
- """str: The default node shape for sub-step functions in the visualizer."""
50
+ # Node ID sanitization
45
51
 
46
- DEFAULT_VISUALIZER_SUB_STEP_FUNCTION_NODE_STYLE = "dotted"
47
- """ str: The default node style for sub-step functions in the visualizer."""
52
+ VISUALIZER_INVALID_NODE_ID_CHARS = r"[^0-9A-Za-z_]"
53
+ """str: Regex matching characters not allowed in a Mermaid node ID; matches are replaced with "_"."""
@@ -0,0 +1,3 @@
1
+ from .serializer import decode_step_function, encode_step_function
2
+
3
+ __all__ = ["decode_step_function", "encode_step_function"]
@@ -0,0 +1,170 @@
1
+ """Encode/decode logic for declarative (JSON-able dict) StepFunction specs.
2
+
3
+ Author: Vineeth Penugonda
4
+ """
5
+
6
+ from typing import Any, Callable, Dict, Optional, cast
7
+
8
+ from stepfunction.core.step_function.step_function import StepFunction
9
+ from stepfunction.exceptions.step_errors import (
10
+ UnregisteredFunctionError,
11
+ UnserializableStepError,
12
+ )
13
+ from stepfunction.registry.step_registry import StepRegistry
14
+ from stepfunction.registry.step_registry import registry as default_registry
15
+ from stepfunction.types.step_types import StepParams
16
+
17
+
18
+ def _name_for_or_raise(
19
+ step_registry: StepRegistry, func: Callable[[Any], Any], step_name: str
20
+ ) -> str:
21
+ name = step_registry.name_for(func)
22
+ if name is None:
23
+ raise UnregisteredFunctionError(
24
+ f"The function used in step '{step_name}' is not registered in the "
25
+ "given registry, so its name can't be determined for export. "
26
+ "Register it with stepfunction.registry.step_registry.register_step() first."
27
+ )
28
+ return name
29
+
30
+
31
+ def _encode_step(
32
+ step_name: str, step: StepParams, step_registry: StepRegistry
33
+ ) -> Dict[str, Any]:
34
+ if step["step_type"] is not None:
35
+ raise UnserializableStepError(step_name, step["step_type"])
36
+
37
+ encoded: Dict[str, Any] = {
38
+ "next_step": step["next_step"],
39
+ "on_failure": step["on_failure"],
40
+ "parallel": step["parallel"],
41
+ "stop_on_failure": step["stop_on_failure"],
42
+ }
43
+
44
+ if step["is_sub_step_function"]:
45
+ sub_step_function = cast(StepFunction, step["sub_step_function"])
46
+ encoded["sub_step_function"] = encode_step_function(
47
+ sub_step_function, step_registry
48
+ )
49
+ return encoded
50
+
51
+ if step["parallel"]:
52
+ func_map = cast(Dict[str, Callable[[Any], Any]], step["func"])
53
+ encoded["func"] = {
54
+ slot: _name_for_or_raise(step_registry, fn, step_name)
55
+ for slot, fn in func_map.items()
56
+ }
57
+ else:
58
+ func = cast(Callable[[Any], Any], step["func"])
59
+ encoded["func"] = _name_for_or_raise(step_registry, func, step_name)
60
+
61
+ branch = step["branch"]
62
+ if branch is not None:
63
+ if callable(branch):
64
+ encoded["branch"] = _name_for_or_raise(step_registry, branch, step_name)
65
+ else:
66
+ encoded["branch"] = {str(key): value for key, value in branch.items()}
67
+
68
+ return encoded
69
+
70
+
71
+ def encode_step_function(
72
+ sf: StepFunction, step_registry: Optional[StepRegistry] = None
73
+ ) -> Dict[str, Any]:
74
+ """Export ``sf`` as a JSON-able dict.
75
+
76
+ Recurses into nested sub-step-functions by calling itself again, so
77
+ arbitrary nesting depth is handled without special-casing.
78
+
79
+ Raises:
80
+ UnserializableStepError: If any step was built from a BaseStep
81
+ instance (RetryStep, TimeoutStep, WaitStep, or a custom
82
+ BaseStep subclass) — not yet supported.
83
+ UnregisteredFunctionError: If a step or branch function used in
84
+ ``sf`` has no registered name in ``step_registry``.
85
+ """
86
+ step_registry = step_registry or default_registry
87
+
88
+ return {
89
+ "name": sf.name,
90
+ "start_step": sf.current_step,
91
+ "steps": {
92
+ step_name: _encode_step(step_name, step, step_registry)
93
+ for step_name, step in sf.steps.items()
94
+ },
95
+ }
96
+
97
+
98
+ def decode_step_function(
99
+ data: Dict[str, Any],
100
+ step_registry: Optional[StepRegistry] = None,
101
+ _validate: bool = True,
102
+ ) -> StepFunction:
103
+ """Reconstruct a StepFunction from a dict produced by ``encode_step_function``.
104
+
105
+ Rebuilds the workflow purely through ``add_step``/``add_sub_step_function``/
106
+ ``set_start_step`` — the same public API a user would call by hand — and
107
+ recurses into nested "sub_step_function" entries by calling itself again.
108
+ Validates exactly once, at the very end, at the outermost level only:
109
+ ``StepFunction.validate()`` already recurses into sub-step functions and
110
+ reports a readable breadcrumb across nesting levels, so a malformed
111
+ nested spec still fails fast without the decoder needing its own
112
+ recursive validation pass.
113
+
114
+ Raises:
115
+ UnregisteredFunctionError: If a referenced function name isn't
116
+ registered in ``step_registry`` (defaults to the package's
117
+ default singleton registry if not given).
118
+ ValueError: If the reconstructed workflow fails validate().
119
+ """
120
+ step_registry = step_registry or default_registry
121
+
122
+ sf = StepFunction(data["name"])
123
+
124
+ for step_name, step_data in data["steps"].items():
125
+ if "sub_step_function" in step_data:
126
+ sub_step_function = decode_step_function(
127
+ step_data["sub_step_function"], step_registry, _validate=False
128
+ )
129
+ sf.add_sub_step_function(
130
+ step_name,
131
+ sub_step_function=sub_step_function,
132
+ next_step=step_data.get("next_step"),
133
+ on_failure=step_data.get("on_failure"),
134
+ )
135
+ continue
136
+
137
+ func_spec = step_data["func"]
138
+ if isinstance(func_spec, dict):
139
+ func: Any = {
140
+ slot: step_registry.get(ref) for slot, ref in func_spec.items()
141
+ }
142
+ else:
143
+ func = step_registry.get(func_spec)
144
+
145
+ branch_spec = step_data.get("branch")
146
+ if branch_spec is None:
147
+ branch: Any = None
148
+ elif isinstance(branch_spec, dict):
149
+ branch = dict(branch_spec)
150
+ else:
151
+ branch = step_registry.get(branch_spec)
152
+
153
+ sf.add_step(
154
+ step_name,
155
+ func,
156
+ next_step=step_data.get("next_step"),
157
+ on_failure=step_data.get("on_failure"),
158
+ branch=branch,
159
+ parallel=step_data.get("parallel", False),
160
+ stop_on_failure=step_data.get("stop_on_failure", False),
161
+ )
162
+
163
+ start_step = data.get("start_step")
164
+ if start_step is not None:
165
+ sf.set_start_step(start_step)
166
+
167
+ if _validate:
168
+ sf.validate()
169
+
170
+ return sf