Skip to content

Instantly share code, notes, and snippets.

@spinfish
Last active September 15, 2021 03:04
Show Gist options
  • Save spinfish/386f124a3bc974ff36fb2b0fc1d6cd65 to your computer and use it in GitHub Desktop.
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
"""
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