mirror of
https://github.com/zulip/zulip.git
synced 2026-06-30 21:11:04 +08:00
request: Map HttpRequest to ZulipRequestNotes for typing.
We create a class called ZulipRequestNotes as a new home to all the additional attributes that we add to the Django HttpRequest object. This allows mypy to do the typecheck and also enforces type safety. Most of the attributes are added in the middleware, and thus it is generally safe to assert that they are not None in a code path that goes through the middleware. The caller is obligated to do manual the type check otherwise. This also resolves some cyclic dependencies that zerver.lib.request have with zerver.lib.rate_limiter and zerver.tornado.handlers.
This commit is contained in:
parent
269f6fb577
commit
03693cd27e
@ -1,5 +1,7 @@
|
||||
import threading
|
||||
import weakref
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass, field
|
||||
from functools import wraps
|
||||
from types import FunctionType
|
||||
from typing import (
|
||||
@ -8,6 +10,7 @@ from typing import (
|
||||
Dict,
|
||||
Generic,
|
||||
List,
|
||||
MutableMapping,
|
||||
Optional,
|
||||
Sequence,
|
||||
TypeVar,
|
||||
@ -22,8 +25,54 @@ from django.http import HttpRequest, HttpResponse
|
||||
from django.utils.translation import gettext as _
|
||||
from typing_extensions import Literal
|
||||
|
||||
import zerver.lib.rate_limiter as rate_limiter
|
||||
import zerver.tornado.handlers as handlers
|
||||
from zerver.lib.exceptions import ErrorCode, InvalidJSONError, JsonableError
|
||||
from zerver.lib.types import Validator, ViewFuncT
|
||||
from zerver.models import Client, Realm
|
||||
|
||||
|
||||
@dataclass
|
||||
class ZulipRequestNotes:
|
||||
"""This class contains extra metadata that Zulip associated with a
|
||||
Django HttpRequest object.
|
||||
|
||||
Note that most Optional fields will be definitely not None once
|
||||
middlware has run. In the future, we may want to express that in
|
||||
the types by having different types ZulipEarlyRequestNotes and
|
||||
post-middleware ZulipRequestNotes types, but for now we have a lot
|
||||
of `assert request_notes.foo is not None` when accessing them.
|
||||
"""
|
||||
|
||||
client: Optional[Client] = None
|
||||
client_name: Optional[str] = None
|
||||
client_version: Optional[str] = None
|
||||
log_data: Optional[MutableMapping[str, Any]] = None
|
||||
rate_limit: Optional[str] = None
|
||||
requestor_for_logs: Optional[str] = None
|
||||
# We use realm_cached to indicate whether the realm is cached or not.
|
||||
# Because the default value of realm is None, which can indicate "unset"
|
||||
# and "nonexistence" at the same time.
|
||||
realm: Optional[Realm] = None
|
||||
has_fetched_realm: bool = False
|
||||
set_language: Optional[str] = None
|
||||
ratelimits_applied: List["rate_limiter.RateLimitResult"] = field(default_factory=lambda: [])
|
||||
query: Optional[str] = None
|
||||
error_format: Optional[str] = None
|
||||
placeholder_open_graph_description: Optional[str] = None
|
||||
saved_response: Optional[HttpResponse] = None
|
||||
tornado_handler: Optional["handlers.AsyncDjangoHandler"] = None
|
||||
|
||||
|
||||
request_notes_map: MutableMapping[HttpRequest, ZulipRequestNotes] = weakref.WeakKeyDictionary()
|
||||
|
||||
|
||||
def get_request_notes(request: HttpRequest) -> ZulipRequestNotes:
|
||||
try:
|
||||
return request_notes_map[request]
|
||||
except KeyError:
|
||||
request_notes_map[request] = ZulipRequestNotes()
|
||||
return request_notes_map[request]
|
||||
|
||||
|
||||
class RequestConfusingParmsError(JsonableError):
|
||||
|
||||
@ -49,9 +49,11 @@ from django.core.exceptions import ValidationError
|
||||
from django.core.validators import URLValidator, validate_email
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from zerver.lib.request import JsonableError, ResultT
|
||||
from zerver.lib.exceptions import JsonableError
|
||||
from zerver.lib.types import ProfileFieldData, Validator
|
||||
|
||||
ResultT = TypeVar("ResultT")
|
||||
|
||||
|
||||
def check_string(var_name: str, val: object) -> str:
|
||||
if not isinstance(val, str):
|
||||
|
||||
@ -13,7 +13,6 @@ from django.utils.cache import patch_vary_headers
|
||||
from tornado.wsgi import WSGIContainer
|
||||
|
||||
from zerver.lib.response import json_response
|
||||
from zerver.middleware import async_request_timer_restart, async_request_timer_stop
|
||||
from zerver.tornado.descriptors import get_descriptor_by_handler_id
|
||||
|
||||
current_handler_id = 0
|
||||
@ -45,6 +44,10 @@ def finish_handler(
|
||||
) -> None:
|
||||
err_msg = f"Got error finishing handler for queue {event_queue_id}"
|
||||
try:
|
||||
# We import async_request_timer_restart during runtime
|
||||
# to avoid cyclic dependency with zerver.lib.request
|
||||
from zerver.middleware import async_request_timer_restart
|
||||
|
||||
# We call async_request_timer_restart here in case we are
|
||||
# being finished without any events (because another
|
||||
# get_events request has supplanted this request)
|
||||
@ -143,6 +146,10 @@ class AsyncDjangoHandler(tornado.web.RequestHandler, base.BaseHandler):
|
||||
response = self.get_response(request)
|
||||
|
||||
if hasattr(response, "asynchronous"):
|
||||
# We import async_request_timer_restart during runtime
|
||||
# to avoid cyclic dependency with zerver.lib.request
|
||||
from zerver.middleware import async_request_timer_stop
|
||||
|
||||
# For asynchronous requests, this is where we exit
|
||||
# without returning the HttpResponse that Django
|
||||
# generated back to the user in order to long-poll the
|
||||
|
||||
Loading…
Reference in New Issue
Block a user