Skip to content

Commit b5d0094

Browse files
committed
added tuple sequence edge input typing
1 parent aa5138b commit b5d0094

2 files changed

Lines changed: 39 additions & 4 deletions

File tree

src/edgygraph/graphs.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,17 @@
33
from collections import defaultdict
44
from collections.abc import Hashable
55
import asyncio
6-
from pydantic import BaseModel, ConfigDict, Field
6+
from pydantic import BaseModel, ConfigDict, Field, SkipValidation
77

8-
from .nodes import START, Node
8+
from .nodes import START, END, Node # type: ignore
99
from .states import StateProtocol as State, SharedProtocol as Shared
1010
from .hooks import GraphHook
1111
from .diff import Change, ChangeConflictException, Diff
1212
from .types import \
1313
Edge, ErrorEdge, \
1414
SingleSource, SingleErrorSource, \
1515
Config, ErrorConfig, \
16+
NodeTupel, is_node_tupel, \
1617
Entry, ErrorEntry, Entries, \
1718
NextNode
1819

@@ -64,7 +65,7 @@ class Graph[T: State = State, S: Shared = Shared](BaseModel):
6465
- `(source, target, Config(instant=True))`: An instant edge from source to target. The target nodes are collected recursively and executed parallel to the source node. Make sure not to create cycles.
6566
- `(ValueError, target)`: An error edge from ValueError to target. The edge is traversed if a node, which is executed by an incoming edge located BEFORE this error edge in the edge list, throws a ValueError.
6667
- `((source, Exception), target)`: An error edge from Exception to target. The edge is traversed if the source node is executed by an incoming edge which is located BEFORE this error edge in the edge list throws an Exception. Source node lists are also supported.
67-
- `(Exception, target, ErrorConfig(propagate=True))`: If propagate is True, the exception is propagated to the next error edges in the edge list. If the exception is not handled by any error edge, it is ultimately raised.
68+
- `(Exception, target, ErrorConfig(propagate=True))`: If propagate is `True`, the exception is propagated to the next error edges in the edge list. If the exception is not handled by any error edge, it is ultimately raised.
6869
6970
7071
Attributes:
@@ -74,7 +75,7 @@ class Graph[T: State = State, S: Shared = Shared](BaseModel):
7475

7576
model_config = ConfigDict(arbitrary_types_allowed=True)
7677

77-
edges: Sequence[Edge[T, S] | ErrorEdge[T, S]] = Field(default_factory=list[Edge[T, S] | ErrorEdge[T, S]])
78+
edges: Sequence[Edge[T, S] | ErrorEdge[T, S] | SkipValidation[NodeTupel[T, S]]] = Field(default_factory=list[Edge[T, S] | ErrorEdge[T, S] | NodeTupel[T, S]])
7879
hooks: Sequence[GraphHook[T, S]] = Field(default_factory=list[GraphHook[T, S]], exclude=True)
7980

8081
edge_index: dict[SingleSource[T, S], list[Entry[T, S]]] = Field(default_factory=lambda: defaultdict(list), init=False)
@@ -101,6 +102,14 @@ def index_edges(self) -> None:
101102

102103
for i, edge in enumerate(self.edges):
103104

105+
# Node Sequence
106+
if is_node_tupel(edge):
107+
edge = cast(NodeTupel[T, S], edge)
108+
for source, next in zip(edge, edge[1:]):
109+
if isinstance(source, type): assert source is START, f"Unexpected type in node sequence: {source}"
110+
self.edge_index[source].append(Entry[T, S](next=next, config=Config(), index=i))
111+
continue
112+
104113
match edge:
105114
case (source, next, config): pass
106115
case (source, next): config = Config() if source is START or isinstance(source, (Node, list)) else ErrorConfig()
@@ -262,6 +271,7 @@ async def get_next_from_error(self, state: T, shared: S, eg: ExceptionGroup) ->
262271
for e in eg.exceptions:
263272

264273
print(e)
274+
265275

266276
source_node: NextNode[T, S] | None = getattr(e, "source_node", None)
267277

src/edgygraph/types.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
from .states import StateProtocol as State, SharedProtocol as Shared
77

88

9+
type NodeTupel[T: State, S: Shared] = tuple[type[START] | Node[T, S], *tuple[Node[T, S], ...]] | tuple[Node[T, S] | type[START], *tuple[Node[T, S], ...], type[END]]
10+
911
type SingleSource[T: State, S: Shared] = Node[T, S] | type[START]
1012
type Source[T: State, S: Shared] = SingleSource[T, S] | list[SingleSource[T, S]]
1113

@@ -20,6 +22,29 @@
2022
type ErrorEdge[T: State, S: Shared] = tuple[ErrorSource[T, S], Next[T, S]] | tuple[ErrorSource[T, S], Next[T, S], ErrorConfig]
2123

2224

25+
def is_node_tupel(edge: tuple[Any, ...]) -> bool:
26+
27+
if len(edge) < 2:
28+
return False
29+
30+
if is_only_node_tuple(edge):
31+
return True
32+
33+
if edge[0] is START and is_only_node_tuple(edge[1:]):
34+
return True
35+
36+
if edge[0] is START and is_only_node_tuple(edge[1:-1]) and edge[-1] is END:
37+
return True
38+
39+
if is_only_node_tuple(edge[:-1]) and edge[-1] is END:
40+
return True
41+
42+
return False
43+
44+
def is_only_node_tuple(edge: tuple[Any, ...]) -> bool:
45+
return all(isinstance(n, Node) for n in edge)
46+
47+
2348
class Config(BaseModel):
2449
"""
2550
Configuration for the edge.

0 commit comments

Comments
 (0)