Python装饰器内部调用错误的函数

时间:2013-09-16 16:08:28

标签: python twisted decorator

我们实施了一个扭曲的网络API。

为了处理auth,我们使用了一个装饰器,我们用。包装一些路径。

@requires_auth(roles=[Roles.Admin])
def get_secret_stuff(request):
    return 42

requires_auth包装器实现如下。

def requires_auth(roles):
    def wrap(f):
        def wrapped_f(request, *args, **kwargs):
            # If the user is authenticated then...
            return f(request, *args, **kwargs)
        return wrapped_f
    return wrap

问题是如果有这个装饰器的多个路由,那么调用 其中任何一个都会导致被称为最新的装饰路线。

这显然不是我想要的,反驳了我对装饰器应该如何工作的理解。 我在代码中添加了一些打印语句,试图弄清楚:

def requires_auth(roles):
    def wrap(f):
        print(f) # This shows that the decorator is being called correctly once per each
                 # route that is decorated
        def wrapped_f(request, *args, **kwargs):
            # If the user is authenticated then...
            return f(request, *args, **kwargs)
        return wrapped_f
    return wrap

如果它很重要,我会对其中一些路线使用twisted的inlineCallbacks,以及所有这些路线的扭曲网页@app.route(url, methods)装饰器。

感谢您阅读:)

编辑: 我删除了构造函数的默认参数,因为我被告知这是一个坏主意:)

编辑:这是一个说明问题的最小例子:

from klein import Klein
import json
app = Klein()

def requires_auth(roles):
    def wrap(f):
        print('inside the first clojure with f=%s' % str(f))
        def wrapped_f(request, *args, **kwargs):
            print('inside the second closure with f=%s' % str(f))
            return f(request, *args, **kwargs)
        return wrapped_f
    return wrap

@app.route('/thing_a')
@requires_auth(roles=['user'])
def get_a(request):
    return json.dumps({'thing A': 'hello'})

@app.route('/thing_b')
@requires_auth(roles=['admin'])
def get_b(request):
    return json.dumps({'thing B': 'goodbye'})

app.run('0.0.0.0', 8080)

转到路线'/ thing_a'会产生来自route_b的json

3 个答案:

答案 0 :(得分:3)

试试这个:

from functools import wraps

def require_auth(roles=(Roles.USER,), *args, **kwargs):

    def call(f, *args, **kwargs):
        return f(*args, **kwargs)

    def deco(f):
        @wraps(f)
        def wrapped_f(request, *a, **kw):
            # do your authentication here
            return call(f, request, *a, **kw)

        return wrapped_f

    return deco

答案 1 :(得分:1)

避免使用可变参数(例如列表)作为任何函数或方法的默认参数。 More on why this is a bad idea

我无法确认,但很可能这就是造成问题的原因。

编辑:如果我不清楚,我指的是

def requires_auth(roles=[Roles.USER]):

默认参数是可变的(列表)。

答案 2 :(得分:0)

您需要以正确的顺序应用装饰器。这可能会奏效:

@route(...)
@requires_auth(roles=[Roles.Admin])
def get_secret_stuff(request):
    return 42

这可能不会:

@requires_auth(roles=[Roles.Admin])
@route(...)
def get_secret_stuff(request):
    return 42

因为分别是这些意思

  

在授权程序中换行get_secret_stuff,并将结果用作路径

  

使用get_secret_stuff作为路由,并将结果包装在授权程序中。授权人永远不会进入这条路线。