繁体   English   中英

如何将 python 类型注释添加到烧瓶全局上下文 g?

[英]How can I add python type annotations to the flask global context g?

我有一个装饰器,可以将用户添加到 Flask 全局上下文 g:

class User:
    def __init__(self, user_data) -> None:
        self.username: str = user_data["username"]
        self.email: str = user_data["email"]

def login_required(f):
    @wraps(f)
    def wrap(*args, **kwargs):
        user_data = get_user_data()
        user = User(user_data)
        g.user = User(user_data)

        return f(*args, **kwargs)

    return wrap

我希望在控制器中访问 g.user 时知道 g.user 的类型(用户)。 我怎样才能做到这一点? (我正在使用pyright)

我在Typechecking dynamic added attributes 中描述了一个类似的问题。 一种解决方案是使用typing.TYPE_CHECKING添加自定义类型提示:

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from flask.ctx import _AppCtxGlobals

    class MyGlobals(_AppCtxGlobals):
        user: 'User'

    g = MyGlobals()
else:
    from flask import g

现在例如

reveal_type(g.user)

会发出

note: Revealed type is 'myapp.User'

如果自定义类型应该在多个模块中重用,您可以为flask引入部分存根。 的短截线的位置是依赖于类型检查器,例如mypy读取来自自定义存根MYPY_PATH环境变量, pyright长相对于typings在项目根目录等实施例的局部存根目录:

# _typeshed/flask/__init__.pyi

from typing import Any
from flask.ctx import _AppCtxGlobals
from models import User


def __getattr__(name: str) -> Any: ...  # incomplete


class MyGlobals(_AppCtxGlobals):
    user: User
    def __getattr__(self, name: str) -> Any: ...  # incomplete


g: MyGlobals

这是一个有意见的解决方案: flask.g很神奇,并且与服务器实现非常紧密地联系在一起。 IMO,它的使用应该保持在最小范围内。

我创建了一个文件来管理 g,它允许我输入它

    # request_context.py
    from flask import g
    from somewhere import User
    
    def set_user(user: User) -> None:
       g.user = user
    
    def get_user() -> User:
       # you could validate here that the user exists
       return g.user

然后在你的代码中:

    # yourcode.py
    import request_context
    
    class User:
        ...
    
    def login_required(f):
        @wraps(f)
        def wrap(*args, **kwargs):
            user_data = get_user_data()
            user = User(user_data)
            request_context.set_user(User(user_data))
    
            return f(*args, **kwargs)
    
        return wrap

您可以代理g对象。 考虑以下实现:

import flask


class User:
    ...


class _g:

    user: User
    # Add type hints for other attributes
    # ...

    def __getattr__(self, key):
        return getattr(flask.g, key)


g = _g()

您可以注释一个类的属性,即使该类不是您的,只需在其后加一个冒号即可。 例如:

g.user: User

就是这样。 由于它可能在任何地方都有效,我会将它放在代码的顶部:

from functools import wraps

from flask import Flask, g

app = Flask(__name__)


class User:
    def __init__(self, user_data) -> None:
        self.username: str = user_data["username"]
        self.email: str = user_data["email"]


# Annotate the g.user attribute
g.user: User


def login_required(f):
    @wraps(f)
    def wrap(*args, **kwargs):
        g.user = User({'username': 'wile-e-coyote',
                       'email': 'coyote@localhost'})

        return f(*args, **kwargs)

    return wrap


@app.route('/')
@login_required
def hello_world():
    return f'Hello, {g.user.email}'


if __name__ == '__main__':
    app.run()

就是这样。

暂无
暂无

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

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