Last active
September 15, 2021 03:04
-
-
Save spinfish/386f124a3bc974ff36fb2b0fc1d6cd65 to your computer and use it in GitHub Desktop.
Coroutine utility class with before and after invoke hooks, kind of like a discord.py command
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Module level docstring go brrrrrrr | |
Anyways this module provides a special coroutine utility class (wrapped by a | |
decorator) that allows your coro to have a custom name and before and after | |
invoke hooks. | |
""" | |
__all__ = ("coroutine",) | |
__license__ = "MIT" | |
__version__ = "1.1.1" | |
__author__ = "mackenzie" | |
from asyncio import iscoroutinefunction | |
class _Coroutine: | |
"""Coroutine utility class with before and after invoke hooks. | |
Attributes | |
---------- | |
function: :class:`function` | |
The coroutine function that was used. | |
name: :class:`str` | |
The coroutine's name, defaults to the function name if not provided. | |
""" | |
__slots__ = ("__after_invoke", "__before_invoke", "__func", "__name") | |
def __init__(self, func, *, name=None): | |
if not (func.__class__.__name__ == "function" and iscoroutinefunction(func)): | |
raise TypeError("func must be a coroutine function, not {!r}".format(func.__class__)) | |
self.__func = func | |
self.__name = name or func.__name__ | |
@property | |
def function(self): | |
return self.__func | |
@property | |
def name(self): | |
return self.__name | |
async def __call__(self, *args, **kwargs): | |
""" | |
Calls the coroutine and returns its result, | |
invoking before and after hooks. | |
""" | |
try: | |
await self.__before_invoke() | |
except AttributeError: | |
pass | |
result = await self.function(*args, **kwargs) | |
try: | |
await self.__after_invoke() | |
except AttributeError: | |
pass | |
return result | |
def after_invoke(self, func): | |
""" | |
A decorator that registers another coroutine as | |
a post-invoke hook. | |
""" | |
if not iscoroutinefunction(func): | |
raise TypeError("Post-invoke hook must be a coroutine.") | |
self.__after_invoke = func | |
return func | |
def before_invoke(self, func): | |
""" | |
A decorator that registers another coroutine as | |
a pre-invoke hook. | |
""" | |
if not iscoroutinefunction(func): | |
raise TypeError("Pre-invoke hook must be a coroutine.") | |
self.__before_invoke = func | |
return func | |
def coroutine(*, name=None): | |
""" | |
A decorator that returns a wrapped instance of the coro utility class. | |
Example | |
------- | |
.. code-block:: python3 | |
@coroutine(name='my_coroutine') | |
async def function(): | |
print('In the middle...') | |
return 'Hi there!' | |
@function.before_invoke | |
async def before_function(): | |
print('This happens before the coroutine is called.') | |
@function.after_invoke | |
async def after_function(): | |
print('This happens after the coroutine is called.') | |
print(function.name) # 'my_coroutine' | |
print(asyncio.run(function())) # 'Hi there!' | |
""" | |
return lambda func: _Coroutine(func, name=name) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment