Add First Version Of Workflow Layer Codes
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
from __future__ import annotations
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Any, Dict, List, Optional
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class Status(Enum):
|
||||
PENDING = "pending"
|
||||
READY = "ready"
|
||||
RUNNING = "running"
|
||||
PAUSED = "paused"
|
||||
STOPPED = "stopped"
|
||||
COMPLETED = "completed"
|
||||
FAILED = "failed"
|
||||
|
||||
|
||||
class TaskEvent(Enum):
|
||||
BEFORE_RUN = "before_run"
|
||||
STATUS_CHANGED = "status_changed"
|
||||
ERROR = "error"
|
||||
COMPLETED = "completed"
|
||||
|
||||
|
||||
class TaskEventListener:
|
||||
def handle(self, event: TaskEvent, task: "Task", payload: Optional[Any] = None):
|
||||
pass
|
||||
|
||||
def handle_log(self, task: "Task", message: str):
|
||||
pass
|
||||
|
||||
|
||||
class ExecutionContext:
|
||||
def __init__(self):
|
||||
self._assets: Dict[str, Any] = {}
|
||||
|
||||
def put_asset(self, key: str, value: Any):
|
||||
self._assets[key] = value
|
||||
|
||||
def get_asset(self, key: str, default: Any = None):
|
||||
return self._assets.get(key, default)
|
||||
|
||||
def snapshot(self):
|
||||
return dict(self._assets)
|
||||
|
||||
|
||||
class Task(ABC):
|
||||
def __init__(self, name: str):
|
||||
self._name = name
|
||||
|
||||
# execution dependencies (DAG edges)
|
||||
self._parents: List["Task"] = []
|
||||
|
||||
# structural parent in workflow tree (Composite hierarchy)
|
||||
self._parent_task: Optional["Task"] = None
|
||||
|
||||
self._status: Status = Status.PENDING
|
||||
self._listeners: List[TaskEventListener] = []
|
||||
self._timestamps: Dict[str, datetime] = {}
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def parents(self):
|
||||
return self._parents
|
||||
|
||||
def add_parent(self, parent: "Task"):
|
||||
if parent not in self._parents:
|
||||
self._parents.append(parent)
|
||||
|
||||
def set_parent_task(self, parent: "Task"):
|
||||
self._parent_task = parent
|
||||
|
||||
# Events / Logging / Status
|
||||
def add_listener(self, listener: TaskEventListener):
|
||||
self._listeners.append(listener)
|
||||
|
||||
def _emit(self, event: TaskEvent, payload: Optional[Any] = None):
|
||||
for listener in self._listeners:
|
||||
listener.handle(event, self, payload)
|
||||
|
||||
def log(self, message: str):
|
||||
for listener in self._listeners:
|
||||
listener.handle_log(self, message)
|
||||
|
||||
def _set_status(self, new_status: Status):
|
||||
old_status = self._status
|
||||
self._status = new_status
|
||||
|
||||
if new_status == Status.RUNNING:
|
||||
self._timestamps["start"] = datetime.now()
|
||||
|
||||
if new_status in (Status.COMPLETED, Status.FAILED, Status.STOPPED):
|
||||
self._timestamps["end"] = datetime.now()
|
||||
|
||||
if old_status != new_status:
|
||||
self._emit(TaskEvent.STATUS_CHANGED, {"from": old_status, "to": new_status})
|
||||
|
||||
def get_status(self):
|
||||
return self._status
|
||||
|
||||
def get_timestamps(self):
|
||||
return dict(self._timestamps)
|
||||
|
||||
# Abstract execution
|
||||
@abstractmethod
|
||||
def run(self, context: ExecutionContext):
|
||||
pass
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Task {self.name}>"
|
||||
Reference in New Issue
Block a user