Coverage for src/dataknobs_fsm/core/state.py: 58%
173 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-08 14:11 -0700
« prev ^ index » next coverage.py v7.11.0, created at 2025-11-08 14:11 -0700
1"""Core state definitions and instances for FSM.
3This module provides the fundamental building blocks for state management in
4dataknobs-fsm. States are the nodes in the state network graph, representing
5discrete steps in a workflow.
7Architecture:
8 The state system uses a two-level design:
10 **StateDefinition (Blueprint):**
11 - Defines the static structure of a state
12 - Schema, functions, resources, retry logic
13 - Immutable once created
14 - Shared across multiple executions
16 **StateInstance (Runtime):**
17 - Runtime instance of a StateDefinition
18 - Holds execution data for a specific workflow run
19 - Tracks status, timing, errors, resources
20 - Manages data mode handlers
22 **Separation Benefits:**
23 - Definition can be reused across executions
24 - Instance holds execution-specific state
25 - Clean separation of static vs dynamic concerns
27State Lifecycle:
28 A state instance progresses through a defined lifecycle:
30 **1. Creation:**
31 - StateInstance created from StateDefinition
32 - Status: PENDING
33 - No data yet
35 **2. Entry:**
36 - enter() called with input data
37 - Status: ACTIVE
38 - Data mode handler applies transformations
39 - Pre-validation functions run
40 - Resources acquired
42 **3. Processing:**
43 - Validation functions run
44 - Transform functions modify data
45 - Can modify_data() for additional changes
46 - Can pause() / resume() execution
48 **4. Exit:**
49 - exit() called to finalize state
50 - Data mode handler commits or rolls back
51 - Status: COMPLETED or FAILED
52 - Resources can be released
53 - Duration calculated
55 **5. Error Handling:**
56 - fail() marks state as FAILED
57 - Error tracking (error_count, last_error)
58 - Retry logic can re-enter state
59 - Transaction rollback if configured
61Data Handling Modes:
62 States use configurable data handling modes to control how data flows:
64 **COPY Mode (DataHandlingMode.COPY):**
65 - Deep copy on entry
66 - Modifications to local copy
67 - Commit on exit
68 - Thread-safe, memory-intensive
69 - Best for: Production, concurrent processing
71 **REFERENCE Mode (DataHandlingMode.REFERENCE):**
72 - Lazy loading with optimistic locking
73 - Version tracking for conflicts
74 - Moderate memory usage
75 - Best for: Large datasets, memory-constrained
77 **DIRECT Mode (DataHandlingMode.DIRECT):**
78 - In-place modification
79 - No copying, fastest performance
80 - Not thread-safe
81 - Best for: Single-threaded, performance-critical
83 Each state can specify a preferred data_mode, or inherit from FSM-level
84 configuration.
86State Types:
87 States can be classified by their role in the workflow:
89 **NORMAL:**
90 - Standard processing state
91 - Most states are NORMAL
92 - Can have incoming and outgoing transitions
94 **START:**
95 - Entry point for workflow
96 - No incoming transitions
97 - Exactly one per network (typically)
99 **END:**
100 - Terminal state
101 - No outgoing transitions
102 - Workflow completes here
104 **START_END:**
105 - Both entry and exit
106 - For simple single-state FSMs
108 **ERROR:**
109 - Error handling state
110 - Typically has special error recovery logic
112 **CHOICE:**
113 - Branching decision point
114 - Multiple outgoing transitions
115 - Conditional logic determines path
117 **WAIT:**
118 - Pause/synchronization point
119 - Waits for external event
120 - Can have timeout
122 **PARALLEL:**
123 - Parallel execution split/join
124 - Spawns concurrent paths
126Resource Management:
127 States can declare resource requirements:
129 **Resource Types:**
130 - Database connections
131 - API clients
132 - File handles
133 - External services
134 - Memory allocations
136 **Lifecycle:**
137 - Requirements declared in StateDefinition
138 - Resources acquired on state entry
139 - Tracked in StateInstance.acquired_resources
140 - Released on state exit or error
142 **Resource Pooling:**
143 - Resource managers can pool connections
144 - States request from pool on entry
145 - Return to pool on exit
146 - Reduces acquisition overhead
148Validation and Schemas:
149 States can validate data using schemas:
151 **StateSchema:**
152 - Defines expected field types
153 - Required vs optional fields
154 - Type constraints
155 - Extra field handling
157 **Validation Functions:**
158 - Pre-validation: Before state entry
159 - Validation: After entry, before transform
160 - Custom validation logic
161 - Return (is_valid, errors)
163 **Validation Flow:**
164 1. Schema validation (if schema defined)
165 2. Pre-validation functions
166 3. State entry
167 4. Validation functions
168 5. Transform functions
170Transformation:
171 States can transform data via transform functions:
173 **Transform Functions:**
174 - Modify data during state processing
175 - Multiple transforms can chain
176 - Applied in order of registration
177 - Return modified data
179 **Use Cases:**
180 - Data enrichment
181 - Format conversion
182 - Filtering
183 - Aggregation
184 - Computation
186Error Handling and Retries:
187 States support automatic retry on failure:
189 **Retry Configuration:**
190 - retry_count: Number of retry attempts
191 - retry_delay: Delay between retries (seconds)
192 - Configured in StateDefinition
194 **Retry Behavior:**
195 - State fails, error recorded
196 - If retries remaining, re-enter state
197 - Exponential backoff possible
198 - If all retries exhausted, fail workflow
200 **Error Tracking:**
201 - error_count: Total errors encountered
202 - last_error: Most recent error message
203 - Status: FAILED after all retries exhausted
205Transaction Support:
206 States participate in transactions when configured:
208 **Transaction Modes:**
209 - NONE: No transactional guarantees
210 - OPTIMISTIC: Commit at workflow end
211 - PESSIMISTIC: Commit at each state
213 **State Integration:**
214 - StateInstance.transaction: Active transaction
215 - Data changes recorded in transaction log
216 - Rollback on error
217 - Commit on success
219Note:
220 This is an internal API used by the core FSM and execution engines.
221 Users typically interact with states indirectly through the SimpleFSM,
222 AsyncSimpleFSM, or AdvancedFSM APIs.
224See Also:
225 - :class:`~dataknobs_fsm.core.fsm.FSM`: Core FSM class
226 - :class:`~dataknobs_fsm.core.network.StateNetwork`: State graph container
227 - :class:`~dataknobs_fsm.core.data_modes.DataHandlingMode`: Data mode enum
228 - :class:`~dataknobs_fsm.core.data_modes.DataModeManager`: Data mode handler manager
229 - :class:`~dataknobs_fsm.functions.base.IValidationFunction`: Validation function interface
230 - :class:`~dataknobs_fsm.functions.base.ITransformFunction`: Transform function interface
231"""
233from dataclasses import dataclass, field as dataclass_field
234from datetime import datetime
235from enum import Enum
236from typing import Any, Dict, List, Set, Tuple, TYPE_CHECKING
237from uuid import uuid4
239from dataknobs_data.fields import Field
240from dataknobs_fsm.core.data_modes import DataHandlingMode, DataModeManager
241from dataknobs_fsm.core.transactions import Transaction
242from dataknobs_fsm.functions.base import (
243 IValidationFunction,
244 ITransformFunction,
245 IEndStateTestFunction,
246 ResourceConfig,
247)
249if TYPE_CHECKING:
250 from dataknobs_fsm.core.arc import ArcDefinition
253class StateType(Enum):
254 """Type of state in the FSM.
256 State types classify states by their role in the workflow. The type
257 influences how the execution engine treats the state and what transitions
258 are valid.
260 Attributes:
261 NORMAL: Regular processing state with standard behavior. Can have
262 both incoming and outgoing transitions. Most states are NORMAL.
263 START: Entry point state where workflow execution begins. Typically
264 has no incoming transitions. Each network should have exactly one
265 START state (or START_END).
266 END: Terminal state where workflow execution completes. Has no
267 outgoing transitions. Networks can have multiple END states for
268 different completion paths.
269 START_END: Combined entry and exit state for simple single-state
270 FSMs. Acts as both START and END simultaneously.
271 ERROR: Error handling state for workflow failures. Typically has
272 special error recovery logic and may have transitions back to
273 recovery states.
274 CHOICE: Decision/branching state with conditional logic. Has multiple
275 outgoing transitions with conditions that determine the path taken.
276 WAIT: Pause/synchronization state that waits for external events.
277 Can have timeout configuration. Used for async workflows.
278 PARALLEL: Parallel execution split/join point. Spawns concurrent
279 execution paths that may later merge. Used for parallel workflows.
281 Note:
282 State type affects validation rules:
283 - START states typically cannot have incoming arcs
284 - END states typically cannot have outgoing arcs
285 - CHOICE states must have multiple outgoing arcs with conditions
286 """
288 NORMAL = "normal" # Regular processing state
289 START = "start" # Entry point state
290 END = "end" # Terminal state
291 START_END = "start_end" # Both entry and terminal state (for simple FSMs)
292 ERROR = "error" # Error handling state
293 CHOICE = "choice" # Decision/branching state
294 WAIT = "wait" # Waiting/pause state
295 PARALLEL = "parallel" # Parallel execution state
298class StateStatus(Enum):
299 """Status of a state instance during execution.
301 State status tracks the current execution state of a StateInstance.
302 The status changes as the instance progresses through its lifecycle.
304 Lifecycle:
305 Normal flow: PENDING → ACTIVE → COMPLETED
306 Failure flow: PENDING → ACTIVE → FAILED
307 Skip flow: PENDING → SKIPPED
308 Pause flow: PENDING → ACTIVE → PAUSED → ACTIVE → COMPLETED
310 Attributes:
311 PENDING: State instance created but not yet entered. Initial status
312 for all state instances. No data processing has occurred.
313 ACTIVE: State is currently being processed. Entry functions have run,
314 and transform/validation functions are executing or have executed.
315 Resources may be acquired.
316 COMPLETED: State processing completed successfully. Exit functions
317 have run, data committed, resources released. This is a terminal
318 status for this state instance.
319 FAILED: State processing failed with an error. Error has been recorded
320 in last_error field. If retry_count > 0, state may be re-entered.
321 Otherwise, this is a terminal status.
322 SKIPPED: State was skipped during execution, typically due to
323 conditional logic or optimization. No processing occurred. This
324 is a terminal status for this state instance.
325 PAUSED: State execution has been temporarily paused, typically in
326 debugging scenarios or waiting for external events. Can transition
327 back to ACTIVE via resume().
329 Status Transitions:
330 Valid transitions:
331 - PENDING → ACTIVE (via enter())
332 - PENDING → SKIPPED (via skip())
333 - ACTIVE → COMPLETED (via exit() with success)
334 - ACTIVE → FAILED (via fail() or exception)
335 - ACTIVE → PAUSED (via pause())
336 - PAUSED → ACTIVE (via resume())
338 Note:
339 Status is managed internally by the StateInstance class. Users
340 typically observe status via execution results or debugging tools.
341 """
343 PENDING = "pending" # Not yet entered
344 ACTIVE = "active" # Currently processing
345 COMPLETED = "completed" # Successfully completed
346 FAILED = "failed" # Failed with error
347 SKIPPED = "skipped" # Skipped in execution
348 PAUSED = "paused" # Paused execution
351@dataclass
352class StateSchema:
353 """Schema definition for state data."""
355 fields: List[Field]
356 required_fields: Set[str] = dataclass_field(default_factory=set)
357 constraints: Dict[str, Any] = dataclass_field(default_factory=dict)
358 allow_extra_fields: bool = True
360 def validate(self, data: Dict[str, Any]) -> Tuple[bool, List[str]]:
361 """Validate data against schema.
363 Args:
364 data: Data to validate.
366 Returns:
367 Tuple of (is_valid, error_messages).
368 """
369 errors = []
371 # Check required fields
372 for field_name in self.required_fields:
373 if field_name not in data:
374 errors.append(f"Required field '{field_name}' is missing")
376 # Check field types
377 field_map = {f.name: f for f in self.fields}
378 for field_name, value in data.items():
379 if field_name in field_map:
380 field_def = field_map[field_name]
381 test_field = Field(field_name, value, field_def.type)
382 if not test_field.validate():
383 errors.append(
384 f"Field '{field_name}' has invalid type. "
385 f"Expected {field_def.type}, got {type(value).__name__}"
386 )
387 elif not self.allow_extra_fields:
388 errors.append(f"Unexpected field '{field_name}'")
390 return len(errors) == 0, errors
393@dataclass
394class StateDefinition:
395 """Definition of a state in the FSM.
397 StateDefinition is the static blueprint for a state, defining its structure,
398 schema, functions, resources, and execution configuration. It is immutable
399 once created and can be reused across multiple executions.
401 Architecture:
402 StateDefinition follows a declarative design pattern:
403 - Define structure, not behavior
404 - Functions referenced by interface, not implementation
405 - Configuration-driven execution
406 - Immutable and reusable
408 Key Components:
409 **Identity:**
410 - name: Unique identifier within network
411 - type: Role in workflow (START/END/NORMAL/etc)
412 - description: Human-readable description
413 - metadata: Additional custom attributes
415 **Data Validation:**
416 - schema: Optional StateSchema for data validation
417 - pre_validation_functions: Run before state entry
418 - validation_functions: Run after entry, before transform
419 - Ensures data quality throughout workflow
421 **Data Transformation:**
422 - transform_functions: Modify data during processing
423 - Multiple transforms chain in order
424 - Each returns modified data
426 **Resource Requirements:**
427 - resource_requirements: List of ResourceConfig
428 - Database connections, API clients, etc.
429 - Acquired on entry, released on exit
431 **Execution Configuration:**
432 - timeout: Max execution time in seconds
433 - retry_count: Number of retry attempts on failure
434 - retry_delay: Delay between retries in seconds
435 - data_mode: Preferred data handling mode
437 **Network Integration:**
438 - outgoing_arcs: List of transitions to other states
439 - end_test_function: Determines if this is an end state
441 Data Modes:
442 States can specify a preferred data_mode:
443 - COPY: Deep copy for thread safety (default)
444 - REFERENCE: Lazy loading for memory efficiency
445 - DIRECT: In-place modification for performance
446 - None: Inherit from FSM-level configuration
448 Validation:
449 Multiple levels of validation:
450 1. Schema validation (structural)
451 2. Pre-validation functions (business logic before entry)
452 3. Validation functions (business logic after entry)
454 Resource Management:
455 States declare resource requirements:
456 - Specified as ResourceConfig objects
457 - Include resource type, name, and configuration
458 - Execution engine acquires before state entry
459 - Released after state exit or on error
461 Error Handling:
462 Built-in retry support:
463 - retry_count: Number of attempts (0 = no retry)
464 - retry_delay: Delay between attempts in seconds
465 - Exponential backoff can be implemented in retry logic
466 - After all retries exhausted, state fails
468 Note:
469 StateDefinition is immutable after creation. To modify a state,
470 create a new StateDefinition. This ensures consistency when the
471 same definition is used across multiple executions.
473 Functions are stored as references (interfaces), not implementations.
474 The actual function implementations are registered in the FSM's
475 FunctionRegistry.
477 See Also:
478 - :class:`~dataknobs_fsm.core.state.StateInstance`: Runtime instance
479 - :class:`~dataknobs_fsm.core.state.StateSchema`: Data schema
480 - :class:`~dataknobs_fsm.core.state.StateType`: State type enum
481 - :class:`~dataknobs_fsm.functions.base.IValidationFunction`: Validation interface
482 - :class:`~dataknobs_fsm.functions.base.ITransformFunction`: Transform interface
483 """
485 name: str
486 type: StateType = StateType.NORMAL
487 description: str = ""
488 metadata: Dict[str, Any] = dataclass_field(default_factory=dict)
490 # Schema and data handling
491 schema: StateSchema | None = None
492 data_mode: DataHandlingMode | None = None # Preferred data mode
494 # Resource requirements
495 resource_requirements: List[ResourceConfig] = dataclass_field(default_factory=list)
497 # Functions
498 pre_validation_functions: List[IValidationFunction] = dataclass_field(default_factory=list)
499 validation_functions: List[IValidationFunction] = dataclass_field(default_factory=list)
500 transform_functions: List[ITransformFunction] = dataclass_field(default_factory=list)
501 end_test_function: IEndStateTestFunction | None = None
503 # Arc references (will be populated when building network)
504 outgoing_arcs: List["ArcDefinition"] = dataclass_field(default_factory=list)
506 # Execution settings
507 timeout: float | None = None # Timeout in seconds
508 retry_count: int = 0 # Number of retries on failure
509 retry_delay: float = 1.0 # Delay between retries in seconds
511 def is_start_state(self) -> bool:
512 """Check if this is a start state.
514 Returns:
515 True if this is a start state.
516 """
517 return self.type == StateType.START
519 def is_end_state(self) -> bool:
520 """Check if this is an end state.
522 Returns:
523 True if this is an end state.
524 """
525 return self.type == StateType.END
527 @property
528 def is_start(self) -> bool:
529 """Property alias for is_start_state()."""
530 return self.is_start_state()
532 @property
533 def is_end(self) -> bool:
534 """Property alias for is_end_state()."""
535 return self.is_end_state()
537 @property
538 def arcs(self) -> List["ArcDefinition"]:
539 """Get the outgoing arcs from this state."""
540 return self.outgoing_arcs
542 def validate_data(self, data: Dict[str, Any]) -> Tuple[bool, List[str]]:
543 """Validate data against state schema.
545 Args:
546 data: Data to validate.
548 Returns:
549 Tuple of (is_valid, error_messages).
550 """
551 if self.schema is None:
552 return True, []
553 return self.schema.validate(data)
555 def add_pre_validation_function(self, func: IValidationFunction) -> None:
556 """Add a pre-validation function.
558 Args:
559 func: Pre-validation function to add.
560 """
561 self.pre_validation_functions.append(func)
563 def add_validation_function(self, func: IValidationFunction) -> None:
564 """Add a validation function.
566 Args:
567 func: Validation function to add.
568 """
569 self.validation_functions.append(func)
571 def add_transform_function(self, func: ITransformFunction) -> None:
572 """Add a transform function.
574 Args:
575 func: Transform function to add.
576 """
577 self.transform_functions.append(func)
579 def add_outgoing_arc(self, arc: "ArcDefinition") -> None:
580 """Add an outgoing arc.
582 Args:
583 arc: Arc definition to add.
584 """
585 self.outgoing_arcs.append(arc)
588@dataclass
589class StateInstance:
590 """Runtime instance of a state.
592 StateInstance represents a single execution of a StateDefinition within
593 a workflow. It holds the runtime data, tracks execution status, manages
594 resources, and implements the state lifecycle.
596 Architecture:
597 StateInstance follows the Instance pattern:
598 - One StateDefinition → Many StateInstances
599 - Each instance is independent
600 - Instance holds mutable execution state
601 - Definition holds immutable structure
603 Lifecycle:
604 StateInstance progresses through a defined lifecycle:
606 **1. Creation:**
607 ```python
608 from dataknobs_fsm.core.state import StateDefinition, StateInstance, StateType
610 # Define state first
611 state_def = StateDefinition(name="process", type=StateType.NORMAL)
613 # Create instance
614 instance = StateInstance(definition=state_def)
615 # Status: PENDING
616 # No data yet
617 ```
619 **2. Entry:**
620 ```python
621 instance.enter(input_data)
622 # Status: ACTIVE
623 # Data mode handler applies transformations
624 # Resources acquired
625 # Execution tracking begins
626 ```
628 **3. Processing:**
629 ```python
630 # Validation, transformation happen
631 instance.modify_data(updates)
632 # Can pause/resume if needed
633 ```
635 **4. Exit:**
636 ```python
637 result_data = instance.exit(commit=True)
638 # Status: COMPLETED (or FAILED)
639 # Data mode handler commits changes
640 # Resources released
641 # Duration calculated
642 ```
644 **5. Error Handling:**
645 ```python
646 instance.fail(error_message)
647 # Status: FAILED
648 # Error tracking updated
649 # Can retry if retry_count > 0
650 ```
652 Attributes:
653 id (str): Unique identifier for this instance (UUID)
654 definition (StateDefinition): The state blueprint being executed
655 status (StateStatus): Current execution status (PENDING/ACTIVE/COMPLETED/FAILED/etc)
656 data (Dict[str, Any]): Current state data
657 data_mode_manager (DataModeManager | None): Manages data handling modes
658 data_handler (Any | None): Active data mode handler (COPY/REFERENCE/DIRECT)
659 transaction (Transaction | None): Active transaction if using transactional mode
660 acquired_resources (Dict[str, Any]): Resources acquired for this instance
661 entry_time (datetime | None): When state was entered
662 exit_time (datetime | None): When state was exited
663 execution_count (int): Number of times state has been entered
664 error_count (int): Number of errors encountered
665 last_error (str | None): Most recent error message
666 executed_arcs (List[str]): IDs of arcs executed from this state
667 next_state (str | None): Name of next state to transition to
669 Data Handling:
670 StateInstance uses data mode handlers to manage how data flows:
672 **COPY Mode:**
673 - Deep copy on entry via data_handler.on_entry()
674 - Modifications to local copy
675 - Commit on exit via data_handler.on_exit(commit=True)
676 - Thread-safe, memory-intensive
678 **REFERENCE Mode:**
679 - Lazy loading with version tracking
680 - Optimistic locking for conflicts
681 - on_modification() tracks changes
682 - Memory-efficient
684 **DIRECT Mode:**
685 - In-place modification
686 - No copying overhead
687 - Fastest performance
688 - Not thread-safe
690 Resource Management:
691 StateInstance tracks acquired resources:
693 **Lifecycle:**
694 1. acquire: add_resource(name, handle)
695 2. use: get_resource(name) → handle
696 3. release: release_resources()
698 **Automatic Cleanup:**
699 - Resources released on exit()
700 - Resources released on fail()
701 - Ensures no resource leaks
703 Execution Tracking:
704 StateInstance tracks detailed execution metrics:
706 **Timing:**
707 - entry_time: When processing started
708 - exit_time: When processing completed
709 - get_duration(): Calculates elapsed time
711 **Counts:**
712 - execution_count: Total entries (for retries)
713 - error_count: Total errors encountered
714 - executed_arcs: History of transitions
716 **State:**
717 - status: Current execution status
718 - next_state: Determined next state
719 - last_error: Most recent error message
721 Transaction Support:
722 StateInstance participates in transactions:
724 **Integration:**
725 - transaction field holds active transaction
726 - Data changes logged to transaction
727 - Rollback on fail()
728 - Commit on exit(commit=True)
730 **Modes:**
731 - NONE: No transaction tracking
732 - OPTIMISTIC: Commit at workflow end
733 - PESSIMISTIC: Commit at each state exit
735 Methods:
736 **Lifecycle:**
737 - enter(input_data): Enter state with data
738 - exit(commit=True): Exit and finalize
739 - fail(error): Mark as failed
740 - skip(): Skip this state
742 **Control:**
743 - pause(): Temporarily pause
744 - resume(): Resume from pause
745 - modify_data(updates): Update state data
747 **Resources:**
748 - add_resource(name, resource): Acquire resource
749 - get_resource(name): Get acquired resource
750 - release_resources(): Release all resources
752 **Tracking:**
753 - record_arc_execution(arc_id): Track transition
754 - get_duration(): Get execution time
755 - to_dict(): Serialize to dictionary
757 Note:
758 StateInstance is managed by execution engines. Users typically don't
759 create or manipulate instances directly, but may observe them via
760 debugging tools or execution results.
762 Each workflow execution creates fresh StateInstances, even when reusing
763 StateDefinitions. This ensures execution isolation.
765 See Also:
766 - :class:`~dataknobs_fsm.core.state.StateDefinition`: State blueprint
767 - :class:`~dataknobs_fsm.core.state.StateStatus`: Status enum
768 - :class:`~dataknobs_fsm.core.data_modes.DataModeManager`: Data mode manager
769 - :class:`~dataknobs_fsm.core.transactions.Transaction`: Transaction support
770 """
772 id: str = dataclass_field(default_factory=lambda: str(uuid4()))
773 definition: StateDefinition = None
774 status: StateStatus = StateStatus.PENDING
776 # Data handling
777 data: Dict[str, Any] = dataclass_field(default_factory=dict)
778 data_mode_manager: DataModeManager | None = None
779 data_handler: Any | None = None # Active data handler
781 # Transaction participation
782 transaction: Transaction | None = None
784 # Resource access
785 acquired_resources: Dict[str, Any] = dataclass_field(default_factory=dict)
787 # Execution tracking
788 entry_time: datetime | None = None
789 exit_time: datetime | None = None
790 execution_count: int = 0
791 error_count: int = 0
792 last_error: str | None = None
794 # Arc execution history
795 executed_arcs: List[str] = dataclass_field(default_factory=list)
796 next_state: str | None = None
798 def __post_init__(self):
799 """Initialize data mode manager if not provided."""
800 if self.data_mode_manager is None:
801 # Use definition's data_mode if available and not None, else default to COPY
802 default_mode = DataHandlingMode.COPY
803 if self.definition and self.definition.data_mode:
804 default_mode = self.definition.data_mode
805 self.data_mode_manager = DataModeManager(default_mode)
807 def enter(self, input_data: Dict[str, Any]) -> None:
808 """Enter the state with input data.
810 Args:
811 input_data: Input data for the state.
812 """
813 self.status = StateStatus.ACTIVE
814 self.entry_time = datetime.now()
815 self.execution_count += 1
817 # Apply data mode
818 if self.data_mode_manager:
819 mode = self.definition.data_mode if self.definition and self.definition.data_mode else self.data_mode_manager.default_mode
820 self.data_handler = self.data_mode_manager.get_handler(mode)
821 self.data = self.data_handler.on_entry(input_data)
822 else:
823 self.data = input_data
825 def exit(self, commit: bool = True) -> Dict[str, Any]:
826 """Exit the state.
828 Args:
829 commit: Whether to commit data changes.
831 Returns:
832 The final state data.
833 """
834 self.exit_time = datetime.now()
836 # Handle data mode exit
837 if self.data_handler:
838 self.data = self.data_handler.on_exit(self.data, commit)
840 if self.status == StateStatus.ACTIVE:
841 self.status = StateStatus.COMPLETED
843 return self.data
845 def fail(self, error: str) -> None:
846 """Mark the state as failed.
848 Args:
849 error: Error message.
850 """
851 self.status = StateStatus.FAILED
852 self.error_count += 1
853 self.last_error = error
854 self.exit_time = datetime.now()
856 def pause(self) -> None:
857 """Pause state execution."""
858 if self.status == StateStatus.ACTIVE:
859 self.status = StateStatus.PAUSED
861 def resume(self) -> None:
862 """Resume paused state execution."""
863 if self.status == StateStatus.PAUSED:
864 self.status = StateStatus.ACTIVE
866 def skip(self) -> None:
867 """Skip this state."""
868 self.status = StateStatus.SKIPPED
869 self.exit_time = datetime.now()
871 def modify_data(self, updates: Dict[str, Any]) -> None:
872 """Modify state data.
874 Args:
875 updates: Data updates to apply.
876 """
877 if self.data_handler:
878 # Let the data handler manage modifications
879 self.data.update(updates)
880 self.data = self.data_handler.on_modification(self.data)
881 else:
882 self.data.update(updates)
884 def add_resource(self, name: str, resource: Any) -> None:
885 """Add an acquired resource.
887 Args:
888 name: Resource name.
889 resource: The resource handle/connection.
890 """
891 self.acquired_resources[name] = resource
893 def get_resource(self, name: str) -> Any | None:
894 """Get an acquired resource.
896 Args:
897 name: Resource name.
899 Returns:
900 The resource if available.
901 """
902 return self.acquired_resources.get(name)
904 def release_resources(self) -> None:
905 """Release all acquired resources."""
906 self.acquired_resources.clear()
908 def record_arc_execution(self, arc_id: str) -> None:
909 """Record that an arc was executed.
911 Args:
912 arc_id: ID of the executed arc.
913 """
914 self.executed_arcs.append(arc_id)
916 def get_duration(self) -> float | None:
917 """Get execution duration in seconds.
919 Returns:
920 Duration in seconds if available.
921 """
922 if self.entry_time and self.exit_time:
923 return (self.exit_time - self.entry_time).total_seconds()
924 elif self.entry_time:
925 return (datetime.now() - self.entry_time).total_seconds()
926 return None
928 def to_dict(self) -> Dict[str, Any]:
929 """Convert to dictionary representation.
931 Returns:
932 Dictionary with state instance data.
933 """
934 return {
935 "id": self.id,
936 "name": self.definition.name if self.definition else None,
937 "status": self.status.value,
938 "data": self.data,
939 "entry_time": self.entry_time.isoformat() if self.entry_time else None,
940 "exit_time": self.exit_time.isoformat() if self.exit_time else None,
941 "duration": self.get_duration(),
942 "execution_count": self.execution_count,
943 "error_count": self.error_count,
944 "last_error": self.last_error,
945 "executed_arcs": self.executed_arcs,
946 "next_state": self.next_state,
947 }
950# Simplified State class for network usage
951class State:
952 """Simplified state class for use in state networks."""
954 def __init__(self, name: str, **kwargs):
955 """Initialize state.
957 Args:
958 name: State name.
959 **kwargs: Additional state properties.
960 """
961 self.name = name
962 self.metadata = kwargs
963 self.resource_requirements = kwargs.get("resource_requirements", {})
965 def to_dict(self) -> Dict[str, Any]:
966 """Convert to dictionary."""
967 return {
968 "name": self.name,
969 "metadata": self.metadata,
970 "resource_requirements": self.resource_requirements
971 }
974# StateMode for backwards compatibility
975class StateMode(Enum):
976 """Mode of state operation."""
977 NORMAL = "normal"
978 PARALLEL = "parallel"
979 SEQUENTIAL = "sequential"