2013-09-16 32 views
4

我们已经实现了一个扭曲的web API。Python装饰器内部调用错误函数

为了处理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 

在情况下,它是很重要的,我现在用扭曲的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) 

要去航线使用可变一个从route_b

+2

不应该'wrap'返回'wrapped_f'? – Max

+0

对不起,我错误地把它留下了,更新了。 – avoid3d

+1

您有'@ requires_auth'但是'def require_auth' ...注意's' – Eric

回答

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 
+0

这是@即使没有呼叫,这仍然有效:) – avoid3d

1

避免JSON '/ thing_a' 的结果参数(例如列表)作为任何函数或方法的默认参数。 More on why this is a bad idea

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

编辑:万一我不清楚,我指的是

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

默认参数为可变(列表)。

+0

谢谢你的答案,但删除默认参数后,它出现装饰仍然称错误的功能:( – avoid3d

+0

是啊,你很清楚,见上面 – avoid3d

+2

默认的可变参数罚款,如果你没有实际变异他们尽管'def require_auth(roles =(Roles.USER,)):'这里会更好 – Eric

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在授权,并使用结果作为路径

and

使用get_secret_stuff作为路由,并将结果包装在授权者中。授权人员不会将其放入路由中。

+0

谢谢,但我试过只有两条路径被定义和排列,每一个明智的包装顺序,它仍然显示相同的问题 – avoid3d

+0

@ avoid3d:你需要显示我们在你的问题中提供了更多的代码 - 你把它减少到的情况没有什么问题 – Eric

+0

好的,谢谢你,我应该重新编辑一个问题还是重新编写一个问题我打算做一个独立的例子 – avoid3d