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

1"""Core state definitions and instances for FSM. 

2 

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. 

6 

7Architecture: 

8 The state system uses a two-level design: 

9 

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 

15 

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 

21 

22 **Separation Benefits:** 

23 - Definition can be reused across executions 

24 - Instance holds execution-specific state 

25 - Clean separation of static vs dynamic concerns 

26 

27State Lifecycle: 

28 A state instance progresses through a defined lifecycle: 

29 

30 **1. Creation:** 

31 - StateInstance created from StateDefinition 

32 - Status: PENDING 

33 - No data yet 

34 

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 

41 

42 **3. Processing:** 

43 - Validation functions run 

44 - Transform functions modify data 

45 - Can modify_data() for additional changes 

46 - Can pause() / resume() execution 

47 

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 

54 

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 

60 

61Data Handling Modes: 

62 States use configurable data handling modes to control how data flows: 

63 

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 

70 

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 

76 

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 

82 

83 Each state can specify a preferred data_mode, or inherit from FSM-level 

84 configuration. 

85 

86State Types: 

87 States can be classified by their role in the workflow: 

88 

89 **NORMAL:** 

90 - Standard processing state 

91 - Most states are NORMAL 

92 - Can have incoming and outgoing transitions 

93 

94 **START:** 

95 - Entry point for workflow 

96 - No incoming transitions 

97 - Exactly one per network (typically) 

98 

99 **END:** 

100 - Terminal state 

101 - No outgoing transitions 

102 - Workflow completes here 

103 

104 **START_END:** 

105 - Both entry and exit 

106 - For simple single-state FSMs 

107 

108 **ERROR:** 

109 - Error handling state 

110 - Typically has special error recovery logic 

111 

112 **CHOICE:** 

113 - Branching decision point 

114 - Multiple outgoing transitions 

115 - Conditional logic determines path 

116 

117 **WAIT:** 

118 - Pause/synchronization point 

119 - Waits for external event 

120 - Can have timeout 

121 

122 **PARALLEL:** 

123 - Parallel execution split/join 

124 - Spawns concurrent paths 

125 

126Resource Management: 

127 States can declare resource requirements: 

128 

129 **Resource Types:** 

130 - Database connections 

131 - API clients 

132 - File handles 

133 - External services 

134 - Memory allocations 

135 

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 

141 

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 

147 

148Validation and Schemas: 

149 States can validate data using schemas: 

150 

151 **StateSchema:** 

152 - Defines expected field types 

153 - Required vs optional fields 

154 - Type constraints 

155 - Extra field handling 

156 

157 **Validation Functions:** 

158 - Pre-validation: Before state entry 

159 - Validation: After entry, before transform 

160 - Custom validation logic 

161 - Return (is_valid, errors) 

162 

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 

169 

170Transformation: 

171 States can transform data via transform functions: 

172 

173 **Transform Functions:** 

174 - Modify data during state processing 

175 - Multiple transforms can chain 

176 - Applied in order of registration 

177 - Return modified data 

178 

179 **Use Cases:** 

180 - Data enrichment 

181 - Format conversion 

182 - Filtering 

183 - Aggregation 

184 - Computation 

185 

186Error Handling and Retries: 

187 States support automatic retry on failure: 

188 

189 **Retry Configuration:** 

190 - retry_count: Number of retry attempts 

191 - retry_delay: Delay between retries (seconds) 

192 - Configured in StateDefinition 

193 

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 

199 

200 **Error Tracking:** 

201 - error_count: Total errors encountered 

202 - last_error: Most recent error message 

203 - Status: FAILED after all retries exhausted 

204 

205Transaction Support: 

206 States participate in transactions when configured: 

207 

208 **Transaction Modes:** 

209 - NONE: No transactional guarantees 

210 - OPTIMISTIC: Commit at workflow end 

211 - PESSIMISTIC: Commit at each state 

212 

213 **State Integration:** 

214 - StateInstance.transaction: Active transaction 

215 - Data changes recorded in transaction log 

216 - Rollback on error 

217 - Commit on success 

218 

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. 

223 

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""" 

232 

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 

238 

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) 

248 

249if TYPE_CHECKING: 

250 from dataknobs_fsm.core.arc import ArcDefinition 

251 

252 

253class StateType(Enum): 

254 """Type of state in the FSM. 

255 

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. 

259 

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. 

280 

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 """ 

287 

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 

296 

297 

298class StateStatus(Enum): 

299 """Status of a state instance during execution. 

300 

301 State status tracks the current execution state of a StateInstance. 

302 The status changes as the instance progresses through its lifecycle. 

303 

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 

309 

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(). 

328 

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()) 

337 

338 Note: 

339 Status is managed internally by the StateInstance class. Users 

340 typically observe status via execution results or debugging tools. 

341 """ 

342 

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 

349 

350 

351@dataclass 

352class StateSchema: 

353 """Schema definition for state data.""" 

354 

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 

359 

360 def validate(self, data: Dict[str, Any]) -> Tuple[bool, List[str]]: 

361 """Validate data against schema. 

362  

363 Args: 

364 data: Data to validate. 

365  

366 Returns: 

367 Tuple of (is_valid, error_messages). 

368 """ 

369 errors = [] 

370 

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") 

375 

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}'") 

389 

390 return len(errors) == 0, errors 

391 

392 

393@dataclass 

394class StateDefinition: 

395 """Definition of a state in the FSM. 

396 

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. 

400 

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 

407 

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 

414 

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 

420 

421 **Data Transformation:** 

422 - transform_functions: Modify data during processing 

423 - Multiple transforms chain in order 

424 - Each returns modified data 

425 

426 **Resource Requirements:** 

427 - resource_requirements: List of ResourceConfig 

428 - Database connections, API clients, etc. 

429 - Acquired on entry, released on exit 

430 

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 

436 

437 **Network Integration:** 

438 - outgoing_arcs: List of transitions to other states 

439 - end_test_function: Determines if this is an end state 

440 

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 

447 

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) 

453 

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 

460 

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 

467 

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. 

472 

473 Functions are stored as references (interfaces), not implementations. 

474 The actual function implementations are registered in the FSM's 

475 FunctionRegistry. 

476 

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 """ 

484 

485 name: str 

486 type: StateType = StateType.NORMAL 

487 description: str = "" 

488 metadata: Dict[str, Any] = dataclass_field(default_factory=dict) 

489 

490 # Schema and data handling 

491 schema: StateSchema | None = None 

492 data_mode: DataHandlingMode | None = None # Preferred data mode 

493 

494 # Resource requirements 

495 resource_requirements: List[ResourceConfig] = dataclass_field(default_factory=list) 

496 

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 

502 

503 # Arc references (will be populated when building network) 

504 outgoing_arcs: List["ArcDefinition"] = dataclass_field(default_factory=list) 

505 

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 

510 

511 def is_start_state(self) -> bool: 

512 """Check if this is a start state. 

513  

514 Returns: 

515 True if this is a start state. 

516 """ 

517 return self.type == StateType.START 

518 

519 def is_end_state(self) -> bool: 

520 """Check if this is an end state. 

521  

522 Returns: 

523 True if this is an end state. 

524 """ 

525 return self.type == StateType.END 

526 

527 @property 

528 def is_start(self) -> bool: 

529 """Property alias for is_start_state().""" 

530 return self.is_start_state() 

531 

532 @property 

533 def is_end(self) -> bool: 

534 """Property alias for is_end_state().""" 

535 return self.is_end_state() 

536 

537 @property 

538 def arcs(self) -> List["ArcDefinition"]: 

539 """Get the outgoing arcs from this state.""" 

540 return self.outgoing_arcs 

541 

542 def validate_data(self, data: Dict[str, Any]) -> Tuple[bool, List[str]]: 

543 """Validate data against state schema. 

544  

545 Args: 

546 data: Data to validate. 

547  

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) 

554 

555 def add_pre_validation_function(self, func: IValidationFunction) -> None: 

556 """Add a pre-validation function. 

557 

558 Args: 

559 func: Pre-validation function to add. 

560 """ 

561 self.pre_validation_functions.append(func) 

562 

563 def add_validation_function(self, func: IValidationFunction) -> None: 

564 """Add a validation function. 

565 

566 Args: 

567 func: Validation function to add. 

568 """ 

569 self.validation_functions.append(func) 

570 

571 def add_transform_function(self, func: ITransformFunction) -> None: 

572 """Add a transform function. 

573  

574 Args: 

575 func: Transform function to add. 

576 """ 

577 self.transform_functions.append(func) 

578 

579 def add_outgoing_arc(self, arc: "ArcDefinition") -> None: 

580 """Add an outgoing arc. 

581  

582 Args: 

583 arc: Arc definition to add. 

584 """ 

585 self.outgoing_arcs.append(arc) 

586 

587 

588@dataclass 

589class StateInstance: 

590 """Runtime instance of a state. 

591 

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. 

595 

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 

602 

603 Lifecycle: 

604 StateInstance progresses through a defined lifecycle: 

605 

606 **1. Creation:** 

607 ```python 

608 from dataknobs_fsm.core.state import StateDefinition, StateInstance, StateType 

609 

610 # Define state first 

611 state_def = StateDefinition(name="process", type=StateType.NORMAL) 

612 

613 # Create instance 

614 instance = StateInstance(definition=state_def) 

615 # Status: PENDING 

616 # No data yet 

617 ``` 

618 

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 ``` 

627 

628 **3. Processing:** 

629 ```python 

630 # Validation, transformation happen 

631 instance.modify_data(updates) 

632 # Can pause/resume if needed 

633 ``` 

634 

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 ``` 

643 

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 ``` 

651 

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 

668 

669 Data Handling: 

670 StateInstance uses data mode handlers to manage how data flows: 

671 

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 

677 

678 **REFERENCE Mode:** 

679 - Lazy loading with version tracking 

680 - Optimistic locking for conflicts 

681 - on_modification() tracks changes 

682 - Memory-efficient 

683 

684 **DIRECT Mode:** 

685 - In-place modification 

686 - No copying overhead 

687 - Fastest performance 

688 - Not thread-safe 

689 

690 Resource Management: 

691 StateInstance tracks acquired resources: 

692 

693 **Lifecycle:** 

694 1. acquire: add_resource(name, handle) 

695 2. use: get_resource(name) → handle 

696 3. release: release_resources() 

697 

698 **Automatic Cleanup:** 

699 - Resources released on exit() 

700 - Resources released on fail() 

701 - Ensures no resource leaks 

702 

703 Execution Tracking: 

704 StateInstance tracks detailed execution metrics: 

705 

706 **Timing:** 

707 - entry_time: When processing started 

708 - exit_time: When processing completed 

709 - get_duration(): Calculates elapsed time 

710 

711 **Counts:** 

712 - execution_count: Total entries (for retries) 

713 - error_count: Total errors encountered 

714 - executed_arcs: History of transitions 

715 

716 **State:** 

717 - status: Current execution status 

718 - next_state: Determined next state 

719 - last_error: Most recent error message 

720 

721 Transaction Support: 

722 StateInstance participates in transactions: 

723 

724 **Integration:** 

725 - transaction field holds active transaction 

726 - Data changes logged to transaction 

727 - Rollback on fail() 

728 - Commit on exit(commit=True) 

729 

730 **Modes:** 

731 - NONE: No transaction tracking 

732 - OPTIMISTIC: Commit at workflow end 

733 - PESSIMISTIC: Commit at each state exit 

734 

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 

741 

742 **Control:** 

743 - pause(): Temporarily pause 

744 - resume(): Resume from pause 

745 - modify_data(updates): Update state data 

746 

747 **Resources:** 

748 - add_resource(name, resource): Acquire resource 

749 - get_resource(name): Get acquired resource 

750 - release_resources(): Release all resources 

751 

752 **Tracking:** 

753 - record_arc_execution(arc_id): Track transition 

754 - get_duration(): Get execution time 

755 - to_dict(): Serialize to dictionary 

756 

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. 

761 

762 Each workflow execution creates fresh StateInstances, even when reusing 

763 StateDefinitions. This ensures execution isolation. 

764 

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 """ 

771 

772 id: str = dataclass_field(default_factory=lambda: str(uuid4())) 

773 definition: StateDefinition = None 

774 status: StateStatus = StateStatus.PENDING 

775 

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 

780 

781 # Transaction participation 

782 transaction: Transaction | None = None 

783 

784 # Resource access 

785 acquired_resources: Dict[str, Any] = dataclass_field(default_factory=dict) 

786 

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 

793 

794 # Arc execution history 

795 executed_arcs: List[str] = dataclass_field(default_factory=list) 

796 next_state: str | None = None 

797 

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) 

806 

807 def enter(self, input_data: Dict[str, Any]) -> None: 

808 """Enter the state with input data. 

809  

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 

816 

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 

824 

825 def exit(self, commit: bool = True) -> Dict[str, Any]: 

826 """Exit the state. 

827  

828 Args: 

829 commit: Whether to commit data changes. 

830  

831 Returns: 

832 The final state data. 

833 """ 

834 self.exit_time = datetime.now() 

835 

836 # Handle data mode exit 

837 if self.data_handler: 

838 self.data = self.data_handler.on_exit(self.data, commit) 

839 

840 if self.status == StateStatus.ACTIVE: 

841 self.status = StateStatus.COMPLETED 

842 

843 return self.data 

844 

845 def fail(self, error: str) -> None: 

846 """Mark the state as failed. 

847  

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() 

855 

856 def pause(self) -> None: 

857 """Pause state execution.""" 

858 if self.status == StateStatus.ACTIVE: 

859 self.status = StateStatus.PAUSED 

860 

861 def resume(self) -> None: 

862 """Resume paused state execution.""" 

863 if self.status == StateStatus.PAUSED: 

864 self.status = StateStatus.ACTIVE 

865 

866 def skip(self) -> None: 

867 """Skip this state.""" 

868 self.status = StateStatus.SKIPPED 

869 self.exit_time = datetime.now() 

870 

871 def modify_data(self, updates: Dict[str, Any]) -> None: 

872 """Modify state data. 

873  

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) 

883 

884 def add_resource(self, name: str, resource: Any) -> None: 

885 """Add an acquired resource. 

886  

887 Args: 

888 name: Resource name. 

889 resource: The resource handle/connection. 

890 """ 

891 self.acquired_resources[name] = resource 

892 

893 def get_resource(self, name: str) -> Any | None: 

894 """Get an acquired resource. 

895  

896 Args: 

897 name: Resource name. 

898  

899 Returns: 

900 The resource if available. 

901 """ 

902 return self.acquired_resources.get(name) 

903 

904 def release_resources(self) -> None: 

905 """Release all acquired resources.""" 

906 self.acquired_resources.clear() 

907 

908 def record_arc_execution(self, arc_id: str) -> None: 

909 """Record that an arc was executed. 

910  

911 Args: 

912 arc_id: ID of the executed arc. 

913 """ 

914 self.executed_arcs.append(arc_id) 

915 

916 def get_duration(self) -> float | None: 

917 """Get execution duration in seconds. 

918  

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 

927 

928 def to_dict(self) -> Dict[str, Any]: 

929 """Convert to dictionary representation. 

930  

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 } 

948 

949 

950# Simplified State class for network usage 

951class State: 

952 """Simplified state class for use in state networks.""" 

953 

954 def __init__(self, name: str, **kwargs): 

955 """Initialize state. 

956  

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", {}) 

964 

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 } 

972 

973 

974# StateMode for backwards compatibility 

975class StateMode(Enum): 

976 """Mode of state operation.""" 

977 NORMAL = "normal" 

978 PARALLEL = "parallel" 

979 SEQUENTIAL = "sequential"