@@ -519,27 +519,27 @@ class EntityInstanceId:
519519 """Represents the ID of a durable entity instance."""
520520 name : str
521521 key : str
522-
522+
523523 def __str__ (self ) -> str :
524524 """Return the string representation in the format: name@key"""
525525 return f"{ self .name } @{ self .key } "
526-
526+
527527 @classmethod
528528 def from_string (cls , instance_id : str ) -> 'EntityInstanceId' :
529529 """Parse an entity instance ID from string format (name@key)."""
530530 if '@' not in instance_id :
531531 raise ValueError (f"Invalid entity instance ID format: { instance_id } . Expected format: name@key" )
532-
532+
533533 parts = instance_id .split ('@' , 1 )
534534 if len (parts ) != 2 or not parts [0 ] or not parts [1 ]:
535535 raise ValueError (f"Invalid entity instance ID format: { instance_id } . Expected format: name@key" )
536-
536+
537537 return cls (name = parts [0 ], key = parts [1 ])
538538
539539
540540class EntityOperationFailedException (Exception ):
541541 """Exception raised when an entity operation fails."""
542-
542+
543543 def __init__ (self , entity_id : EntityInstanceId , operation_name : str , failure_details : FailureDetails ):
544544 self .entity_id = entity_id
545545 self .operation_name = operation_name
@@ -549,7 +549,7 @@ def __init__(self, entity_id: EntityInstanceId, operation_name: str, failure_det
549549
550550class EntityContext :
551551 """Context for entity operations, providing access to state and scheduling capabilities."""
552-
552+
553553 def __init__ (self , instance_id : str , operation_name : str , is_new_entity : bool = False ):
554554 self ._instance_id = instance_id
555555 self ._operation_name = operation_name
@@ -642,7 +642,7 @@ def signal_entity(self, entity_id: Union[str, EntityInstanceId], operation_name:
642642 # Store the signal for later processing during entity execution
643643 if not hasattr (self , '_signals' ):
644644 self ._signals = []
645-
645+
646646 entity_id_str = str (entity_id ) if isinstance (entity_id , EntityInstanceId ) else entity_id
647647 self ._signals .append ({
648648 'entity_id' : entity_id_str ,
@@ -651,8 +651,8 @@ def signal_entity(self, entity_id: Union[str, EntityInstanceId], operation_name:
651651 })
652652
653653 def start_new_orchestration (self , orchestrator : Union [Orchestrator [TInput , TOutput ], str ], * ,
654- input : Optional [TInput ] = None ,
655- instance_id : Optional [str ] = None ) -> str :
654+ input : Optional [TInput ] = None ,
655+ instance_id : Optional [str ] = None ) -> str :
656656 """Start a new orchestration from within an entity operation.
657657
658658 Parameters
@@ -672,16 +672,16 @@ def start_new_orchestration(self, orchestrator: Union[Orchestrator[TInput, TOutp
672672 # Store the orchestration start request for later processing
673673 if not hasattr (self , '_orchestrations' ):
674674 self ._orchestrations = []
675-
675+
676676 orchestrator_name = orchestrator if isinstance (orchestrator , str ) else get_name (orchestrator )
677677 new_instance_id = instance_id or str (uuid .uuid4 ())
678-
678+
679679 self ._orchestrations .append ({
680680 'name' : orchestrator_name ,
681681 'input' : input ,
682682 'instance_id' : new_instance_id
683683 })
684-
684+
685685 return new_instance_id
686686
687687
@@ -691,42 +691,43 @@ def start_new_orchestration(self, orchestrator: Union[Orchestrator[TInput, TOutp
691691# Activities are simple functions that can be scheduled by orchestrators
692692Activity = Callable [[ActivityContext , TInput ], TOutput ]
693693
694+
694695class EntityBase :
695696 """Base class for entity implementations that provides method-based dispatch.
696-
697+
697698 This class allows entities to be implemented as classes with methods for each operation,
698699 similar to the .NET TaskEntity pattern. The entity context is automatically injected
699700 when methods are called.
700701 """
701-
702+
702703 def __init__ (self ):
703704 self ._context : Optional [EntityContext ] = None
704705 self ._state : Optional [Any ] = None
705-
706+
706707 @property
707708 def context (self ) -> EntityContext :
708709 """Get the current entity context."""
709710 if self ._context is None :
710711 raise RuntimeError ("Entity context is not available outside of operation execution" )
711712 return self ._context
712-
713+
713714 def get_state (self , state_type : type [T ] = None ) -> Optional [T ]:
714715 """Get the current state of the entity."""
715716 return self ._state
716-
717+
717718 def set_state (self , state : Any ) -> None :
718719 """Set the current state of the entity."""
719720 self ._state = state
720-
721+
721722 def signal_entity (self , entity_id : Union [str , EntityInstanceId ], operation_name : str , * ,
722723 input : Optional [Any ] = None ) -> None :
723724 """Signal another entity with an operation."""
724725 if self ._context :
725726 self ._context .signal_entity (entity_id , operation_name , input = input )
726-
727+
727728 def start_new_orchestration (self , orchestrator : Union [Orchestrator [TInput , TOutput ], str ], * ,
728- input : Optional [TInput ] = None ,
729- instance_id : Optional [str ] = None ) -> str :
729+ input : Optional [TInput ] = None ,
730+ instance_id : Optional [str ] = None ) -> str :
730731 """Start a new orchestration from within an entity operation."""
731732 if self ._context :
732733 return self ._context .start_new_orchestration (orchestrator , input = input , instance_id = instance_id )
@@ -736,12 +737,12 @@ def start_new_orchestration(self, orchestrator: Union[Orchestrator[TInput, TOutp
736737def dispatch_to_entity_method (entity_obj : Any , ctx : EntityContext , input : Any ) -> Any :
737738 """
738739 Dispatch an entity operation to the appropriate method on an entity object.
739-
740+
740741 This function implements flexible method dispatch similar to the .NET implementation:
741742 1. Look for an exact method name match (case-insensitive)
742743 2. If the entity is an EntityBase subclass, inject context and state
743744 3. Handle method parameters automatically (context, input, or both)
744-
745+
745746 Parameters
746747 ----------
747748 entity_obj : Any
@@ -750,44 +751,44 @@ def dispatch_to_entity_method(entity_obj: Any, ctx: EntityContext, input: Any) -
750751 The entity context
751752 input : Any
752753 The operation input
753-
754+
754755 Returns
755756 -------
756757 Any
757758 The result of the operation
758759 """
759760 import inspect
760-
761+
761762 # Set up entity base if applicable
762763 if isinstance (entity_obj , EntityBase ):
763764 entity_obj ._context = ctx
764765 entity_obj ._state = ctx .get_state ()
765-
766+
766767 # Look for a method with the operation name (case-insensitive)
767768 operation_name = ctx .operation_name .lower ()
768769 method = None
769-
770+
770771 for attr_name in dir (entity_obj ):
771772 if attr_name .lower () == operation_name and callable (getattr (entity_obj , attr_name )):
772773 method = getattr (entity_obj , attr_name )
773774 break
774-
775+
775776 if method is None :
776777 raise NotImplementedError (f"Entity does not implement operation '{ ctx .operation_name } '" )
777-
778+
778779 # Inspect method signature to determine parameters
779780 sig = inspect .signature (method )
780781 args = []
781782 kwargs = {}
782-
783+
783784 # Skip 'self' parameter for bound methods
784785 parameters = list (sig .parameters .values ())
785786 if parameters and parameters [0 ].name == 'self' :
786787 parameters = parameters [1 :]
787-
788+
788789 for param in parameters :
789790 param_type = param .annotation
790-
791+
791792 # Check for EntityContext parameter
792793 if param_type == EntityContext or param .name .lower () in ['context' , 'ctx' ]:
793794 if param .kind == param .POSITIONAL_OR_KEYWORD :
@@ -803,18 +804,18 @@ def dispatch_to_entity_method(entity_obj: Any, ctx: EntityContext, input: Any) -
803804 # Default positional parameter (assume it's input)
804805 elif param .kind == param .POSITIONAL_OR_KEYWORD and len (args ) == 0 :
805806 args .append (input )
806-
807+
807808 try :
808809 result = method (* args , ** kwargs )
809-
810+
810811 # Update state if entity is EntityBase
811812 if isinstance (entity_obj , EntityBase ):
812813 ctx .set_state (entity_obj ._state )
813814 entity_obj ._context = None # Clear context after operation
814-
815+
815816 return result
816-
817- except Exception as ex :
817+
818+ except Exception :
818819 # Clear context on error
819820 if isinstance (entity_obj , EntityBase ):
820821 entity_obj ._context = None
0 commit comments