Source code for polymerist.genutils.decorators.meta

'''Decorators for modifying other decorators'''

__author__ = 'Timotej Bernat'
__email__ = 'timotej.bernat@colorado.edu'

from typing import Concatenate, Callable, ParamSpec, TypeAlias, TypeVar
from functools import update_wrapper, wraps

Params = ParamSpec('Params') # can also use to typehint *args and **kwargs
ReturnType = TypeVar('ReturnType') 
Decorator : TypeAlias = Callable[[Callable[Params, ReturnType]], Callable[Params, ReturnType]]


# META DECORATORS
[docs] def extend_to_methods(dec : Decorator) -> Decorator: '''Meta-decorator; modifies an existing decorator definition to be transferrable to methods with no additional code The modified decorator can be used interchangably to decorate both ordinary functions AND methods of classes''' ReturnSignature = dec.__annotations__.get('return') @wraps(dec, updated=()) # transfer decorator signature to decorator adapter class, without updating the __dict__ field class AdaptedDecorator: def __init__(self, funct : Callable[Params, ReturnType]) -> None: '''Record function''' self.funct = funct update_wrapper(self, funct) # equivalent to functools.wraps, transfers docstring, module, etc. for documentation def __call__(self, *args : Params.args, **kwargs : Params.kwargs) -> ReturnSignature: # TODO : fix this to reflect the decorator's return signature '''Apply decorator to function, then call decorated function''' return dec(self.funct)(*args, **kwargs) def __get__(self, instance : object, owner : type) -> Callable[[Concatenate[object, Params]], ReturnType]: '''Generate partial application with calling instance as first argument (fills in for "self")''' method = self.funct.__get__(instance, owner) # look up method belonging to owner class return dec(method) # return the decorated method return AdaptedDecorator