Tengo un decorador que agrega un usuario al contexto global del matraz 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 wrapQuiero que se sepa el tipo (Usuario) de g.user cuando acceda a g.user en los controladores. ¿Cómo puedo conseguir esto? (estoy usando pyright)
Puede anotar un atributo en una clase, incluso si esa clase no es suya, simplemente con dos puntos después. Por ejemplo:
g.user: UserEso es todo. Dado que presumiblemente es válido en todas partes, lo pondría en la parte superior de su código:
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()Eso es todo.
Esta es una solución con una opinión: flask.g es mágico y está muy ligado a la implementación del servidor. En mi opinión, su uso debe mantenerse contenido y mínimo.
Creé un archivo para administrar g, lo que me permitió escribirlo
# 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.usery luego en tu código:
# 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 wrapPodrías representar el objeto g . Considere la siguiente implementación:
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()Tuve un problema similar descrito en Comprobación de tipos de atributos agregados dinámicamente . Una solución es agregar sugerencias de tipo personalizadas usandotyping.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 gAhora por ejemplo
reveal_type(g.user)emitirá
note: Revealed type is 'myapp.User' Si los tipos personalizados deben reutilizarse en varios módulos, puede introducir un código auxiliar parcial para el flask . La ubicación de los stubs depende del verificador de tipos, por ejemplo, mypy lee stubs personalizados de la variable de entorno MYPY_PATH , pyright busca un directorio de typings en el directorio raíz del proyecto, etc. Ejemplo de un stub parcial:
# _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