Coverage for src/dataknobs_fsm/core/state.py: 58%

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

2

3 This module provides the fundamental building blocks for state management in

4 dataknobs-fsm. States are the nodes in the state network graph, representing

5 discrete steps in a workflow.

6

7 Architecture:

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

27 State 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

61 Data 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

86 State 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

126 Resource 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

148 Validation 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

170 Transformation:

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

186 Error 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

205 Transaction 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

219 Note:

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

224 See 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

233 from dataclasses import dataclass, field as dataclass_field

234 from datetime import datetime

235 from enum import Enum

236 from typing import Any, Dict, List, Set, Tuple, TYPE_CHECKING

237 from uuid import uuid4

238

239 from dataknobs_data.fields import Field

240 from dataknobs_fsm.core.data_modes import DataHandlingMode, DataModeManager

241 from dataknobs_fsm.core.transactions import Transaction

242 from dataknobs_fsm.functions.base import (

243 IValidationFunction,

244 ITransformFunction,

245 IEndStateTestFunction,

246 ResourceConfig,

247)

248

249 if TYPE_CHECKING:

250 from dataknobs_fsm.core.arc import ArcDefinition

251

252

253 class 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

298 class 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

352 class 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

394 class 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

589 class 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

951 class 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

975 class StateMode(Enum):

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

977 NORMAL = "normal"

978 PARALLEL = "parallel"

979 SEQUENTIAL = "sequential"