Added context manager functionality and other related features

This commit is contained in:
nocturn9x 2020-03-25 13:39:32 +00:00
parent 68df239020
commit 0e29cb7e8a
6 changed files with 42 additions and 4 deletions

View File

@ -1,8 +1,8 @@
__author__ = "Nocturn9x aka Isgiambyy" __author__ = "Nocturn9x aka Isgiambyy"
__version__ = (0, 0, 1) __version__ = (0, 0, 1)
from .core import EventLoop from .core import EventLoop
from .exceptions import GiambioError, AlreadyJoinedError, CancelledError from .exceptions import GiambioError, AlreadyJoinedError, CancelledError, TaskCancelled
from .traps import sleep, join from .traps import sleep, join
from .util import TaskManager
__all__ = ["EventLoop", "sleep", "join", "GiambioError", "AlreadyJoinedError", "CancelledError", "TaskManager", "TaskCancelled"]
__all__ = ["EventLoop", "sleep", "join", "GiambioError", "AlreadyJoinedError", "CancelledError"]

View File

@ -1,5 +1,6 @@
import types import types
from .traps import join, sleep, want_read, want_write, cancel from .traps import join, sleep, want_read, want_write, cancel
from .exceptions import CancelledError
class Result: class Result:
"""A wrapper for results of coroutines""" """A wrapper for results of coroutines"""
@ -23,6 +24,7 @@ class Task:
self.result = None # Updated when the coroutine execution ends self.result = None # Updated when the coroutine execution ends
self.loop = loop # The EventLoop object that spawned the task self.loop = loop # The EventLoop object that spawned the task
self.cancelled = False self.cancelled = False
self.execution = "INIT"
def run(self): def run(self):
self.status = True self.status = True

View File

@ -11,6 +11,7 @@ from .abstractions import Task, Result
from socket import SOL_SOCKET, SO_ERROR from socket import SOL_SOCKET, SO_ERROR
from .traps import join, sleep, want_read, want_write, cancel from .traps import join, sleep, want_read, want_write, cancel
class EventLoop: class EventLoop:
"""Implementation of an event loop, alternates between execution of coroutines (asynchronous functions) """Implementation of an event loop, alternates between execution of coroutines (asynchronous functions)
@ -50,6 +51,7 @@ class EventLoop:
method, *args = self.running.run() method, *args = self.running.run()
getattr(self, method)(*args) # Sneaky method call, thanks to David Beazley for this ;) getattr(self, method)(*args) # Sneaky method call, thanks to David Beazley for this ;)
except StopIteration as e: except StopIteration as e:
self.running.execution = "FINISH"
self.running.result = Result(e.args[0] if e.args else None, None) # Saves the return value self.running.result = Result(e.args[0] if e.args else None, None) # Saves the return value
self.to_run.extend(self.joined.pop(self.running, ())) # Reschedules the parent task self.to_run.extend(self.joined.pop(self.running, ())) # Reschedules the parent task
except RuntimeError: except RuntimeError:

View File

@ -14,4 +14,7 @@ class CancelledError(GiambioError):
return "giambio.exceptions.CancelledError" return "giambio.exceptions.CancelledError"
class TaskCancelled(GiambioError): class TaskCancelled(GiambioError):
"""This exception is raised when the user attempts to join a cancelled task""" """This exception is raised when the user attempts to join a cancelled task"""
class TaskFinished(TaskCancelled):
"""This exception is raised when the user attempts to join an already finished task"""

View File

@ -39,6 +39,8 @@ def join(task):
if task.cancelled: if task.cancelled:
raise TaskCancelled("Cannot join cancelled task!") raise TaskCancelled("Cannot join cancelled task!")
if task.execution == "FINISH":
raise TaskFinished("Cannot join already terminated task!")
task.joined = True task.joined = True
yield "want_join", task yield "want_join", task
return task.get_result() # This raises an exception if the child task errored return task.get_result() # This raises an exception if the child task errored

29
giambio/util.py Normal file
View File

@ -0,0 +1,29 @@
from .abstractions import Task
from collections import deque
class TaskManager(Task):
"""Class to be used inside context managers to spawn multiple tasks and be sure that they will all joined before the code exits the with block"""
def __init__(self, loop):
self.tasks = deque() # All tasks spawned
self.values = {} # Results OR exceptions of each task
self.loop = loop
async def __aenter__(self):
return self
async def __aexit__(self, *args):
for task in self.tasks:
self.values[task.coroutine.__name__] = await task.join()
async def spawn(self, coro):
task = self.loop.spawn(coro)
self.tasks.append(task)
return task
async def schedule(self, coro, delay):
task = self.loop.schedule(coro, delay)
self.tasks.append(task)
return task