Events System Quickstart¤
This tutorial walks through creating and using events in PANTHER's testing framework.
Basic Event Creation¤
Step 1: Create Your First Event¤
from dataclasses import dataclass
from datetime import datetime
from enum import Enum
from panther.core.events.base.event_base import BaseEvent
# Define event types for your domain
class MyTestEventType(Enum):
TEST_STARTED = "my_test.started"
TEST_COMPLETED = "my_test.completed"
# Create event class
@dataclass(frozen=True)
class MyTestStartedEvent(BaseEvent):
"""Event when a custom test starts."""
test_name: str
start_time: datetime
def __post_init__(self):
object.__setattr__(self, 'event_type', MyTestEventType.TEST_STARTED)
super().__post_init__()
# Create an event
event = MyTestStartedEvent(
entity_type="custom_test",
entity_id="test-001",
test_name="Connection Test",
start_time=datetime.now()
)
print(f"Created event: {event.event_type}")
print(f"Event UUID: {event.uuid}")
Step 2: Create an Event Emitter¤
from panther.core.events.base.event_emitter_base import EntityEventEmitterBase
class MyTestEmitter(EntityEventEmitterBase[MyTestEventType]):
"""Emitter for custom test events."""
def __init__(self, test_id: str):
super().__init__(test_id, "custom_test")
def emit_test_started(self, test_name: str):
"""Emit test started event."""
event = MyTestStartedEvent(
entity_id=self.entity_id,
test_name=test_name,
start_time=datetime.now()
)
self.emit_event(event)
def emit_test_completed(self, test_name: str, duration: float):
"""Emit test completed event."""
event = MyTestCompletedEvent(
entity_id=self.entity_id,
test_name=test_name,
end_time=datetime.now(),
duration=duration
)
self.emit_event(event)
# Use the emitter
emitter = MyTestEmitter("test-001")
emitter.emit_test_started("Connection Test")
Working with State Management¤
Step 3: Create State Manager¤
from enum import Enum
from panther.core.events.base.state_base import StateManager
class MyTestState(Enum):
CREATED = "created"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
# Define valid state transitions
transitions = {
MyTestState.CREATED: [MyTestState.RUNNING],
MyTestState.RUNNING: [MyTestState.COMPLETED, MyTestState.FAILED],
MyTestState.COMPLETED: [], # Terminal state
MyTestState.FAILED: [] # Terminal state
}
# Create state manager
state_manager = StateManager(
initial_state=MyTestState.CREATED,
valid_transitions=transitions
)
# Try state transitions
print(f"Initial state: {state_manager.current_state}")
success = state_manager.transition_to(MyTestState.RUNNING)
print(f"Transition to RUNNING: {success}") # True
success = state_manager.transition_to(MyTestState.CREATED)
print(f"Invalid transition back: {success}") # False
success = state_manager.transition_to(MyTestState.COMPLETED)
print(f"Transition to COMPLETED: {success}") # True
Creating Complete Event Workflow¤
Step 4: Complete Test Example¤
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Dict, Any
from panther.core.events.base.event_base import BaseEvent
from panther.core.events.base.event_emitter_base import EntityEventEmitterBase
# Complete event type definition
class QuickTestEventType(Enum):
TEST_CREATED = "quick_test.created"
TEST_STARTED = "quick_test.started"
TEST_COMPLETED = "quick_test.completed"
TEST_FAILED = "quick_test.failed"
# Event classes
@dataclass(frozen=True)
class QuickTestEvent(BaseEvent):
"""Base event for quick test tutorial."""
test_name: str
config: Dict[str, Any] = field(default_factory=dict)
@dataclass(frozen=True)
class QuickTestCreatedEvent(QuickTestEvent):
"""Event when test is created."""
creation_time: datetime
def __post_init__(self):
object.__setattr__(self, 'event_type', QuickTestEventType.TEST_CREATED)
super().__post_init__()
@dataclass(frozen=True)
class QuickTestStartedEvent(QuickTestEvent):
"""Event when test execution starts."""
start_time: datetime
def __post_init__(self):
object.__setattr__(self, 'event_type', QuickTestEventType.TEST_STARTED)
super().__post_init__()
@dataclass(frozen=True)
class QuickTestCompletedEvent(QuickTestEvent):
"""Event when test completes successfully."""
end_time: datetime
duration: float
results: Dict[str, Any] = field(default_factory=dict)
def __post_init__(self):
object.__setattr__(self, 'event_type', QuickTestEventType.TEST_COMPLETED)
super().__post_init__()
@dataclass(frozen=True)
class QuickTestFailedEvent(QuickTestEvent):
"""Event when test fails."""
failure_time: datetime
error_message: str
error_type: str = "unknown"
def __post_init__(self):
object.__setattr__(self, 'event_type', QuickTestEventType.TEST_FAILED)
super().__post_init__()
# Complete emitter
class QuickTestEmitter(EntityEventEmitterBase[QuickTestEventType]):
"""Complete emitter for tutorial test events."""
def __init__(self, test_id: str):
super().__init__(test_id, "quick_test")
self.start_time = None
def emit_created(self, test_name: str, config: Dict[str, Any] = None):
"""Emit test created event."""
event = QuickTestCreatedEvent(
entity_id=self.entity_id,
test_name=test_name,
config=config or {},
creation_time=datetime.now()
)
self.emit_event(event)
def emit_started(self, test_name: str, config: Dict[str, Any] = None):
"""Emit test started event."""
self.start_time = datetime.now()
event = QuickTestStartedEvent(
entity_id=self.entity_id,
test_name=test_name,
config=config or {},
start_time=self.start_time
)
self.emit_event(event)
def emit_completed(self, test_name: str, results: Dict[str, Any] = None):
"""Emit test completed event."""
end_time = datetime.now()
duration = (end_time - self.start_time).total_seconds() if self.start_time else 0
event = QuickTestCompletedEvent(
entity_id=self.entity_id,
test_name=test_name,
config={},
end_time=end_time,
duration=duration,
results=results or {}
)
self.emit_event(event)
def emit_failed(self, test_name: str, error_message: str, error_type: str = "unknown"):
"""Emit test failed event."""
event = QuickTestFailedEvent(
entity_id=self.entity_id,
test_name=test_name,
config={},
failure_time=datetime.now(),
error_message=error_message,
error_type=error_type
)
self.emit_event(event)
# Run complete example
def run_test_workflow():
"""Run a complete test workflow with events."""
emitter = QuickTestEmitter("tutorial-test-001")
# Test lifecycle
print("=== Starting Test Lifecycle ===")
# 1. Create test
emitter.emit_created("Tutorial Connection Test", {
"timeout": 30,
"retry_count": 3
})
print("✓ Test created")
# 2. Start test
emitter.emit_started("Tutorial Connection Test", {
"timeout": 30,
"retry_count": 3
})
print("✓ Test started")
# Simulate test execution
import time
time.sleep(1) # Simulate test work
# 3. Complete test (or fail)
import random
if random.choice([True, False]):
emitter.emit_completed("Tutorial Connection Test", {
"packets_sent": 100,
"packets_received": 98,
"success_rate": 0.98
})
print("✓ Test completed successfully")
else:
emitter.emit_failed("Tutorial Connection Test",
"Connection timeout after 30 seconds",
"timeout_error")
print("✗ Test failed")
# Run the example
if __name__ == "__main__":
run_test_workflow()
Event Deduplication Example¤
Step 5: Understanding Event Deduplication¤
from panther.core.events.base.event_base import create_content_based_uuid
# Create identical events
event1 = QuickTestStartedEvent(
entity_id="test-123",
test_name="Dedup Test",
config={"param": "value"},
start_time=datetime(2025, 1, 1, 12, 0, 0)
)
event2 = QuickTestStartedEvent(
entity_id="test-123",
test_name="Dedup Test",
config={"param": "value"},
start_time=datetime(2025, 1, 1, 12, 0, 0)
)
print(f"Event 1 UUID: {event1.uuid}")
print(f"Event 2 UUID: {event2.uuid}")
print(f"UUIDs match: {event1.uuid == event2.uuid}") # True - same content
# Create different event
event3 = QuickTestStartedEvent(
entity_id="test-456", # Different entity_id
test_name="Dedup Test",
config={"param": "value"},
start_time=datetime(2025, 1, 1, 12, 0, 0)
)
print(f"Event 3 UUID: {event3.uuid}")
print(f"Event 1 vs 3 match: {event1.uuid == event3.uuid}") # False - different content
Next Steps¤
Integration with Observers¤
Once you've created events, you'll want to process them with observers:
# See observer tutorial for processing these events
from panther.core.observer.base.observer_interface import IObserver
class TutorialObserver(IObserver):
def is_interested(self, event_type: str) -> bool:
return event_type.startswith("quick_test.")
def on_event(self, event):
print(f"Observed: {event.event_type} for {event.entity_id}")
# Register observer with event manager
# (See observer module documentation for details)
Advanced Features¤
- Batch Events: Create events that contain multiple data points
- Error Events: Standardized error event patterns
- Metrics Integration: Events that trigger metrics collection
- State Coordination: Events that drive state machine transitions
Best Practices¤
- Event Granularity: Create events for meaningful state changes, not every operation
- Immutability: Always use
frozen=Truedataclass for events - Type Safety: Use enums for event types to prevent typos
- Documentation: Include docstrings with usage examples
- Testing: Write tests for event creation and emission logic
This tutorial covers the basics of PANTHER's event system. See the full API reference for comprehensive details on all event types and advanced features.