Под катом свой метод решения задачи, вылившийся в небольшую библиотеку, доступную для общего пользования.
Итак, было решено создать и использовать некий специальный реестр декораторов. Т.е. любую функцию-декоратор регистрировать в этом реестре перед ее использованием в качестве декоратора. После этого, используя API реестра можно достаточно просто отслеживать использование зарегистрированых декораторов на методах, функциях и в модулях.
Тем не менее есть несколько правил, о которых слдедует помнить. Регистрация built-in декораторов не будет работать. То есть, к сожалению, не получится трэкать такие декораторы, как @staticmethod
, @classmethod
и им подобные (если кто-то сможет найти решение этой проблемы — буду премного благодарен). И самое главное, — декораторы должны быть зарегистрированы до их использования.
Фактически, механика работы реестра достаточно проста. Регистрируя декоратор вы фактически получаете задекорированый исходный декоратор который помимо своей исходной функциональности также записывает информацию о себе в аттрубут "__annotations__" декорируемой функции.
Если функция (или метод) декорируются несколькими декораторами, важно только зарегистрирвать все декораторы перед их использованием и все декораторы будут правильно учтены. Т.е., конструкция вида:
@decorator_one @decorator_two @decorator_three def some_function(): pass
будет успешно работать.
Библиотека «regd» (так я ее назвал), совместима как с Python 2.x, так и с версией 3.x (на наших проектах у нас используются обе ветки, поэтому совместимость проверялась).
Исходники доступны на Github, лицензия, как всегда — MIT, так что делайте все, что захотите.
Документация здесь.
Установить можно просто через PyPI:
$ pip install regd
Ниже несколько слов о функционале.
1. Регистрация декораторов.
«Обычные» и «параметризованые» декораторы должны регистрироваться разными методами:
from regd import DecoratorRegistry as dreg # регистрация "обычного" декоратора simple_decorator = dreg.decorator( mydecorator) # регистрация "параметризованного" декоратора param_decorator = dreg.parametrized_decorator( param_decorator)
2. API рефлексии
Чтобы функции API были более понятными давайте для начала создадим и зарегистрируем простой декоратор, который по факту ничнего не делает, а просто существует.
from regd import DecoratorRegistry as dreg # создадим декоратор def mydecorator( fn) : # здесь может быть какая-то полезная работа... def wrapper( *args, **kwargs) # ... или здесь что-то полезное ... return fn( *args, **kwargs) return wrapper # зарегистрируем наш декоратор mydecorator = dreg.decorator( mydecorator)
Помните — зарегистрировать декораторы нужно до их использования.
Теперь, после регистрации, можем использовать наш декоратор как обычно:
@mydecorator def myfunc() : pass
Теперь в любой момент из любого места в коде можем узнать, задекорирована ли функция декоратором:
print( dreg.is_decorated_with( myfunc, mydecorator))
Еще несколько полезных методов:
all_decorated_module_functions( module, exclude_methods=False, exclude_functions=False)
— позволяет получить все функции и/или методы классов задекорированные зарегистрированными декораторами в заданном модулеmodule_functions_decorated_with( module, decorator, exclude_methods=False, exclude_functions=False)
— позволяет получить все функции и/или методы классов задекорированные заданным декоратором в заданном модулеdecorated_methods( cls, decorator)
— получаем все методы класса/объекта задекорированные заданным декораторомget_decorators( fn)
— вернет список все известных декораторов для заданной функции/методаget_real_function( fn)
— вернет ссылку ра исходную функцию, которая была задекорирована декораторами (да, можно получить доступ к исходной функции и даже выполнить ее в обход декорирования)is_decorated_with( fn, decorator)
— проверяет, задекорирована ли заданная функция заданным декоратором
Надеюсь, кому-то пригодится или покажется полезным. Все замечания и предложения приветствуются.
ссылка на оригинал статьи http://habrahabr.ru/post/178017/
Добавить комментарий