3333from ...exceptions import (
3434 EmptySagaStepException ,
3535 MultipleElseThenException ,
36+ OrderPrecedenceException ,
3637)
3738from ..operations import (
3839 SagaOperation ,
5152 Saga ,
5253 )
5354
54- ConditionalSagaStepClass = TypeVar ("ConditionalSagaStepClass" , bound = type )
5555
56-
57- class ConditionalSagaStepDecoratorWrapper (SagaStepDecoratorWrapper ):
56+ @ runtime_checkable
57+ class ConditionalSagaStepDecoratorWrapper (SagaStepDecoratorWrapper , Protocol ):
5858 """TODO"""
5959
6060 meta : ConditionalSagaStepDecoratorMeta
@@ -70,29 +70,37 @@ def definition(self):
7070 """TODO"""
7171 if_then_alternatives = getmembers (
7272 self ._inner ,
73- predicate = lambda x : isinstance (x , IfThenAlternativeWrapper ) and isinstance (x .meta , IfThenAlternativeMeta ),
73+ predicate = (
74+ lambda x : isinstance (x , IfThenAlternativeDecoratorWrapper )
75+ and isinstance (x .meta , IfThenAlternativeDecoratorMeta )
76+ ),
7477 )
75- if_then_alternatives = map (lambda member : member [1 ].meta .alternative , if_then_alternatives )
76- if_then_alternatives = sorted (if_then_alternatives , key = attrgetter ("order" ))
78+ if_then_alternatives = list (map (lambda member : member [1 ].meta .alternative , if_then_alternatives ))
79+ for alternative in if_then_alternatives :
80+ if alternative .order is None :
81+ raise OrderPrecedenceException (f"The { alternative !r} alternative does not have 'order' value." )
82+ if_then_alternatives .sort (key = attrgetter ("order" ))
7783
7884 for alternative in if_then_alternatives :
79- self ._definition .if_then_alternatives . append (alternative )
85+ self ._definition .if_then (alternative )
8086
8187 else_then_alternatives = getmembers (
8288 self ._inner ,
8389 predicate = (
84- lambda x : isinstance (x , ElseThenAlternativeWrapper ) and isinstance (x .meta , ElseThenAlternativeMeta )
90+ lambda x : isinstance (x , ElseThenAlternativeDecoratorWrapper )
91+ and isinstance (x .meta , ElseThenAlternativeDecoratorMeta )
8592 ),
8693 )
8794 else_then_alternatives = list (map (lambda member : member [1 ].meta .alternative , else_then_alternatives ))
88- if len (else_then_alternatives ) > 1 :
89- raise MultipleElseThenException ()
90- elif len (else_then_alternatives ) == 1 :
91- self ._definition .else_then_alternative = else_then_alternatives [0 ]
95+ for alternative in else_then_alternatives :
96+ self ._definition .else_then (alternative )
9297
9398 return self ._definition
9499
95100
101+ TP = TypeVar ("TP" , bound = type )
102+
103+
96104class ConditionalSagaStep (SagaStep ):
97105 """Conditional Saga Step class."""
98106
@@ -131,34 +139,51 @@ def _from_raw(cls, raw: dict[str, Any], **kwargs) -> ConditionalSagaStep:
131139
132140 return cls (** current )
133141
134- def __call__ (self , type_ : ConditionalSagaStepClass ) -> ConditionalSagaStepClass :
142+ def __call__ (self , type_ : TP ) -> Union [ TP , ConditionalSagaStepDecoratorWrapper ] :
135143 meta = ConditionalSagaStepDecoratorMeta (type_ , self )
136144 type_ .meta = meta
137145 return type_
138146
139- def if_then (self , condition : ConditionCallback , saga : Saga ) -> ConditionalSagaStep :
147+ def if_then (
148+ self , alternative : Union [IfThenAlternative , ConditionCallback ], saga : Optional [Saga ] = None
149+ ) -> ConditionalSagaStep :
140150 """Add a new ``IfThenAlternative`` based on a condition and a saga.
141151
142- :param condition : The condition that must be satisfied to execute the alternative.
152+ :param alternative : The condition that must be satisfied to execute the alternative.
143153 :param saga: The saga to be executed if the condition is satisfied.
144154 :return: This method returns the same instance that is called.
145155 """
156+ if not isinstance (alternative , IfThenAlternative ):
157+ alternative = IfThenAlternative (saga , alternative )
158+
159+ if alternative .order is None :
160+ if self .if_then_alternatives :
161+ alternative .order = self .if_then_alternatives [- 1 ].order + 1
162+ else :
163+ alternative .order = 1
164+
165+ if self .if_then_alternatives and alternative .order <= self .if_then_alternatives [- 1 ].order :
166+ raise OrderPrecedenceException (
167+ f"Unsatisfied precedence constraints. Previous: { self .if_then_alternatives [- 1 ].order } "
168+ f"Current: { alternative .order } "
169+ )
146170
147- alternative = IfThenAlternative (condition , saga )
148171 self .if_then_alternatives .append (alternative )
149172 return self
150173
151- def else_then (self , saga : Saga ) -> ConditionalSagaStep :
174+ def else_then (self , alternative : Union [ ElseThenAlternative , Saga ] ) -> ConditionalSagaStep :
152175 """Set the ``ElseThenAlternative`` with the given saga.
153176
154- :param saga : The saga to be executed if not any condition is met.
177+ :param alternative : The saga to be executed if not any condition is met.
155178 :return: This method returns the same instance that is called.
156179 """
157180
158181 if self .else_then_alternative is not None :
159182 raise MultipleElseThenException ()
160183
161- alternative = ElseThenAlternative (saga )
184+ if not isinstance (alternative , ElseThenAlternative ):
185+ alternative = ElseThenAlternative (alternative )
186+
162187 self .else_then_alternative = alternative
163188 return self
164189
@@ -200,35 +225,36 @@ def __iter__(self) -> Iterable:
200225
201226
202227@runtime_checkable
203- class IfThenAlternativeWrapper (Protocol ):
228+ class IfThenAlternativeDecoratorWrapper (Protocol ):
204229 """TODO"""
205230
206- meta : IfThenAlternativeMeta
231+ meta : IfThenAlternativeDecoratorMeta
232+ __call__ : ConditionCallback
207233
208234
209- class IfThenAlternativeMeta :
235+ class IfThenAlternativeDecoratorMeta :
210236 """TODO"""
211237
212- def __init__ (self , func : ConditionCallback , alternative : IfThenAlternative ):
213- self ._func = func
238+ def __init__ (self , inner : ConditionCallback , alternative : IfThenAlternative ):
239+ self ._inner = inner
214240 self ._alternative = alternative
215241
216242 @cached_property
217243 def alternative (self ) -> IfThenAlternative :
218244 """TODO"""
219- self ._alternative .condition = SagaOperation (self ._func )
245+ self ._alternative .condition = SagaOperation (self ._inner )
220246 return self ._alternative
221247
222248
223249class IfThenAlternative :
224250 """If Then Alternative class."""
225251
226252 def __init__ (
227- self , condition : Union [SagaOperation , ConditionCallback ] = None , saga : Saga = None , order : Optional [int ] = None
253+ self ,
254+ saga : Saga ,
255+ condition : Optional [Union [ConditionCallback , SagaOperation [ConditionCallback ]]] = None ,
256+ order : Optional [int ] = None ,
228257 ):
229- if saga is None :
230- raise ValueError ("TODO" )
231-
232258 if not isinstance (condition , SagaOperation ):
233259 condition = SagaOperation (condition )
234260
@@ -258,10 +284,10 @@ def from_raw(cls, raw: Union[dict[str, Any], IfThenAlternative], **kwargs) -> If
258284
259285 return cls (** current )
260286
261- def __call__ (self , type_ : ConditionCallback ) -> Union [ ConditionCallback , IfThenAlternativeMeta ] :
262- meta = IfThenAlternativeMeta ( type_ , self )
263- type_ .meta = meta
264- return type_
287+ def __call__ (self , func : ConditionCallback ) -> IfThenAlternativeDecoratorWrapper :
288+ meta = IfThenAlternativeDecoratorMeta ( func , self )
289+ func .meta = meta
290+ return func
265291
266292 def validate (self ) -> None :
267293 """Check if the alternative is valid.
@@ -283,6 +309,9 @@ def raw(self) -> dict[str, Any]:
283309 "saga" : self .saga .raw ,
284310 }
285311
312+ def __repr__ (self ) -> str :
313+ return f"{ type (self ).__name__ } { tuple (self )} "
314+
286315 def __eq__ (self , other : Any ) -> bool :
287316 return isinstance (other , type (self )) and tuple (self ) == tuple (other )
288317
@@ -294,17 +323,18 @@ def __iter__(self):
294323
295324
296325@runtime_checkable
297- class ElseThenAlternativeWrapper (Protocol ):
326+ class ElseThenAlternativeDecoratorWrapper (Protocol ):
298327 """TODO"""
299328
300- meta : ElseThenAlternativeMeta
329+ meta : ElseThenAlternativeDecoratorMeta
330+ __call__ : Callable
301331
302332
303- class ElseThenAlternativeMeta :
333+ class ElseThenAlternativeDecoratorMeta :
304334 """TODO"""
305335
306- def __init__ (self , func : Callable , alternative : ElseThenAlternative ):
307- self ._func = func
336+ def __init__ (self , inner : Callable , alternative : ElseThenAlternative ):
337+ self ._inner = inner
308338 self ._alternative = alternative
309339
310340 @cached_property
@@ -316,10 +346,7 @@ def alternative(self) -> ElseThenAlternative:
316346class ElseThenAlternative :
317347 """Else Then Alternative class."""
318348
319- def __init__ (self , saga : Saga = None ):
320- if saga is None :
321- raise ValueError ("TODO" )
322-
349+ def __init__ (self , saga : Saga ):
323350 self .saga = saga
324351
325352 @classmethod
@@ -343,10 +370,10 @@ def from_raw(cls, raw: Union[dict[str, Any], ElseThenAlternative], **kwargs) ->
343370
344371 return cls (** current )
345372
346- def __call__ (self , type_ : Callable ) -> Union [ Callable , ElseThenAlternativeMeta ] :
347- meta = ElseThenAlternativeMeta ( type_ , self )
348- type_ .meta = meta
349- return type_
373+ def __call__ (self , func : Callable ) -> ElseThenAlternativeDecoratorWrapper :
374+ meta = ElseThenAlternativeDecoratorMeta ( func , self )
375+ func .meta = meta
376+ return func
350377
351378 def validate (self ) -> None :
352379 """Check if the alternative is valid.
@@ -367,6 +394,9 @@ def raw(self) -> dict[str, Any]:
367394 "saga" : self .saga .raw ,
368395 }
369396
397+ def __repr__ (self ) -> str :
398+ return f"{ type (self ).__name__ } { tuple (self )} "
399+
370400 def __eq__ (self , other : Any ) -> bool :
371401 return isinstance (other , type (self )) and tuple (self ) == tuple (other )
372402
0 commit comments