将两个python装饰器合并为一个 [英] Combine two python decorators into one

查看:94
本文介绍了将两个python装饰器合并为一个的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在这里,我想结合两个装饰器,因为它们非常相似,不同之处在于如何处理未经身份验证的用户.我更希望有一个可以与参数一起调用的装饰器.

 # Authentication decorator for routes
# Will redirect to the login page if not authenticated
def requireAuthentication(fn):
    def decorator(**kwargs):
        # Is user logged on?
        if "user" in request.session:
            return fn(**kwargs)
        # No, redirect to login page
        else:
            redirect('/login?url={0}{1}'.format(request.path, ("?" + request.query_string if request.query_string else '')))
    return decorator

# Authentication decorator for routes
# Will return an error message (in JSON) if not authenticated
def requireAuthenticationJSON(fn):
    def decorator(**kwargs):
        # Is user logged on?
        if "user" in request.session:
            return fn(**kwargs)
        # No, return error
        else:
            return {
                "exception": "NotAuthorized",
                "error" : "You are not authorized, please log on"
            }
    return decorator
 

目前,我正在将这些装饰器用于特定的路线,例如

 @get('/day/')
@helpers.requireAuthentication
def day():
    ...

@get('/night/')
@helpers.requireAuthenticationJSON
def night():
    ...
 

我更喜欢这个:

 @get('/day/')
@helpers.requireAuthentication()
def day():
    ...

@get('/night/')
@helpers.requireAuthentication(json = True)
def night():
    ...
 

我正在使用Bottle框架使用python 3.3.有可能做我想做的吗?怎么样?

解决方案

只需添加另一个包装即可捕获json参数:

def requireAuthentication(json=False):
    def decorator(fn):
        def wrapper(**kwargs):
            # Is user logged on?
            if "user" in request.session:
                return fn(**kwargs)

            # No, return error
            if json:
                return {
                    "exception": "NotAuthorized",
                    "error" : "You are not authorized, please log on"
                }
            redirect('/login?url={0}{1}'.format(request.path, ("?" + request.query_string if request.query_string else '')))
        return wrapper
    return decorator

我已经将您原来的requireAuthentication函数重命名为decorator(因为该函数所做的是,它装饰了fn),并将旧的decorator重命名为wrapper,这是通常的惯例.

无论您在之后中放置什么,@都是一个表达式,首先对其求值以找到实际的装饰器函数. @helpers.requireAuthentication()表示您要调用requireAuthentication,它的返回值用作@行所应用功能的实际修饰符.

Here are two decorators I'd like to combine as they are pretty similar, the difference is how a not authenticated user is handled. I'd prefer to have one single decorator that I can call with an argument.

# Authentication decorator for routes
# Will redirect to the login page if not authenticated
def requireAuthentication(fn):
    def decorator(**kwargs):
        # Is user logged on?
        if "user" in request.session:
            return fn(**kwargs)
        # No, redirect to login page
        else:
            redirect('/login?url={0}{1}'.format(request.path, ("?" + request.query_string if request.query_string else '')))
    return decorator

# Authentication decorator for routes
# Will return an error message (in JSON) if not authenticated
def requireAuthenticationJSON(fn):
    def decorator(**kwargs):
        # Is user logged on?
        if "user" in request.session:
            return fn(**kwargs)
        # No, return error
        else:
            return {
                "exception": "NotAuthorized",
                "error" : "You are not authorized, please log on"
            }
    return decorator

And currently I'm using these decorators for specific routes, e.g.

@get('/day/')
@helpers.requireAuthentication
def day():
    ...

@get('/night/')
@helpers.requireAuthenticationJSON
def night():
    ...

I'd much more prefer this:

@get('/day/')
@helpers.requireAuthentication()
def day():
    ...

@get('/night/')
@helpers.requireAuthentication(json = True)
def night():
    ...

I'm on python 3.3 using the Bottle framework. Is it possible to do what I want? How?

解决方案

Just add another wrapper to capture the json parameter:

def requireAuthentication(json=False):
    def decorator(fn):
        def wrapper(**kwargs):
            # Is user logged on?
            if "user" in request.session:
                return fn(**kwargs)

            # No, return error
            if json:
                return {
                    "exception": "NotAuthorized",
                    "error" : "You are not authorized, please log on"
                }
            redirect('/login?url={0}{1}'.format(request.path, ("?" + request.query_string if request.query_string else '')))
        return wrapper
    return decorator

I've renamed your original requireAuthentication function to decorator (because that is what that function did, it decorated fn) and renamed the old decorator to wrapper, the usual convention.

Whatever you put after the @ is an expression, evaluated first to find the actual decorator function. @helpers.requireAuthentication() means you want to call requireAuthentication and it's return value is then used as the actual decorator for the function the @ line applies to.

这篇关于将两个python装饰器合并为一个的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆