简体   繁体   English

如何在运行时替换装饰器?

[英]How do I replace a decorator at runtime?

I'm trying to adapt another StackOverflow answer on conditionally applying a decorator to only require login for a specific environment (eventually a staging environment, but development until I get this working). 我试图在有条件的情况下应用装饰器以适应另一个StackOverflow答案 ,该装饰器仅要求针对特定环境(最终是staging环境,但需要development才能登录)。 Toward that end, I started with the following 为此,我从以下内容开始

auth = HTTPDigestAuth()

def login_required(dec, condition):
    def decorator(func):
        if not condition:
            return func
        return dec(func)
    return decorator

@bp.route('/auth')
@login_required(auth.login_required, current_app.config['ENV'] != 'development')
def auth_route():
    return current_app.config['ENV']

When I launch the server, I get a RuntimeError: Working outside of application context error. 启动服务器时,出现RuntimeError: Working outside of application context错误RuntimeError: Working outside of application context After trying a few suggestions from an earlier version of this question, I got the RuntimeError to disappear, but the decorator still isn't being correctly applied when I want. 在尝试了该问题早期版本的一些建议后,我发现RuntimeError消失了,但在需要时仍然不能正确应用装饰器。 Here's the current version: 这是当前版本:

def login_required(dec):
    def decorator(func):
        if not os.environ.get('ENV') != 'development':
            return func
        return dec(func)
    return decorator

@bp.route('/auth')
@login_required(auth.login_required)
def auth_route():
    return current_app.config['ENV']

This never returns the auth.login_reqired function. 这永远不会返回auth.login_reqired函数。 It always lets the browser in without authentication. 它始终使浏览器无需身份验证即可进入。

So, I tried changing the condition to 因此,我尝试将条件更改为

if not os.environ.get('ENV') is not None:

and then the authentication shows up. 然后显示身份验证。

Yes, I've done an export ENV=development in the shell and confirmed it with the env command. 是的,我已经在shell中完成了export ENV=development并使用env命令确认了。 But even then it's not reading the environment variable as I would expect. 但是即使那样,它也不会像我期望的那样读取环境变量。

Perhaps this is simply the wrong way to go about it? 也许这只是错误的解决方法? My final goal is to require authentication on one particular environment. 我的最终目标是要求在一个特定环境上进行身份验证。 Is this possible with the path I'm on? 我走的路有可能吗? Is it possible at all? 有可能吗?

current_app is a context local proxy that only has meaning during a request. current_app是上下文本地代理,仅请求期间才有意义。 This means you can't use it before a request, ie as part of a decorator. 这意味着您不能请求之前 (即作为装饰器的一部分)使用它。

Using current_app is generally good practice because Flask allows multiple apps to be configured. 通常,使用current_app是个好习惯,因为Flask允许配置多个应用程序。 In your specific case however, it isn't actually necessary. 但是,在您的特定情况下,实际上没有必要。 For instance, the following would work, because it uses the app object directly instead of the current_app proxy: 例如,下面的方法将起作用,因为它直接使用app对象而不是current_app代理:

from yourpackage import app

@bp.route('/auth')
@login_required(auth.login_required, app.config['ENV'] != 'development')
def auth():
    return current_app.config['ENV']

Let me paste something from documentation of Flask 让我从Flask的文档中粘贴一些内容

Lifetime of the Context The application context is created and destroyed as necessary. 上下文的生命周期根据需要创建和销毁应用程序上下文。 When a Flask application begins handling a request, it pushes an application context and a request context. 当Flask应用程序开始处理请求时,它将推送应用程序上下文和请求上下文。 When the request ends it pops the request context then the application context. 当请求结束时,它会弹出请求上下文,然后弹出应用程序上下文。 Typically, an application context will have the same lifetime as a request. 通常,应用程序上下文将与请求具有相同的生存期。

Now let's consider how decorators work. 现在让我们考虑装饰器是如何工作的。 It is just a syntactic sugar see this answer . 只是语法糖就看这个答案

So the login_required decorator is called while the module is loaded and the current app is not available yet because it's not handling request. 因此,在加载模块时将调用login_required装饰器,并且当前应用程序尚不可用,因为它未处理请求。

I would do this way, move condition to the decorator function (relating to your example). 我会这样做,将条件移动到装饰器函数(与您的示例有关)。 It will be called while the request will be handled so you should have access to current_app. 在处理请求时将调用它,因此您应该可以访问current_app。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM