python-saga-orchestrator 0.2.3.dev0__py3-none-any.whl → 0.2.4__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.
- {python_saga_orchestrator-0.2.3.dev0.dist-info → python_saga_orchestrator-0.2.4.dist-info}/METADATA +1 -1
- {python_saga_orchestrator-0.2.3.dev0.dist-info → python_saga_orchestrator-0.2.4.dist-info}/RECORD +10 -10
- saga_orchestrator/_version.py +2 -2
- saga_orchestrator/domain/models/context.py +0 -1
- saga_orchestrator/domain/models/step.py +62 -15
- saga_orchestrator/inbox/models.py +1 -2
- saga_orchestrator/outbox/models.py +1 -2
- {python_saga_orchestrator-0.2.3.dev0.dist-info → python_saga_orchestrator-0.2.4.dist-info}/WHEEL +0 -0
- {python_saga_orchestrator-0.2.3.dev0.dist-info → python_saga_orchestrator-0.2.4.dist-info}/licenses/LICENSE +0 -0
- {python_saga_orchestrator-0.2.3.dev0.dist-info → python_saga_orchestrator-0.2.4.dist-info}/top_level.txt +0 -0
{python_saga_orchestrator-0.2.3.dev0.dist-info → python_saga_orchestrator-0.2.4.dist-info}/RECORD
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
python_saga_orchestrator-0.2.
|
|
1
|
+
python_saga_orchestrator-0.2.4.dist-info/licenses/LICENSE,sha256=ESYyLizI0WWtxMeS7rGVcX3ivMezm-HOd5WdeOh-9oU,1056
|
|
2
2
|
saga_orchestrator/__init__.py,sha256=FG7zoYhCzpZC_JEsy3ebDraH2ZZ9q3IGIjTGKqBzcF4,2836
|
|
3
|
-
saga_orchestrator/_version.py,sha256=
|
|
3
|
+
saga_orchestrator/_version.py,sha256=oIZQE-4i_2bThWVLsbnRskxqsRwrfcev8lVgZK6VSzM,520
|
|
4
4
|
saga_orchestrator/admin/__init__.py,sha256=TKwKTM7IieI4nlMAbJ0O0OI0KPKfwbclVffNjRtIyAg,80
|
|
5
5
|
saga_orchestrator/admin/api.py,sha256=u_eLELUlaHpEiuHqweNHzBwSYCtotERAiOxyVtUMe2I,1715
|
|
6
6
|
saga_orchestrator/core/__init__.py,sha256=EsUqhbO_CgCYZz0yBnf6OUXH3N-_uxMod4A7yGzvbMY,264
|
|
@@ -17,11 +17,11 @@ saga_orchestrator/domain/mixins/saga_step_histrory.py,sha256=phB_oue8ksMMtDiNSER
|
|
|
17
17
|
saga_orchestrator/domain/mixins/types.py,sha256=FlKee4yNq6y1lXX3W_SeE385udNNg-oZqMxqVXZoEpo,2526
|
|
18
18
|
saga_orchestrator/domain/models/__init__.py,sha256=vgUSmwVdPhKNijFZsfmZ7mQZD8DJgCV4s4hMVQZNlwE,853
|
|
19
19
|
saga_orchestrator/domain/models/builder.py,sha256=D3mVYEJT7QJ0e2dNrR2UNHT5xhRK4te6s5WchU57EIA,816
|
|
20
|
-
saga_orchestrator/domain/models/context.py,sha256=
|
|
20
|
+
saga_orchestrator/domain/models/context.py,sha256=LF-6sjB70J3adrLQFz_zgEfRaErJA9Ci5TxIfYfkePA,3885
|
|
21
21
|
saga_orchestrator/domain/models/notify.py,sha256=SuZILSFNAWcUQtjL-I_c4K6VFJXXhD5LLLC2KoMq8dw,837
|
|
22
22
|
saga_orchestrator/domain/models/retry.py,sha256=UM2ZrSGKDZRiPMj0qOGp36xiTr78CVKsceXFFxtiOug,1362
|
|
23
23
|
saga_orchestrator/domain/models/saga_snapshot.py,sha256=ysnBVB2rw059pC07Py_xzAA-Bv7d1h6ZZuubM_Fv0_4,867
|
|
24
|
-
saga_orchestrator/domain/models/step.py,sha256
|
|
24
|
+
saga_orchestrator/domain/models/step.py,sha256=GxVQ4Qi0RKTkGt-9P8tIz54WG8VHmEnghiShUT2mW-E,6947
|
|
25
25
|
saga_orchestrator/domain/models/enums/__init__.py,sha256=IFgCFvhHiQl1MSCN_I_w3O8yR94tZvN5Az8M3V3bXEQ,234
|
|
26
26
|
saga_orchestrator/domain/models/enums/saga_status.py,sha256=Evz7Uy4-KrqGWFkb6RHhONCWLePd5Gjezw5u-FLmxw0,397
|
|
27
27
|
saga_orchestrator/domain/models/enums/saga_step_phase.py,sha256=L73jg8q-IdDXPpMjuMRwhfVZbAMYW_42FkdXNKqwiqI,129
|
|
@@ -29,7 +29,7 @@ saga_orchestrator/domain/models/enums/saga_step_status.py,sha256=DZN5qgDorhdFbhI
|
|
|
29
29
|
saga_orchestrator/inbox/__init__.py,sha256=uoLytCCT-2-zr369ZpGmQOB_D63fmwjV9T6sCHYGk64,656
|
|
30
30
|
saga_orchestrator/inbox/contracts.py,sha256=unV7h1Eqil6sZhmpGVE_-9c_voRQhYA3KdE63T79s4U,1768
|
|
31
31
|
saga_orchestrator/inbox/dispatcher.py,sha256=O4Zzi5fRAZIUQqlk3oJof6rl7_CPzvqSVQ_Un-1WkOY,4323
|
|
32
|
-
saga_orchestrator/inbox/models.py,sha256=
|
|
32
|
+
saga_orchestrator/inbox/models.py,sha256=MBID-3wBepnIetwMMAU22sD9olG_LpNu9K0LyOo_m5c,2632
|
|
33
33
|
saga_orchestrator/inbox/repository.py,sha256=BdcVlBW9zvM7WrsL2S3Rmihmn-Ri6gr69L8ji7qqip8,5286
|
|
34
34
|
saga_orchestrator/inbox/retry.py,sha256=jTxg6tZBl7Vllt89Yw-QdOSObYWXrZWjAyW36mCJHdg,590
|
|
35
35
|
saga_orchestrator/outbox/__init__.py,sha256=g-isr2D737wMV1Mf_vEPFoS34YE6n6l09iKLLAL86uU,917
|
|
@@ -37,11 +37,11 @@ saga_orchestrator/outbox/contracts.py,sha256=KhqIDIDvwA26xu4VQJpU6qg2ySmsRAORest
|
|
|
37
37
|
saga_orchestrator/outbox/dispatcher.py,sha256=BX9WTuTLx1gfu-5jV8_ejpi9nnEilnKLYY_BljJkxC8,3196
|
|
38
38
|
saga_orchestrator/outbox/event.py,sha256=Kj-u_JO55jx3YMTpA9arIvmTgKEAxhH2-WQE1eTi4KU,273
|
|
39
39
|
saga_orchestrator/outbox/factory.py,sha256=_p7XT_ulouBhl9J9fcgWsZkhkjmzkpfIs9m6E55UPdA,1743
|
|
40
|
-
saga_orchestrator/outbox/models.py,sha256=
|
|
40
|
+
saga_orchestrator/outbox/models.py,sha256=BZRAxfKXPun_qpOG3rzG3QI6AxsW0cVrio6ec-yZjFI,2333
|
|
41
41
|
saga_orchestrator/outbox/repository.py,sha256=A3nCi5UOwQT9mlEY03BrLidyO2sFOQGgYcE4pFeY-pA,4786
|
|
42
42
|
saga_orchestrator/outbox/retry.py,sha256=9Ygz3I0HK0r9imTYevBgz14TkAZphKiOSf0grJpH99c,599
|
|
43
43
|
saga_orchestrator/outbox/serialization.py,sha256=CDicJS95CHhLP47XukJAgfm0baZ83daWXQQF3MyczDo,1687
|
|
44
|
-
python_saga_orchestrator-0.2.
|
|
45
|
-
python_saga_orchestrator-0.2.
|
|
46
|
-
python_saga_orchestrator-0.2.
|
|
47
|
-
python_saga_orchestrator-0.2.
|
|
44
|
+
python_saga_orchestrator-0.2.4.dist-info/METADATA,sha256=DC0H0aUU0o7iFem4Mu3FZ7erw8WN460xugFT4DmDqUQ,10288
|
|
45
|
+
python_saga_orchestrator-0.2.4.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
46
|
+
python_saga_orchestrator-0.2.4.dist-info/top_level.txt,sha256=XBp_2J8dacJGCoVxIDaUYhSEuOusCN3BD_uhEjBEEBA,18
|
|
47
|
+
python_saga_orchestrator-0.2.4.dist-info/RECORD,,
|
saga_orchestrator/_version.py
CHANGED
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.2.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2,
|
|
21
|
+
__version__ = version = '0.2.4'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 4)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import contextlib
|
|
4
3
|
import inspect
|
|
5
4
|
from collections.abc import Callable
|
|
6
5
|
from dataclasses import dataclass
|
|
@@ -8,6 +7,7 @@ from datetime import timedelta
|
|
|
8
7
|
from typing import (
|
|
9
8
|
Any,
|
|
10
9
|
Generic,
|
|
10
|
+
Mapping,
|
|
11
11
|
TypeAlias,
|
|
12
12
|
TypeVar,
|
|
13
13
|
get_args,
|
|
@@ -60,16 +60,16 @@ class InputContext:
|
|
|
60
60
|
"""
|
|
61
61
|
Возвращает тип ('event_type') последнего полученного события.
|
|
62
62
|
"""
|
|
63
|
-
|
|
64
|
-
return
|
|
65
|
-
return
|
|
63
|
+
if not isinstance(self.context.latest_event_meta, Mapping):
|
|
64
|
+
return None
|
|
65
|
+
return self.context.latest_event_meta.get("event_type")
|
|
66
66
|
|
|
67
67
|
@property
|
|
68
68
|
def latest_event_payload(self) -> Any | None:
|
|
69
69
|
"""
|
|
70
70
|
Возвращает "сырую" полезную нагрузку (payload) последнего полученного события.
|
|
71
71
|
"""
|
|
72
|
-
return self.context.
|
|
72
|
+
return self.context.latest_event
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
RootInputMap: TypeAlias = Callable[[InputContext], InputModelT | dict[str, Any]]
|
|
@@ -102,16 +102,49 @@ class BaseStep(Generic[InputModelT, OutputModelT]):
|
|
|
102
102
|
|
|
103
103
|
def __init_subclass__(cls) -> None:
|
|
104
104
|
super().__init_subclass__()
|
|
105
|
-
if cls is BaseStep:
|
|
105
|
+
if cls is BaseStep or inspect.isabstract(cls):
|
|
106
106
|
return
|
|
107
|
+
generic_base = None
|
|
108
|
+
for base in getattr(cls, "__orig_bases__", []):
|
|
109
|
+
origin = get_origin(base)
|
|
110
|
+
if origin and issubclass(origin, BaseStep):
|
|
111
|
+
generic_base = base
|
|
112
|
+
break
|
|
113
|
+
|
|
114
|
+
if not generic_base:
|
|
115
|
+
raise TypeValidationError(
|
|
116
|
+
f"Could not find generic parameters for {cls.__name__}. "
|
|
117
|
+
f"Ensure it inherits from a parameterized BaseStep, e.g., BaseStep[MyInput, MyOutput]"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
concrete_args = get_args(generic_base)
|
|
121
|
+
if not concrete_args or any(isinstance(arg, TypeVar) for arg in concrete_args):
|
|
122
|
+
raise TypeValidationError(f"Step '{cls.__name__}' inherits from a generic Step "
|
|
123
|
+
"but was not parameterized with concrete Input/Output models.")
|
|
124
|
+
|
|
125
|
+
concrete_input_model, concrete_output_model = concrete_args
|
|
126
|
+
try:
|
|
127
|
+
hints = get_type_hints(cls.execute, globalns=inspect.getmodule(cls).__dict__, include_extras=True)
|
|
128
|
+
except (AttributeError, NameError) as e:
|
|
129
|
+
raise TypeValidationError(
|
|
130
|
+
f"Could not resolve type hints for '{cls.__name__}.execute'. "
|
|
131
|
+
f"Ensure all types are correctly imported. Original error: {e}"
|
|
132
|
+
)
|
|
133
|
+
|
|
107
134
|
|
|
108
|
-
hints = get_type_hints(cls.execute)
|
|
109
135
|
if "inp" not in hints or "return" not in hints:
|
|
110
136
|
raise TypeValidationError(
|
|
111
137
|
f"Step '{cls.__name__}' must type annotate execute(inp) and return type"
|
|
112
138
|
)
|
|
113
|
-
|
|
114
|
-
|
|
139
|
+
|
|
140
|
+
input_annotation = hints["inp"]
|
|
141
|
+
if isinstance(input_annotation, TypeVar):
|
|
142
|
+
input_model = concrete_input_model
|
|
143
|
+
else:
|
|
144
|
+
input_model = input_annotation
|
|
145
|
+
|
|
146
|
+
return_annotation = hints["return"]
|
|
147
|
+
output_model = cls._resolve_output_model(return_annotation, concrete_output_model)
|
|
115
148
|
if not (inspect.isclass(input_model) and issubclass(input_model, BaseModel)):
|
|
116
149
|
raise TypeValidationError(
|
|
117
150
|
f"Step '{cls.__name__}' input must inherit from pydantic BaseModel"
|
|
@@ -120,22 +153,36 @@ class BaseStep(Generic[InputModelT, OutputModelT]):
|
|
|
120
153
|
cls.output_model = output_model
|
|
121
154
|
|
|
122
155
|
@staticmethod
|
|
123
|
-
def _resolve_output_model(annotation: Any) -> type[BaseModel]:
|
|
156
|
+
def _resolve_output_model(annotation: Any, concrete_model: type[BaseModel] | None = None) -> type[BaseModel]:
|
|
157
|
+
def find_model_in_args(args_tuple: tuple) -> list[type[BaseModel]]:
|
|
158
|
+
candidates = []
|
|
159
|
+
for arg in args_tuple:
|
|
160
|
+
if isinstance(arg, TypeVar) and concrete_model:
|
|
161
|
+
candidates.append(concrete_model)
|
|
162
|
+
elif inspect.isclass(arg) and issubclass(arg, BaseModel):
|
|
163
|
+
candidates.append(arg)
|
|
164
|
+
return list(dict.fromkeys(candidates))
|
|
165
|
+
|
|
124
166
|
if inspect.isclass(annotation) and issubclass(annotation, BaseModel):
|
|
125
167
|
return annotation
|
|
126
168
|
|
|
127
169
|
origin = get_origin(annotation)
|
|
128
170
|
if origin is None:
|
|
171
|
+
if isinstance(annotation, TypeVar) and concrete_model:
|
|
172
|
+
return concrete_model
|
|
129
173
|
raise TypeValidationError("Step execute return type must include BaseModel")
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
]
|
|
174
|
+
|
|
175
|
+
args = get_args(annotation)
|
|
176
|
+
model_candidates = find_model_in_args(args)
|
|
134
177
|
await_candidates = [arg for arg in args if arg is StepAwaitEvent]
|
|
178
|
+
|
|
135
179
|
if len(model_candidates) == 1 and len(await_candidates) <= 1:
|
|
136
180
|
return model_candidates[0]
|
|
181
|
+
|
|
137
182
|
raise TypeValidationError(
|
|
138
|
-
"Step execute return type must be BaseModel or BaseModel | StepAwaitEvent"
|
|
183
|
+
f"Step execute return type must be BaseModel or BaseModel | StepAwaitEvent. "
|
|
184
|
+
f"Found models: {[m.__name__ for m in model_candidates]}, "
|
|
185
|
+
f"Found await events: {len(await_candidates)}."
|
|
139
186
|
)
|
|
140
187
|
|
|
141
188
|
async def execute(self, inp: InputModelT) -> OutputModelT | StepAwaitEvent:
|
|
@@ -5,9 +5,8 @@ from datetime import datetime
|
|
|
5
5
|
from enum import Enum
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
-
from sqlalchemy import JSON, DateTime
|
|
8
|
+
from sqlalchemy import JSON, DateTime, Integer, String, Text, UniqueConstraint, func
|
|
9
9
|
from sqlalchemy import Enum as SqlEnum
|
|
10
|
-
from sqlalchemy import Integer, String, Text, UniqueConstraint, func
|
|
11
10
|
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
|
12
11
|
from sqlalchemy.ext.mutable import MutableDict
|
|
13
12
|
from sqlalchemy.orm import Mapped, declarative_mixin, mapped_column
|
|
@@ -5,9 +5,8 @@ from datetime import datetime
|
|
|
5
5
|
from enum import Enum
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
-
from sqlalchemy import JSON, DateTime
|
|
8
|
+
from sqlalchemy import JSON, DateTime, Integer, String, Text, func
|
|
9
9
|
from sqlalchemy import Enum as SqlEnum
|
|
10
|
-
from sqlalchemy import Integer, String, Text, func
|
|
11
10
|
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
|
12
11
|
from sqlalchemy.ext.mutable import MutableDict
|
|
13
12
|
from sqlalchemy.orm import Mapped, declarative_mixin, mapped_column
|
{python_saga_orchestrator-0.2.3.dev0.dist-info → python_saga_orchestrator-0.2.4.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|