3636 overload ,
3737)
3838
39- from typing_extensions import Concatenate , Literal , TypedDict
39+ from typing_extensions import (
40+ Concatenate ,
41+ Literal ,
42+ Protocol ,
43+ TypedDict ,
44+ runtime_checkable ,
45+ )
4046
4147import temporalio .api .common .v1
4248import temporalio .bridge .proto .child_workflow
6470 MethodSyncSingleParam ,
6571 MultiParamSpec ,
6672 ParamType ,
73+ ProtocolReturnType ,
6774 ReturnType ,
6875 SelfType ,
6976)
@@ -764,8 +771,64 @@ def time_ns() -> int:
764771 return _Runtime .current ().workflow_time_ns ()
765772
766773
767- # noinspection PyPep8Naming
768- class update (object ):
774+ # Needs to be defined here to avoid a circular import
775+ @runtime_checkable
776+ class UpdateMethodMultiArg (Protocol [MultiParamSpec , ProtocolReturnType ]):
777+ """Decorated workflow update functions implement this."""
778+
779+ _defn : temporalio .workflow ._UpdateDefinition
780+
781+ def __call__ (
782+ self , * args : MultiParamSpec .args , ** kwargs : MultiParamSpec .kwargs
783+ ) -> Union [ProtocolReturnType , Awaitable [ProtocolReturnType ]]:
784+ """Generic callable type callback."""
785+ ...
786+
787+ def validator (self , vfunc : Callable [MultiParamSpec , None ]) -> None :
788+ """Use to decorate a function to validate the arguments passed to the update handler."""
789+ ...
790+
791+
792+ @overload
793+ def update (
794+ fn : Callable [MultiParamSpec , Awaitable [ReturnType ]]
795+ ) -> UpdateMethodMultiArg [MultiParamSpec , ReturnType ]:
796+ ...
797+
798+
799+ @overload
800+ def update (
801+ fn : Callable [MultiParamSpec , ReturnType ]
802+ ) -> UpdateMethodMultiArg [MultiParamSpec , ReturnType ]:
803+ ...
804+
805+
806+ @overload
807+ def update (
808+ * , name : str
809+ ) -> Callable [
810+ [Callable [MultiParamSpec , ReturnType ]],
811+ UpdateMethodMultiArg [MultiParamSpec , ReturnType ],
812+ ]:
813+ ...
814+
815+
816+ @overload
817+ def update (
818+ * , dynamic : Literal [True ]
819+ ) -> Callable [
820+ [Callable [MultiParamSpec , ReturnType ]],
821+ UpdateMethodMultiArg [MultiParamSpec , ReturnType ],
822+ ]:
823+ ...
824+
825+
826+ def update (
827+ fn : Optional [CallableSyncOrAsyncType ] = None ,
828+ * ,
829+ name : Optional [str ] = None ,
830+ dynamic : Optional [bool ] = False ,
831+ ):
769832 """Decorator for a workflow update handler method.
770833
771834 This is set on any async or non-async method that you wish to be called upon
@@ -791,44 +854,33 @@ class update(object):
791854 present.
792855 """
793856
794- def __init__ (
795- self ,
796- fn : Optional [CallableSyncOrAsyncType ] = None ,
797- * ,
798- name : Optional [str ] = None ,
799- dynamic : Optional [bool ] = False ,
800- ):
801- """See :py:class:`update`."""
802- if name is not None or dynamic :
803- if name is not None and dynamic :
804- raise RuntimeError ("Cannot provide name and dynamic boolean" )
805- self ._fn = fn
806- self ._name = (
807- name if name is not None else self ._fn .__name__ if self ._fn else None
808- )
809- self ._dynamic = dynamic
810- if self ._fn is not None :
811- # Only bother to assign the definition if we are given a function. The function is not provided when
812- # extra arguments are specified - in that case, the __call__ method is invoked instead.
813- self ._assign_defn ()
814-
815- def __call__ (self , fn : CallableSyncOrAsyncType ):
816- """Call the update decorator (as when passing optional arguments)."""
817- self ._fn = fn
818- self ._assign_defn ()
819- return self
820-
821- def _assign_defn (self ) -> None :
822- assert self ._fn is not None
823- self ._defn = _UpdateDefinition (name = self ._name , fn = self ._fn , is_method = True )
824-
825- def validator (self , fn : Callable [..., None ]):
826- """Decorator for a workflow update validator method. Apply this decorator to a function to have it run before
827- the update handler. If it throws an error, the update will be rejected. The validator must not mutate workflow
828- state at all, and cannot call workflow functions which would schedule new commands (ex: starting an
829- activity).
830- """
831- self ._defn .set_validator (fn )
857+ def with_name (
858+ name : Optional [str ], fn : CallableSyncOrAsyncType
859+ ) -> CallableSyncOrAsyncType :
860+ defn = _UpdateDefinition (name = name , fn = fn , is_method = True )
861+ if defn .dynamic_vararg :
862+ raise RuntimeError (
863+ "Dynamic updates do not support a vararg third param, use Sequence[RawValue]" ,
864+ )
865+ setattr (fn , "_defn" , defn )
866+ setattr (fn , "validator" , partial (_update_validator , defn ))
867+ return fn
868+
869+ if name is not None or dynamic :
870+ if name is not None and dynamic :
871+ raise RuntimeError ("Cannot provide name and dynamic boolean" )
872+ return partial (with_name , name )
873+ if fn is None :
874+ raise RuntimeError ("Cannot create update without function or name or dynamic" )
875+ return with_name (fn .__name__ , fn )
876+
877+
878+ def _update_validator (
879+ update_def : _UpdateDefinition , fn : Optional [Callable [..., None ]] = None
880+ ):
881+ """Decorator for a workflow update validator method."""
882+ if fn is not None :
883+ update_def .set_validator (fn )
832884
833885
834886def upsert_search_attributes (attributes : temporalio .common .SearchAttributes ) -> None :
@@ -1132,7 +1184,7 @@ def _apply_to_class(
11321184 )
11331185 else :
11341186 queries [query_defn .name ] = query_defn
1135- elif isinstance (member , update ):
1187+ elif isinstance (member , UpdateMethodMultiArg ):
11361188 update_defn = member ._defn
11371189 if update_defn .name in updates :
11381190 defn_name = update_defn .name or "<dynamic>"
@@ -1350,16 +1402,19 @@ class _UpdateDefinition:
13501402 arg_types : Optional [List [Type ]] = None
13511403 ret_type : Optional [Type ] = None
13521404 validator : Optional [Callable [..., None ]] = None
1405+ dynamic_vararg : bool = False
13531406
13541407 def __post_init__ (self ) -> None :
13551408 if self .arg_types is None :
13561409 arg_types , ret_type = temporalio .common ._type_hints_from_func (self .fn )
1357- # Disallow dynamic varargs
1358- if not self .name and not _assert_dynamic_handler_args (
1359- self .fn , arg_types , self .is_method
1360- ):
1361- raise RuntimeError (
1362- "Dynamic updates do not support a vararg third param, use Sequence[RawValue]" ,
1410+ # If dynamic, assert it
1411+ if not self .name :
1412+ object .__setattr__ (
1413+ self ,
1414+ "dynamic_vararg" ,
1415+ not _assert_dynamic_handler_args (
1416+ self .fn , arg_types , self .is_method
1417+ ),
13631418 )
13641419 object .__setattr__ (self , "arg_types" , arg_types )
13651420 object .__setattr__ (self , "ret_type" , ret_type )
0 commit comments