from abc import ABC, abstractmethod from aiosched.task import Task from aiosched.context import TaskContext from selectors import EVENT_READ, EVENT_WRITE class BaseDebugger(ABC): """ The base for all debugger objects """ @abstractmethod def on_start(self): """ This method is called when the event loop starts executing """ return NotImplemented @abstractmethod def on_exit(self): """ This method is called when the event loop exits entirely (all tasks completed) """ return NotImplemented @abstractmethod def on_task_schedule(self, task: Task, delay: float): """ This method is called when a new task is scheduled (not spawned) :param task: The Task object representing a aiosched Task and wrapping a coroutine :type task: :class: aiosched.task.Task :param delay: The delay, in seconds, after which the task will start executing :type delay: float """ return NotImplemented @abstractmethod def on_task_spawn(self, task: Task): """ This method is called when a new task is spawned :param task: The Task object representing a aiosched Task and wrapping a coroutine :type task: :class: aiosched.task.Task """ return NotImplemented @abstractmethod def on_task_exit(self, task: Task): """ This method is called when a task exits :param task: The Task object representing an aiosched Task and wrapping a coroutine :type task: :class: aiosched.task.Task """ return NotImplemented @abstractmethod def before_task_step(self, task: Task): """ This method is called right before calling a task's run() method :param task: The Task object representing an aiosched Task and wrapping a coroutine :type task: :class: aiosched.task.Task """ return NotImplemented @abstractmethod def after_task_step(self, task: Task): """ This method is called right after calling a task's run() method :param task: The Task object representing an aiosched Task and wrapping a coroutine :type task: :class: aiosched.task.Task """ return NotImplemented @abstractmethod def before_sleep(self, task: Task, seconds: float): """ This method is called before a task goes to sleep :param task: The Task object representing an aiosched Task and wrapping a coroutine :type task: :class: aiosched.task.Task :param seconds: The amount of seconds the task wants to sleep :type seconds: int """ return NotImplemented @abstractmethod def after_sleep(self, task: Task, seconds: float): """ This method is called after a tasks awakes from sleeping :param task: The Task object representing an aiosched Task and wrapping a coroutine :type task: :class: aiosched.task.Task :param seconds: The amount of seconds the task actually slept :type seconds: float """ return NotImplemented @abstractmethod def before_io(self, timeout: float): """ This method is called right before the event loop checks for I/O events :param timeout: The max. amount of seconds that the loop will hang when using the select() system call :type timeout: float """ return NotImplemented @abstractmethod def after_io(self, timeout: float): """ This method is called right after the event loop has checked for I/O events :param timeout: The actual amount of seconds that the loop has hung when using the select() system call :type timeout: float """ return NotImplemented @abstractmethod def before_cancel(self, task: Task): """ This method is called right before a task gets cancelled :param task: The Task object representing a aiosched Task and wrapping a coroutine :type task: :class: aiosched.task.Task """ return NotImplemented @abstractmethod def after_cancel(self, task: Task) -> object: """ This method is called right after a task gets cancelled :param task: The Task object representing a aiosched Task and wrapping a coroutine :type task: :class: aiosched.task.Task """ return NotImplemented @abstractmethod def on_exception_raised(self, task: Task, exc: BaseException): """ This method is called right after a task has raised an exception :param task: The Task object representing a aiosched Task and wrapping a coroutine :type task: :class: aiosched.task.Task :param exc: The exception that was raised :type exc: BaseException """ return NotImplemented @abstractmethod def on_context_creation(self, ctx: TaskContext): """ This method is called right after a task context is initialized, i.e. when set_context in the event loop is called :param ctx: The context object :type ctx: TaskContext :return: """ return NotImplemented @abstractmethod def on_context_exit(self, ctx: TaskContext): """ This method is called right before a task context is closed, i.e. when close_context in the event loop is called :param ctx: The context object :type ctx: TaskContext :return: """ return NotImplemented @abstractmethod def on_io_schedule(self, stream, event: int): """ This method is called whenever the perform_io primitive is called within the aiosched event loop with the stream to be registered in the selector and the chosen event mask """ return NotImplemented @abstractmethod def on_io_unschedule(self, stream): """ This method is called whenever a stream is unregistered from the loop's I/O selector """ return NotImplemented class SimpleDebugger(BaseDebugger): """ A simple debugger for aiosched """ def on_start(self): print("## Started running") def on_exit(self): print("## Finished running") def on_task_schedule(self, task, delay: int): print( f">> A task named '{task.name}' was scheduled to run in {delay:.2f} seconds" ) def on_task_spawn(self, task): print(f">> A task named '{task.name}' was spawned") def on_task_exit(self, task): print(f"<< Task '{task.name}' exited") def before_task_step(self, task): print(f"-> About to run a step for '{task.name}'") def after_task_step(self, task): print(f"<- Ran a step for '{task.name}'") def before_sleep(self, task, seconds): print(f"# About to put '{task.name}' to sleep for {seconds:.2f} seconds") def after_sleep(self, task, seconds): print(f"# Task '{task.name}' slept for {seconds:.2f} seconds") def before_io(self, timeout): if timeout is None: timeout = float("inf") print(f"!! About to check for I/O for up to {timeout:.2f} seconds") def after_io(self, timeout): print(f"!! Done I/O check (waited for {timeout:.2f} seconds)") def before_cancel(self, task): print(f"// About to cancel '{task.name}'") def after_cancel(self, task): print(f"// Cancelled '{task.name}'") def on_exception_raised(self, task, exc): print(f"== '{task.name}' raised {repr(exc)}") def on_context_creation(self, ctx): print(f"=> A new context was created by {ctx.entry_point.name!r}") def on_context_exit(self, ctx): print(f"=> A context was closed by {ctx.entry_point.name}") def on_io_schedule(self, stream, event: int): evt = "" if event == EVENT_READ: evt = "reading" elif event == EVENT_WRITE: evt = "writing" elif event == EVENT_WRITE | EVENT_READ: evt = "reading or writing" print(f"|| Stream {stream!r} was scheduled for {evt}") def on_io_unschedule(self, stream): print(f"|| Stream {stream!r} was unscheduled")