«Развернуть» и снова обернуть @staticmethod в мета-класс - PullRequest
0 голосов
/ 09 декабря 2018

Я хочу создать метакласс, который будет украшать каждую функцию с помощью декоратора трассировки.

Итак, я получил это:

from functools import wraps
from inspect import getfile

from arrow import now


def trace(f):
    @wraps(f)
    def wrapper(*args, **kwargs):
        print(
            '{timestamp} - {file} - {function} - CALL *{args} **    {kwargs}'.format(timestamp=now().isoformat(sep=' '),
                                                                                                 file=getfile(f),
                                                                                 function=f.__name__, args=args[1:],
                                                                                 kwargs=kwargs))
        result = f(*args, **kwargs)
        print(
            '{timestamp} - {file} - {function} - RESULT     {result}'.format(timestamp=now().isoformat(sep=' '),
                                                                         file=getfile(f),
                                                                         function=f.__name__,
                                                                         result=result))
        return result

    return wrapper


class TraceLogger(type):
    def __new__(mcs, name, bases, dct):
        for attr in dct:
            value = dct[attr]
            if callable(value):
                dct[attr] = trace(value)
        return super(TraceLogger, mcs).__new__(mcs, name, bases, dct)


class ExampleClass(object):
    __metaclass__ = TraceLogger

    def foo(self):
        print('foo')

    @staticmethod
    def bar():
        print('bar')

example = ExampleClass()
example.foo()
example.bar()

Трассировка работает для любых нестатических функций, поскольку статические методы не могут быть вызваны.Как можно развернуть staticmethod, а затем обернуть его два раза в new metclass, например:

dct[attr] = staticmethod(trace(value))

1 Ответ

0 голосов
/ 09 декабря 2018

(в этом ответе я ссылался на три разных вопроса / ответа, потому что я хотел предоставить как можно больше подробностей, а не просто закрыть их как дубликат. Если вы проголосуете за этот ответ, пожалуйста, подумайте и об ответе на связанные вопросы)

Вы наткнулись на интересную «особенность» Python, которая объясняется в ответах на этот вопрос.

Вместо if callable(value) вы могли бы проверить if isinstance(value, (function, staticmethod, classmethod)), но этоприведет только к другому интересному угловому случаю: NameError: name 'function' is not defined (посмотрите, почему здесь ) (Даже выполнение import builtins ; ... ; builtins.function приведет к ошибке).

Вы не можете убежать отнеобходимость проверить, является ли имя атрибута методом, статическим методом или методом класса, и (возможно, см. здесь почему) в вашем случае правильный способ сделать это - использовать types.FunctionType:

import types

...

if type(value) == types.FunctionType: # or isinstance(value, types.FunctionType)
    dct[attr] = trace(value)

...
...