Flask의 request는 PEP 567의 Context Variables을 Werkzeug의 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.
https://peps.python.org/pep-0567/asyncio.Task
.
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()를 사용할 수 있다는 것일 뿐 반드시 해야하는 것은 아닙니다.