pydantic-graph 1.2.1__py3-none-any.whl → 1.26.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.
@@ -0,0 +1,90 @@
1
+ """Utility types and functions for type manipulation and introspection.
2
+
3
+ This module provides helper classes and functions for working with Python's type system,
4
+ including workarounds for type checker limitations and utilities for runtime type inspection.
5
+ """
6
+
7
+ from dataclasses import dataclass
8
+ from typing import Any, Generic, cast, get_args, get_origin
9
+
10
+ from typing_extensions import TypeAliasType, TypeVar
11
+
12
+ T = TypeVar('T', infer_variance=True)
13
+ """Generic type variable with inferred variance."""
14
+
15
+
16
+ class TypeExpression(Generic[T]):
17
+ """A workaround for type checker limitations when using complex type expressions.
18
+
19
+ This class serves as a wrapper for types that cannot normally be used in positions
20
+ requiring `type[T]`, such as `Any`, `Union[...]`, or `Literal[...]`. It provides a
21
+ way to pass these complex type expressions to functions expecting concrete types.
22
+
23
+ Example:
24
+ Instead of `output_type=Union[str, int]` (which may cause type errors),
25
+ use `output_type=TypeExpression[Union[str, int]]`.
26
+
27
+ Note:
28
+ This is a workaround for the lack of TypeForm in the Python type system.
29
+ """
30
+
31
+ pass
32
+
33
+
34
+ TypeOrTypeExpression = TypeAliasType('TypeOrTypeExpression', type[TypeExpression[T]] | type[T], type_params=(T,))
35
+ """Type alias allowing both direct types and TypeExpression wrappers.
36
+
37
+ This alias enables functions to accept either regular types (when compatible with type checkers)
38
+ or TypeExpression wrappers for complex type expressions. The correct type should be inferred
39
+ automatically in either case.
40
+ """
41
+
42
+
43
+ def unpack_type_expression(type_: TypeOrTypeExpression[T]) -> type[T]:
44
+ """Extract the actual type from a TypeExpression wrapper or return the type directly.
45
+
46
+ Args:
47
+ type_: Either a direct type or a TypeExpression wrapper.
48
+
49
+ Returns:
50
+ The unwrapped type, ready for use in runtime type operations.
51
+ """
52
+ if get_origin(type_) is TypeExpression:
53
+ return get_args(type_)[0]
54
+ return cast(type[T], type_)
55
+
56
+
57
+ @dataclass
58
+ class Some(Generic[T]):
59
+ """Container for explicitly present values in Maybe type pattern.
60
+
61
+ This class represents a value that is definitely present, as opposed to None.
62
+ It's part of the Maybe pattern, similar to Option/Maybe in functional programming,
63
+ allowing distinction between "no value" (None) and "value is None" (Some(None)).
64
+ """
65
+
66
+ value: T
67
+ """The wrapped value."""
68
+
69
+
70
+ Maybe = TypeAliasType('Maybe', Some[T] | None, type_params=(T,))
71
+ """Optional-like type that distinguishes between absence and None values.
72
+
73
+ Unlike Optional[T], Maybe[T] can differentiate between:
74
+ - No value present: represented as None
75
+ - Value is None: represented as Some(None)
76
+
77
+ This is particularly useful when None is a valid value in your domain.
78
+ """
79
+
80
+
81
+ def get_callable_name(callable_: Any) -> str:
82
+ """Extract a human-readable name from a callable object.
83
+
84
+ Args:
85
+ callable_: Any callable object (function, method, class, etc.).
86
+
87
+ Returns:
88
+ The callable's __name__ attribute if available, otherwise its string representation.
89
+ """
90
+ return getattr(callable_, '__name__', str(callable_))
@@ -15,6 +15,28 @@ class GraphSetupError(TypeError):
15
15
  super().__init__(message)
16
16
 
17
17
 
18
+ class GraphBuildingError(ValueError):
19
+ """An error raised during graph-building."""
20
+
21
+ message: str
22
+ """The error message."""
23
+
24
+ def __init__(self, message: str):
25
+ self.message = message
26
+ super().__init__(message)
27
+
28
+
29
+ class GraphValidationError(ValueError):
30
+ """An error raised during graph validation."""
31
+
32
+ message: str
33
+ """The error message."""
34
+
35
+ def __init__(self, message: str):
36
+ self.message = message
37
+ super().__init__(message)
38
+
39
+
18
40
  class GraphRuntimeError(RuntimeError):
19
41
  """Error caused by an issue during graph execution."""
20
42
 
pydantic_graph/graph.py CHANGED
@@ -238,8 +238,12 @@ class Graph(Generic[StateT, DepsT, RunEndT]):
238
238
  with ExitStack() as stack:
239
239
  entered_span: AbstractSpan | None = None
240
240
  if span is None:
241
- if self.auto_instrument:
242
- entered_span = stack.enter_context(logfire_span('run graph {graph.name}', graph=self))
241
+ if self.auto_instrument: # pragma: no branch
242
+ # Separate variable because we actually don't want logfire's f-string magic here,
243
+ # we want the span_name to be preformatted for other backends
244
+ # as requested in https://github.com/pydantic/pydantic-ai/issues/3173.
245
+ span_name = f'run graph {self.name}'
246
+ entered_span = stack.enter_context(logfire_span(span_name, graph=self))
243
247
  else:
244
248
  entered_span = stack.enter_context(span)
245
249
  traceparent = None if entered_span is None else get_traceparent(entered_span)
@@ -723,8 +727,12 @@ class GraphRun(Generic[StateT, DepsT, RunEndT]):
723
727
  raise exceptions.GraphRuntimeError(f'Node `{node}` is not in the graph.')
724
728
 
725
729
  with ExitStack() as stack:
726
- if self.graph.auto_instrument:
727
- stack.enter_context(logfire_span('run node {node_id}', node_id=node_id, node=node))
730
+ if self.graph.auto_instrument: # pragma: no branch
731
+ # Separate variable because we actually don't want logfire's f-string magic here,
732
+ # we want the span_name to be preformatted for other backends
733
+ # as requested in https://github.com/pydantic/pydantic-ai/issues/3173.
734
+ span_name = f'run node {node_id}'
735
+ stack.enter_context(logfire_span(span_name, node_id=node_id, node=node))
728
736
 
729
737
  async with self.persistence.record_run(node_snapshot_id):
730
738
  ctx = GraphRunContext(state=self.state, deps=self.deps)
pydantic_graph/nodes.py CHANGED
@@ -28,8 +28,6 @@ DepsT = TypeVar('DepsT', default=None, contravariant=True)
28
28
  class GraphRunContext(Generic[StateT, DepsT]):
29
29
  """Context for a graph."""
30
30
 
31
- # TODO: Can we get rid of this struct and just pass both these things around..?
32
-
33
31
  state: StateT
34
32
  """The state of the graph."""
35
33
  deps: DepsT
@@ -65,7 +65,7 @@ class SimpleStatePersistence(BaseStatePersistence[StateT, RunEndT]):
65
65
  start = perf_counter()
66
66
  try:
67
67
  yield
68
- except Exception:
68
+ except Exception: # pragma: no cover
69
69
  self.last_snapshot.duration = perf_counter() - start
70
70
  self.last_snapshot.status = 'error'
71
71
  raise
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydantic-graph
3
- Version: 1.2.1
3
+ Version: 1.26.0
4
4
  Summary: Graph and state machine library
5
5
  Project-URL: Homepage, https://ai.pydantic.dev/graph/tree/main/pydantic_graph
6
6
  Project-URL: Source, https://github.com/pydantic/pydantic-ai
@@ -0,0 +1,28 @@
1
+ pydantic_graph/__init__.py,sha256=qkrSmWLpnNhD7mLBWV70iS46vy2vFiU2zUModG31wPQ,593
2
+ pydantic_graph/_utils.py,sha256=f0B1VIRxfAslj5UIfBVRzXhIJkDXyF8P3B9V22Q7o7U,6782
3
+ pydantic_graph/exceptions.py,sha256=aeaBf2H18dV7YCNZKZmiXiI6Fyys2qQdunZwd7TSCPk,1648
4
+ pydantic_graph/graph.py,sha256=rEm_5PzRs-5k6Y0mmaF5SGhF0wPA2JSclNAEZtBUZpA,33942
5
+ pydantic_graph/mermaid.py,sha256=u8xM8eEAOWV0TkqEAPJJ9jL2XEZnJ_H7yNGhulg7SL4,10045
6
+ pydantic_graph/nodes.py,sha256=CkY3lrC6jqZtzwhSRjFzmM69TdFFFrr58XSDU4THKHA,7450
7
+ pydantic_graph/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ pydantic_graph/beta/__init__.py,sha256=VVmbEFaCSXYHwXqS4pANg4B3cn_c86tT62tW_EXcuyw,751
9
+ pydantic_graph/beta/decision.py,sha256=x-Ta549b-j5hyBPUWFdwRQDRaJqnBHF1pfBP9L8I3vI,11239
10
+ pydantic_graph/beta/graph.py,sha256=-T-HbVyBC3qgg_-dXURnCbI6K_mqj25jDVh_RMlVsS8,42811
11
+ pydantic_graph/beta/graph_builder.py,sha256=2sD7TR8oGg4Gatrms0jE17NXzQN7drzUvaJKs5BvILU,43329
12
+ pydantic_graph/beta/id_types.py,sha256=FZ3rYSubF6g_Ocv0faL3yJsy1lNN9AGZl9f_izvORUg,2814
13
+ pydantic_graph/beta/join.py,sha256=rzCumDX_YgaU_a5bisfbjbbOuI3IwSZsCZs9TC0T9E4,8002
14
+ pydantic_graph/beta/mermaid.py,sha256=vpB9laZeTaS-P6BJplyN7DLFz0ppRVafGjBfqRiTh-s,7128
15
+ pydantic_graph/beta/node.py,sha256=cTEGKiT3Lutg-PWxBbZDihpnBTVoPMSyCbfB50fjKeY,3071
16
+ pydantic_graph/beta/node_types.py,sha256=Ha1QPbAHqmJ1ARb359b8LOJK-jZDMO_ZyHkYv9Zbglw,3399
17
+ pydantic_graph/beta/parent_forks.py,sha256=lMCT3slwDuZtiLZImqXuW-i0ZftONCWGr7RTpCAe9dY,9691
18
+ pydantic_graph/beta/paths.py,sha256=LkFvgnyNa1lHdFkN83F7Dgsdg9Q2y0zYrLyqprQiQiY,16068
19
+ pydantic_graph/beta/step.py,sha256=n0JstmxM6Z2rCc2EPUrSAp4MS4IjM2mZsE0ymeekzxU,8683
20
+ pydantic_graph/beta/util.py,sha256=F7IkSC0U-tU1yOxncslyOrZ5HlrZIdafBJARsPetIHQ,3153
21
+ pydantic_graph/persistence/__init__.py,sha256=NLBGvUWhem23EdMHHxtX0XgTS2vyixmuWtWmZKj_U58,8968
22
+ pydantic_graph/persistence/_utils.py,sha256=6ySxCc1lFz7bbLUwDLkoZWNqi8VNLBVU4xxJbKI23fQ,2264
23
+ pydantic_graph/persistence/file.py,sha256=XZy295cGc86HfUl_KuB-e7cECZW3bubiEdyJMVQ1OD0,6906
24
+ pydantic_graph/persistence/in_mem.py,sha256=MmahaVpdzmDB30Dm3ZfSCZBqgmx6vH4HXdBaWwVF0K0,6799
25
+ pydantic_graph-1.26.0.dist-info/METADATA,sha256=8lVbDzk80c24V5ZXLShG4qEVpnDFc8fcCj57j8sUfcQ,3895
26
+ pydantic_graph-1.26.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
27
+ pydantic_graph-1.26.0.dist-info/licenses/LICENSE,sha256=vA6Jc482lEyBBuGUfD1pYx-cM7jxvLYOxPidZ30t_PQ,1100
28
+ pydantic_graph-1.26.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
@@ -1,15 +0,0 @@
1
- pydantic_graph/__init__.py,sha256=qkrSmWLpnNhD7mLBWV70iS46vy2vFiU2zUModG31wPQ,593
2
- pydantic_graph/_utils.py,sha256=Z-CCyFQV47Otpup8G43oWho4W0LJrUSE4dRDCg-jjNo,5453
3
- pydantic_graph/exceptions.py,sha256=OUz0YaUHG9CqHYvV4uU4mwF-E2GQCLWKjsWZBu3sMe8,1164
4
- pydantic_graph/graph.py,sha256=nJNUcMQHSj-Yg2d7pgpAxD-3FdVDhtxJ91x_z3UK7xY,33281
5
- pydantic_graph/mermaid.py,sha256=u8xM8eEAOWV0TkqEAPJJ9jL2XEZnJ_H7yNGhulg7SL4,10045
6
- pydantic_graph/nodes.py,sha256=FVMc8YtspydzqUNrYeu1xfosjorafZT2OoNJe16ZvbU,7535
7
- pydantic_graph/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- pydantic_graph/persistence/__init__.py,sha256=NLBGvUWhem23EdMHHxtX0XgTS2vyixmuWtWmZKj_U58,8968
9
- pydantic_graph/persistence/_utils.py,sha256=6ySxCc1lFz7bbLUwDLkoZWNqi8VNLBVU4xxJbKI23fQ,2264
10
- pydantic_graph/persistence/file.py,sha256=XZy295cGc86HfUl_KuB-e7cECZW3bubiEdyJMVQ1OD0,6906
11
- pydantic_graph/persistence/in_mem.py,sha256=TmXB9V-gVMz7qIWov-cWzEa1Cw0Tf9TKGti0CB5U_dQ,6779
12
- pydantic_graph-1.2.1.dist-info/METADATA,sha256=h7wPmdIuGpdhsSCnyCi2-7-toQ2q9TsEi-Awn1FyBVc,3894
13
- pydantic_graph-1.2.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
14
- pydantic_graph-1.2.1.dist-info/licenses/LICENSE,sha256=vA6Jc482lEyBBuGUfD1pYx-cM7jxvLYOxPidZ30t_PQ,1100
15
- pydantic_graph-1.2.1.dist-info/RECORD,,