mirror of
https://github.com/zulip/zulip.git
synced 2025-11-02 13:03:29 +00:00
Since essentially the first use of Tornado in Zulip, we've been maintaining our Tornado+Django system, AsyncDjangoHandler, with several hundred lines of Django code copied into it. The goal for that code was simple: We wanted a way to use our Django middleware (for code sharing reasons) inside a Tornado process (since we wanted to use Tornado for our async events system). As part of the Django 2.2.x upgrade, I looked at upgrading this implementation to be based off modern Django, and it's definitely possible to do that: * Continue forking load_middleware to save response middleware. * Continue manually running the Django response middleware. * Continue working out a hack involving copying all of _get_response to change a couple lines allowing us our Tornado code to not actually return the Django HttpResponse so we can long-poll. The previous hack of returning None stopped being viable with the Django 2.2 MiddlewareMixin.__call__ implementation. But I decided to take this opportunity to look at trying to avoid copying material Django code, and there is a way to do it: * Replace RespondAsynchronously with a response.asynchronous attribute on the HttpResponse; this allows Django to run its normal plumbing happily in a way that should be stable over time, and then we proceed to discard the response inside the Tornado `get()` method to implement long-polling. (Better yet might be raising an exception?). This lets us eliminate maintaining a patched copy of _get_response. * Removing the @asynchronous decorator, which didn't add anything now that we only have one API endpoint backend (with two frontend call points) that could call into this. Combined with the last bullet, this lets us remove a significant hack from our never_cache_responses function. * Calling the normal Django `get_response` method from zulip_finish after creating a duplicate request to process, rather than writing totally custom code to do that. This lets us eliminate maintaining a patched copy of Django's load_middleware. * Adding detailed comments explaining how this is supposed to work, what problems we encounter, and how we solve various problems, which is critical to being able to modify this code in the future. A key advantage of these changes is that the exact same code should work on Django 1.11, Django 2.2, and Django 3.x, because we're no longer copying large blocks of core Django code and thus should be much less vulnerable to refactors. There may be a modest performance downside, in that we now run both request and response middleware twice when longpolling (once for the request we discard). We may be able to avoid the expensive part of it, Zulip's own request/response middleware, with a bit of additional custom code to save work for requests where we're planning to discard the response. Profiling will be important to understanding what's worth doing here.
133 lines
5.0 KiB
INI
133 lines
5.0 KiB
INI
[mypy]
|
|
# Logistics of what code to check and how to handle the data.
|
|
scripts_are_modules = True
|
|
show_traceback = True
|
|
# See https://zulip.readthedocs.io/en/latest/testing/mypy.html#mypy-stubs-for-third-party-modules
|
|
# for notes on how we manage mypy stubs.
|
|
mypy_path = stubs/
|
|
cache_dir = var/mypy-cache
|
|
|
|
# Options to make the checking stricter.
|
|
check_untyped_defs = True
|
|
disallow_untyped_defs = True
|
|
disallow_any_generics = True
|
|
warn_no_return = True
|
|
no_implicit_optional = True
|
|
|
|
# It's useful to try this occasionally, and keep it clean; but when
|
|
# someone fixes a type error we don't want to add a burden for them.
|
|
#warn_unused_ignores = True
|
|
|
|
# We use a lot of third-party libraries we don't have stubs for, as
|
|
# well as a handful of our own modules that we haven't told mypy how
|
|
# to find. Ignore them. (For some details, see:
|
|
# `git log -p -S ignore_missing_imports mypy.ini`.)
|
|
#
|
|
# This doesn't get in the way of using the stubs we *do* have.
|
|
ignore_missing_imports = True
|
|
|
|
# Warn of unreachable or redundant code.
|
|
warn_unreachable = True
|
|
|
|
#
|
|
#
|
|
# IGNORE ERRORS
|
|
#
|
|
#
|
|
|
|
# We suppress all errors in a handful of files, all of them config files.
|
|
|
|
[mypy-conf] # For docs/conf.py.
|
|
ignore_errors = True
|
|
|
|
# zerver.tornado.autoreload is a slightly-patched piece of upstream Tornado.
|
|
[mypy-zerver.tornado.autoreload]
|
|
ignore_errors = True
|
|
|
|
|
|
#
|
|
#
|
|
# STRICT OPTIONAL
|
|
#
|
|
#
|
|
|
|
strict_optional = True
|
|
|
|
# Various issues
|
|
|
|
[mypy-zerver.webhooks.gitlab.view]
|
|
strict_optional = False
|
|
|
|
[mypy-zerver.views.realm] # Other issues in this file too
|
|
strict_optional = False
|
|
|
|
# One change required?
|
|
|
|
[mypy-zerver.migrations.0077_add_file_name_field_to_realm_emoji] #73: error: Argument 2 to "upload_files" of "Uploader" has incompatible type "Optional[bytes]"; expected "bytes"
|
|
strict_optional = False
|
|
|
|
[mypy-zilencer.management.commands.add_new_realm] #22: error: List item 0 has incompatible type "Optional[Stream]"; expected "Stream"
|
|
strict_optional = False
|
|
|
|
# Re-architecting required?
|
|
|
|
[mypy-zerver.lib.queue] # Delayed setup of SimpleQueueClient.channel (Optional)
|
|
strict_optional = False
|
|
[mypy-zerver/management/commands/purge_queue] #24: error: Item "None" of "Optional[Any]" has no attribute "queue_purge"
|
|
strict_optional = False
|
|
|
|
# Tests (may be many issues in file; comment is just one error noted)
|
|
|
|
[mypy-zerver/tests/test_tornado] #202: error: Item "None" of "Optional[Morsel[Any]]" has no attribute "coded_value"
|
|
strict_optional = False
|
|
[mypy-zerver/tests/test_slack_importer] #70: error: Argument 1 to "MockResponse" has incompatible type "None"; expected "Dict[str, Any]"
|
|
strict_optional = False
|
|
[mypy-zerver/tests/test_service_bot_system] #312: error: Argument 1 to "set_bot_config" has incompatible type "Optional[UserProfile]"; expected "UserProfile"
|
|
strict_optional = False
|
|
[mypy-zerver/tests/test_outgoing_webhook_system] #33: error: Argument 1 to "MockServiceHandler" has incompatible type "None"; expected "str"
|
|
strict_optional = False
|
|
[mypy-zerver/tests/test_narrow] #515: error: Incompatible types in assignment (expression has type "None", variable has type "int")
|
|
strict_optional = False
|
|
[mypy-zerver/tests/test_logging_handlers] #73: error: Argument 7 to "makeRecord" of "Logger" has incompatible type "Tuple[Optional[Type[BaseException]], Optional[BaseException], Optional[TracebackType]]"; expected "Union[Tuple[type, BaseException, TracebackType], Tuple[None, None, None], None]"
|
|
strict_optional = False
|
|
[mypy-zerver/tests/test_decorators] #1322: error: Item "None" of "Optional[Match[str]]" has no attribute "groupdict"
|
|
strict_optional = False
|
|
[mypy-zerver/tests/test_auth_backends] #2079: error: Incompatible types in assignment (expression has type "Optional[UserProfile]", variable has type "UserProfile")
|
|
strict_optional = False
|
|
[mypy-zerver/tests/test_signup] #1954: error: Item "None" of "Optional[Match[Any]]" has no attribute "groups"
|
|
strict_optional = False
|
|
[mypy-zerver/tests/test_realm] #191: error: Item "None" of "Optional[Stream]" has no attribute "id"
|
|
strict_optional = False
|
|
[mypy-zerver/tests/test_messages] #3070: error: Incompatible types in assignment (expression has type "None", variable has type "int")
|
|
strict_optional = False
|
|
[mypy-zerver/tests/test_events] #1365: error: Argument 2 to "do_set_realm_notifications_stream" has incompatible type "Optional[Stream]"; expected "Stream"
|
|
strict_optional = False
|
|
[mypy-zerver/tests/test_docs] #131: error: Argument 1 to "isfile" has incompatible type "Optional[str]"; expected "Union[bytes, str]"
|
|
strict_optional = False
|
|
|
|
# General exclusions to work on
|
|
|
|
[mypy-zerver.lib.test_helpers]
|
|
strict_optional = False
|
|
[mypy-zerver.lib.test_classes]
|
|
strict_optional = False
|
|
|
|
[mypy-zerver.tornado.event_queue]
|
|
strict_optional = False
|
|
[mypy-zerver.lib.outgoing_webhook]
|
|
strict_optional = False
|
|
[mypy-zerver.lib.bugdown] # for __init__.py
|
|
strict_optional = False
|
|
[mypy-zerver.lib.push_notifications]
|
|
strict_optional = False
|
|
[mypy-zerver.worker.queue_processors]
|
|
strict_optional = False
|
|
[mypy-zerver.views.registration]
|
|
strict_optional = False
|
|
|
|
[mypy-tools.lib.html_branches]
|
|
strict_optional = False
|
|
|
|
[mypy-zthumbor.loaders.helpers]
|
|
strict_optional = False
|