Flask의 request

Tags:

Flask의 request는 PEP 567의 Context VariablesWerkzeug의 LocalProxy로 구현한 객체입니다. Context Variables은 thread local을 개선합니다.

This concept is similar to thread-local storage (TLS), but, unlike TLS, it also allows correctly keeping track of values per asynchronous task, e.g. asyncio.Task.

https://peps.python.org/pep-0567/

LocalProxy는 매번 컨텍스트를 ContextVar.get() 을 사용할 필요 없이 마치 로컬 객체처럼 다루게 해줍니다. 아래 코드의 _request와 request를 비교하면 그 차이가 명확합니다.

from contextvars import ContextVar
from werkzeug.local import LocalProxy

_request_var = ContextVar("request")
request = LocalProxy(_request_var)

from werkzeug.wrappers import Request

@Request.application
def app(r):
    _request_var.set(r)
    check_auth()
    ...

from werkzeug.exceptions import Unauthorized

def check_auth():
    if request.form["username"] != "admin":
        raise Unauthorized()

Flask는 LocalStack, LocalProxy를 사용하여 request, g 등을 제공하게 됩니다. 따라서 쓰레드로 요청을 처리하든 asyncio를 사용해 처리하든 코드가 유연하게 대응하게 됩니다.

여기까지 도달하게 된 이유는 appcontext 설명에 나온 다음 코드 때문입니다.

from flask import g

def get_db():
    if 'db' not in g:
        g.db = connect_to_database()

    return g.db

@app.teardown_appcontext
def teardown_db(exception):
    db = g.pop('db', None)

    if db is not None:
        db.close()

...

# You can use LocalProxy to make a new context local from get_db():
from werkzeug.local import LocalProxy
db = LocalProxy(get_db)

여기서 LocalProxy를 사용할 수 도 있다고 한 것은 이미 g 가 proxy 이지만 원한다면 지역변수처럼 get_db()를 사용할 수 있다는 것일 뿐 반드시 해야하는 것은 아닙니다.