Декоратор Стоп Автозапуск - PullRequest
0 голосов
/ 27 июня 2019

Я пытаюсь реализовать декоратор с параметрами.

Вот мой фактический код:

def transactional_function(read_only=False):
    """
    A simple wrapper to ensure that the desired function will always runs
    inside a transaction, so we don't have to pollute our code with a bunch of
    run_as_transactions.
    """
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kw):
            in_transaction = getattr(_thread_local_data, "is_in_transaction", False)

            if in_transaction:
                result = func(*args, **kw)
            else:
                if read_only:
                    return run_as_readonly(func, *args, **kw)
                else:
                    return run_as_transaction(func, *args, **kw)
            return result
        return wrapper
    return decorator

Однако это автоматически запускается при объявлении функции.

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

decorated_function(standard_arg, read_only=True)

Возможно ли это как-нибудь?Я мог реализовать только оболочки без параметров или декораторы, которые автоматически запускаются с параметрами.

1 Ответ

0 голосов
/ 27 июня 2019

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

Вот упрощенная версия, которая отображает только состояние read_only и вызывает исходную функцию:

import inspect
from functools import wraps

def transactional_function(func):
    sig = inspect.signature(func)   # extract the original signature
    params = sig.parameters
    if 'read_only' in params:
        change = False              # read_only already exists, just note that
    else:
        change = True
        # let us add a new keyword only parameter 'read_only'
        params = list(params.values())
        params.append(inspect.Parameter('read_only',
                        inspect.Parameter.KEYWORD_ONLY))
        # re-order the parameter list
        params.sort(key=lambda x: {
            inspect.Parameter.POSITIONAL_OR_KEYWORD: 0,
            inspect.Parameter.POSITIONAL_ONLY: 0,
            inspect.Parameter.VAR_POSITIONAL:1,
            inspect.Parameter.KEYWORD_ONLY:2,
            inspect.Parameter.VAR_KEYWORD:3}[x.kind])
        # and set that new parameter list in the signature
        sig = sig.replace(parameters = params)
    @wraps(func)
    def inner(*args, **kwargs):
        read_only = kwargs.get('read_only', False)
        # remove the parameter if it was added by the decorator
        if change and ('read_only' in kwargs):
            del kwargs['read_only']
        print('read_only', read_only)       # add your logic here
        return func(*args, **kwargs)
    # update the signature of the decorated function
    if change:
        inner.__signature__ = sig
    return inner

Демо-версия:

>>> @transactional_function
def foo(bar):
    return bar * 2

>>> foo(2)
read_only False
4
>>> foo(2, read_only=False)
read_only False
4
>>> foo(2, read_only=True)
read_only True
4

и с более сложной подписью:

>>> @transactional_function
def foo2(a, *args, **kwargs):
    return a
>>> foo2(2, read_only=True)
read_only True
2
...