Compare commits
	
		
			17 Commits
		
	
	
		
			response-e
			...
			ruff-only
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 00493a6954 | ||
|   | c8d0c1bf28 | ||
|   | 6ea5c44566 | ||
|   | 1afea39cb2 | ||
|   | e4daf1ab21 | ||
|   | 469cb1663b | ||
|   | cdc5dd6b75 | ||
|   | 6fac60c6fe | ||
|   | 53b7412c01 | ||
|   | 65ba1942cc | ||
|   | 9adb6e8ec0 | ||
|   | ec35f5f2c8 | ||
|   | 9ae25e6744 | ||
|   | 758f10c513 | ||
|   | 140d27ef96 | ||
|   | 209840b771 | ||
|   | 20fd58b8d7 | 
| @@ -1,5 +1,4 @@ | |||||||
| #!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||||
| # -*- coding: utf-8 -*- |  | ||||||
| # | # | ||||||
| # Sanic documentation build configuration file, created by | # Sanic documentation build configuration file, created by | ||||||
| # sphinx-quickstart on Sun Dec 25 18:07:21 2016. | # sphinx-quickstart on Sun Dec 25 18:07:21 2016. | ||||||
| @@ -10,7 +9,6 @@ | |||||||
| import os | import os | ||||||
| import sys | import sys | ||||||
|  |  | ||||||
|  |  | ||||||
| # Add support for auto-doc | # Add support for auto-doc | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -19,8 +17,7 @@ import sys | |||||||
| root_directory = os.path.dirname(os.getcwd()) | root_directory = os.path.dirname(os.getcwd()) | ||||||
| sys.path.insert(0, root_directory) | sys.path.insert(0, root_directory) | ||||||
|  |  | ||||||
| import sanic | import sanic  # noqa: E402 | ||||||
|  |  | ||||||
|  |  | ||||||
| # -- General configuration ------------------------------------------------ | # -- General configuration ------------------------------------------------ | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,10 +1,7 @@ | |||||||
| # -*- coding: utf-8 -*- |  | ||||||
|  |  | ||||||
| import asyncio | import asyncio | ||||||
|  |  | ||||||
| from sanic import Sanic | from sanic import Sanic | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -3,7 +3,6 @@ from random import randint | |||||||
| from sanic import Sanic | from sanic import Sanic | ||||||
| from sanic.response import text | from sanic.response import text | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -25,5 +24,6 @@ def key_exist_handler(request): | |||||||
|  |  | ||||||
|     return text("num does not exist in request") |     return text("num does not exist in request") | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     app.run(host="0.0.0.0", port=8000, debug=True) |     app.run(host="0.0.0.0", port=8000, debug=True) | ||||||
|   | |||||||
| @@ -1,11 +1,8 @@ | |||||||
| # -*- coding: utf-8 -*- |  | ||||||
|  |  | ||||||
| from functools import wraps | from functools import wraps | ||||||
|  |  | ||||||
| from sanic import Sanic | from sanic import Sanic | ||||||
| from sanic.response import json | from sanic.response import json | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| from sanic import Blueprint, Sanic | from sanic import Blueprint, Sanic | ||||||
| from sanic.response import text | from sanic.response import text | ||||||
|  |  | ||||||
|  |  | ||||||
| """ | """ | ||||||
| Demonstrates that blueprint request middleware are executed in the order they | Demonstrates that blueprint request middleware are executed in the order they | ||||||
| are added. And blueprint response middleware are executed in _reverse_ order. | are added. And blueprint response middleware are executed in _reverse_ order. | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| from sanic import Blueprint, Sanic | from sanic import Blueprint, Sanic | ||||||
| from sanic.response import file, json | from sanic.response import file, json | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
| blueprint = Blueprint("bp_example", url_prefix="/my_blueprint") | blueprint = Blueprint("bp_example", url_prefix="/my_blueprint") | ||||||
| blueprint2 = Blueprint("bp_example2", url_prefix="/my_blueprint2") | blueprint2 = Blueprint("bp_example2", url_prefix="/my_blueprint2") | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ from asyncio import sleep | |||||||
|  |  | ||||||
| from sanic import Sanic, response | from sanic import Sanic, response | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("DelayedResponseApp", strict_slashes=True) | app = Sanic("DelayedResponseApp", strict_slashes=True) | ||||||
| app.config.AUTO_EXTEND = False | app.config.AUTO_EXTEND = False | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,7 +10,6 @@ an external service. | |||||||
| from sanic.exceptions import SanicException | from sanic.exceptions import SanicException | ||||||
| from sanic.handlers import ErrorHandler | from sanic.handlers import ErrorHandler | ||||||
|  |  | ||||||
|  |  | ||||||
| """ | """ | ||||||
| Imports and code relevant for our CustomHandler class | Imports and code relevant for our CustomHandler class | ||||||
| (Ordinarily this would be in a separate file) | (Ordinarily this would be in a separate file) | ||||||
| @@ -39,7 +38,6 @@ server's error_handler to an instance of our CustomHandler | |||||||
|  |  | ||||||
| from sanic import Sanic | from sanic import Sanic | ||||||
|  |  | ||||||
|  |  | ||||||
| handler = CustomHandler() | handler = CustomHandler() | ||||||
| app = Sanic("Example", error_handler=handler) | app = Sanic("Example", error_handler=handler) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| from sanic import Sanic, response | from sanic import Sanic, response | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ from sanic import Sanic, response, text | |||||||
| from sanic.handlers import ErrorHandler | from sanic.handlers import ErrorHandler | ||||||
| from sanic.server.async_server import AsyncioServer | from sanic.server.async_server import AsyncioServer | ||||||
|  |  | ||||||
|  |  | ||||||
| HTTP_PORT = 9999 | HTTP_PORT = 9999 | ||||||
| HTTPS_PORT = 8888 | HTTPS_PORT = 8888 | ||||||
|  |  | ||||||
| @@ -36,9 +35,7 @@ def proxy(request, path): | |||||||
|  |  | ||||||
| @https.main_process_start | @https.main_process_start | ||||||
| async def start(app, _): | async def start(app, _): | ||||||
|     http_server = await http.create_server( |     http_server = await http.create_server(port=HTTP_PORT, return_asyncio_server=True) | ||||||
|         port=HTTP_PORT, return_asyncio_server=True |  | ||||||
|     ) |  | ||||||
|     app.add_task(runner(http, http_server)) |     app.add_task(runner(http, http_server)) | ||||||
|     app.ctx.http_server = http_server |     app.ctx.http_server = http_server | ||||||
|     app.ctx.http = http |     app.ctx.http = http | ||||||
| @@ -69,5 +66,6 @@ async def runner(app: Sanic, app_server: AsyncioServer): | |||||||
|         app.is_running = False |         app.is_running = False | ||||||
|         app.is_stopping = True |         app.is_stopping = True | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     https.run(port=HTTPS_PORT, debug=True) |     https.run(port=HTTPS_PORT, debug=True) | ||||||
|   | |||||||
| @@ -5,7 +5,6 @@ import httpx | |||||||
| from sanic import Sanic | from sanic import Sanic | ||||||
| from sanic.response import json | from sanic.response import json | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
| sem = None | sem = None | ||||||
|   | |||||||
| @@ -1,10 +1,8 @@ | |||||||
| import logging | import logging | ||||||
|  |  | ||||||
| from contextvars import ContextVar | from contextvars import ContextVar | ||||||
|  |  | ||||||
| from sanic import Sanic, response | from sanic import Sanic, response | ||||||
|  |  | ||||||
|  |  | ||||||
| log = logging.getLogger(__name__) | log = logging.getLogger(__name__) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| import logging | import logging | ||||||
| import socket | import socket | ||||||
|  |  | ||||||
| from os import getenv | from os import getenv | ||||||
| from platform import node | from platform import node | ||||||
| from uuid import getnode as get_mac | from uuid import getnode as get_mac | ||||||
| @@ -11,7 +10,6 @@ from sanic import Sanic | |||||||
| from sanic.request import Request | from sanic.request import Request | ||||||
| from sanic.response import json | from sanic.response import json | ||||||
|  |  | ||||||
|  |  | ||||||
| log = logging.getLogger("logdna") | log = logging.getLogger("logdna") | ||||||
| log.setLevel(logging.INFO) | log.setLevel(logging.INFO) | ||||||
|  |  | ||||||
| @@ -35,9 +33,7 @@ logdna_options = { | |||||||
|     "mac": get_mac_address(), |     "mac": get_mac_address(), | ||||||
| } | } | ||||||
|  |  | ||||||
| logdna_handler = LogDNAHandler( | logdna_handler = LogDNAHandler(getenv("LOGDNA_API_KEY"), options=logdna_options) | ||||||
|     getenv("LOGDNA_API_KEY"), options=logdna_options |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| logdna = logging.getLogger(__name__) | logdna = logging.getLogger(__name__) | ||||||
| logdna.setLevel(logging.INFO) | logdna.setLevel(logging.INFO) | ||||||
| @@ -48,7 +44,7 @@ app = Sanic("Example") | |||||||
|  |  | ||||||
| @app.middleware | @app.middleware | ||||||
| def log_request(request: Request): | def log_request(request: Request): | ||||||
|     logdna.info("I was Here with a new Request to URL: {}".format(request.url)) |     logdna.info(f"I was Here with a new Request to URL: {request.url}") | ||||||
|  |  | ||||||
|  |  | ||||||
| @app.route("/") | @app.route("/") | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ Modify header or status in response | |||||||
|  |  | ||||||
| from sanic import Sanic, response | from sanic import Sanic, response | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ import logging | |||||||
|  |  | ||||||
| from sanic import Sanic, text | from sanic import Sanic, text | ||||||
|  |  | ||||||
|  |  | ||||||
| logging_format = "[%(asctime)s] %(process)d-%(levelname)s " | logging_format = "[%(asctime)s] %(process)d-%(levelname)s " | ||||||
| logging_format += "%(module)s::%(funcName)s():l%(lineno)d: " | logging_format += "%(module)s::%(funcName)s():l%(lineno)d: " | ||||||
| logging_format += "%(message)s" | logging_format += "%(message)s" | ||||||
|   | |||||||
| @@ -11,7 +11,6 @@ Run with xdist params: | |||||||
| import re | import re | ||||||
|  |  | ||||||
| import pytest | import pytest | ||||||
|  |  | ||||||
| from sanic_testing import SanicTestClient | from sanic_testing import SanicTestClient | ||||||
| from sanic_testing.testing import PORT as PORT_BASE | from sanic_testing.testing import PORT as PORT_BASE | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| from sanic import Sanic, response | from sanic import Sanic, response | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ from sanic.response import stream, text | |||||||
| from sanic.views import HTTPMethodView | from sanic.views import HTTPMethodView | ||||||
| from sanic.views import stream as stream_decorator | from sanic.views import stream as stream_decorator | ||||||
|  |  | ||||||
|  |  | ||||||
| bp = Blueprint("bp_example") | bp = Blueprint("bp_example") | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ from sanic import Sanic, response | |||||||
| from sanic.config import Config | from sanic.config import Config | ||||||
| from sanic.exceptions import RequestTimeout | from sanic.exceptions import RequestTimeout | ||||||
|  |  | ||||||
|  |  | ||||||
| Config.REQUEST_TIMEOUT = 1 | Config.REQUEST_TIMEOUT = 1 | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,7 +6,6 @@ from sanic import Sanic | |||||||
| from sanic.exceptions import SanicException | from sanic.exceptions import SanicException | ||||||
| from sanic.handlers import ErrorHandler | from sanic.handlers import ErrorHandler | ||||||
|  |  | ||||||
|  |  | ||||||
| rollbar.init(getenv("ROLLBAR_API_KEY")) | rollbar.init(getenv("ROLLBAR_API_KEY")) | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -10,7 +10,6 @@ from pathlib import Path | |||||||
|  |  | ||||||
| from sanic import Sanic, response | from sanic import Sanic, response | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -43,9 +42,7 @@ async def handler_file(request): | |||||||
|  |  | ||||||
| @app.route("/file_stream") | @app.route("/file_stream") | ||||||
| async def handler_file_stream(request): | async def handler_file_stream(request): | ||||||
|     return await response.file_stream( |     return await response.file_stream(Path("../") / "setup.py", chunk_size=1024) | ||||||
|         Path("../") / "setup.py", chunk_size=1024 |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| @app.post("/stream", stream=True) | @app.post("/stream", stream=True) | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ import uvloop | |||||||
|  |  | ||||||
| from sanic import Sanic, response | from sanic import Sanic, response | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,4 @@ | |||||||
| import asyncio | import asyncio | ||||||
|  |  | ||||||
| from signal import SIGINT, signal | from signal import SIGINT, signal | ||||||
|  |  | ||||||
| import uvloop | import uvloop | ||||||
| @@ -7,7 +6,6 @@ import uvloop | |||||||
| from sanic import Sanic, response | from sanic import Sanic, response | ||||||
| from sanic.server import AsyncioServer | from sanic.server import AsyncioServer | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -35,11 +33,10 @@ async def after_server_stop(app, loop): | |||||||
| async def test(request): | async def test(request): | ||||||
|     return response.json({"answer": "42"}) |     return response.json({"answer": "42"}) | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|     asyncio.set_event_loop(uvloop.new_event_loop()) |     asyncio.set_event_loop(uvloop.new_event_loop()) | ||||||
|     serv_coro = app.create_server( |     serv_coro = app.create_server(host="0.0.0.0", port=8000, return_asyncio_server=True) | ||||||
|         host="0.0.0.0", port=8000, return_asyncio_server=True |  | ||||||
|     ) |  | ||||||
|     loop = asyncio.get_event_loop() |     loop = asyncio.get_event_loop() | ||||||
|     serv_task = asyncio.ensure_future(serv_coro, loop=loop) |     serv_task = asyncio.ensure_future(serv_coro, loop=loop) | ||||||
|     signal(SIGINT, lambda s, f: loop.stop()) |     signal(SIGINT, lambda s, f: loop.stop()) | ||||||
|   | |||||||
| @@ -6,7 +6,6 @@ from sentry_sdk.integrations.sanic import SanicIntegration | |||||||
| from sanic import Sanic | from sanic import Sanic | ||||||
| from sanic.response import json | from sanic.response import json | ||||||
|  |  | ||||||
|  |  | ||||||
| sentry_init( | sentry_init( | ||||||
|     dsn=getenv("SENTRY_DSN"), |     dsn=getenv("SENTRY_DSN"), | ||||||
|     integrations=[SanicIntegration()], |     integrations=[SanicIntegration()], | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ from sanic import Sanic | |||||||
| from sanic.response import text | from sanic.response import text | ||||||
| from sanic.views import HTTPMethodView | from sanic.views import HTTPMethodView | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("some_name") | app = Sanic("some_name") | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| from sanic import Sanic | from sanic import Sanic | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
| app.static("/", "./static") | app.static("/", "./static") | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| from sanic import Sanic | from sanic import Sanic | ||||||
| from sanic import response as res | from sanic import response as res | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ from sanic import Sanic, response | |||||||
| from sanic.exceptions import ServerError | from sanic.exceptions import ServerError | ||||||
| from sanic.log import logger as log | from sanic.log import logger as log | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -20,7 +19,7 @@ def test_sync(request): | |||||||
|  |  | ||||||
| @app.route("/dynamic/<name>/<i:int>") | @app.route("/dynamic/<name>/<i:int>") | ||||||
| def test_params(request, name, i): | def test_params(request, name, i): | ||||||
|     return response.text("yeehaww {} {}".format(name, i)) |     return response.text(f"yeehaww {name} {i}") | ||||||
|  |  | ||||||
|  |  | ||||||
| @app.route("/exception") | @app.route("/exception") | ||||||
| @@ -43,9 +42,7 @@ async def test_file(request): | |||||||
|  |  | ||||||
| @app.route("/file_stream") | @app.route("/file_stream") | ||||||
| async def test_file_stream(request): | async def test_file_stream(request): | ||||||
|     return await response.file_stream( |     return await response.file_stream(os.path.abspath("setup.py"), chunk_size=1024) | ||||||
|         os.path.abspath("setup.py"), chunk_size=1024 |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| # ----------------------------------------------- # | # ----------------------------------------------- # | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| from sanic import Sanic, response | from sanic import Sanic, response | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| from sanic import Sanic, response | from sanic import Sanic, response | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -14,7 +13,7 @@ async def index(request): | |||||||
|  |  | ||||||
| @app.route("/posts/<post_id>") | @app.route("/posts/<post_id>") | ||||||
| async def post_handler(request, post_id): | async def post_handler(request, post_id): | ||||||
|     return response.text("Post - {}".format(post_id)) |     return response.text(f"Post - {post_id}") | ||||||
|  |  | ||||||
|  |  | ||||||
| if __name__ == "__main__": | if __name__ == "__main__": | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ from sanic import Sanic | |||||||
| from sanic.blueprints import Blueprint | from sanic.blueprints import Blueprint | ||||||
| from sanic.response import json | from sanic.response import json | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic(name="blue-print-group-version-example") | app = Sanic(name="blue-print-group-version-example") | ||||||
|  |  | ||||||
| bp1 = Blueprint(name="ultron", url_prefix="/ultron") | bp1 = Blueprint(name="ultron", url_prefix="/ultron") | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| from sanic import Sanic, response | from sanic import Sanic, response | ||||||
| from sanic.blueprints import Blueprint | from sanic.blueprints import Blueprint | ||||||
|  |  | ||||||
|  |  | ||||||
| # Usage | # Usage | ||||||
| # curl -H "Host: example.com" localhost:8000 | # curl -H "Host: example.com" localhost:8000 | ||||||
| # curl -H "Host: sub.example.com" localhost:8000 | # curl -H "Host: sub.example.com" localhost:8000 | ||||||
| @@ -12,9 +11,7 @@ app = Sanic("Example") | |||||||
| bp = Blueprint("bp", host="bp.example.com") | bp = Blueprint("bp", host="bp.example.com") | ||||||
|  |  | ||||||
|  |  | ||||||
| @app.route( | @app.route("/", host=["example.com", "somethingelse.com", "therestofyourdomains.com"]) | ||||||
|     "/", host=["example.com", "somethingelse.com", "therestofyourdomains.com"] |  | ||||||
| ) |  | ||||||
| async def hello_0(request): | async def hello_0(request): | ||||||
|     return response.text("Some defaults") |     return response.text("Some defaults") | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| from sanic import Sanic | from sanic import Sanic | ||||||
| from sanic.response import redirect | from sanic.response import redirect | ||||||
|  |  | ||||||
|  |  | ||||||
| app = Sanic("Example") | app = Sanic("Example") | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -7,14 +7,10 @@ from emoji import EMOJI | |||||||
| COLUMN_PATTERN = re.compile(r"---:1\s*(.*?)\s*:--:1\s*(.*?)\s*:---", re.DOTALL) | COLUMN_PATTERN = re.compile(r"---:1\s*(.*?)\s*:--:1\s*(.*?)\s*:---", re.DOTALL) | ||||||
| PYTHON_HIGHLIGHT_PATTERN = re.compile(r"```python\{+.*?\}", re.DOTALL) | PYTHON_HIGHLIGHT_PATTERN = re.compile(r"```python\{+.*?\}", re.DOTALL) | ||||||
| BASH_HIGHLIGHT_PATTERN = re.compile(r"```bash\{+.*?\}", re.DOTALL) | BASH_HIGHLIGHT_PATTERN = re.compile(r"```bash\{+.*?\}", re.DOTALL) | ||||||
| NOTIFICATION_PATTERN = re.compile( | NOTIFICATION_PATTERN = re.compile(r":::\s*(\w+)\s*(.*?)\n([\s\S]*?):::", re.MULTILINE) | ||||||
|     r":::\s*(\w+)\s*(.*?)\n([\s\S]*?):::", re.MULTILINE |  | ||||||
| ) |  | ||||||
| EMOJI_PATTERN = re.compile(r":(\w+):") | EMOJI_PATTERN = re.compile(r":(\w+):") | ||||||
| CURRENT_DIR = Path(__file__).parent | CURRENT_DIR = Path(__file__).parent | ||||||
| SOURCE_DIR = ( | SOURCE_DIR = CURRENT_DIR.parent.parent.parent.parent / "sanic-guide" / "src" / "en" | ||||||
|     CURRENT_DIR.parent.parent.parent.parent / "sanic-guide" / "src" / "en" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def convert_columns(content: str): | def convert_columns(content: str): | ||||||
|   | |||||||
							
								
								
									
										5
									
								
								guide/pyproject.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								guide/pyproject.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | |||||||
|  | [tool.ruff] | ||||||
|  | extend = "../pyproject.toml" | ||||||
|  |  | ||||||
|  | [tool.ruff.isort] | ||||||
|  | known-first-party = ["webapp"] | ||||||
| @@ -13,9 +13,7 @@ def do_footer(builder: Builder, request: Request) -> None: | |||||||
|  |  | ||||||
|  |  | ||||||
| def _pagination(request: Request) -> Builder: | def _pagination(request: Request) -> Builder: | ||||||
|     return E.div( |     return E.div(_pagination_left(request), _pagination_right(request), class_="level") | ||||||
|         _pagination_left(request), _pagination_right(request), class_="level" |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def _pagination_left(request: Request) -> Builder: | def _pagination_left(request: Request) -> Builder: | ||||||
| @@ -64,9 +62,7 @@ def _content() -> Builder: | |||||||
|             href="https://github.com/sanic-org/sanic/blob/master/LICENSE", |             href="https://github.com/sanic-org/sanic/blob/master/LICENSE", | ||||||
|             target="_blank", |             target="_blank", | ||||||
|             rel="nofollow noopener noreferrer", |             rel="nofollow noopener noreferrer", | ||||||
|         ).br()( |         ).br()(E.small(f"Copyright © 2018-{year} Sanic Community Organization")), | ||||||
|             E.small(f"Copyright © 2018-{year} Sanic Community Organization") |  | ||||||
|         ), |  | ||||||
|     ) |     ) | ||||||
|     return E.div( |     return E.div( | ||||||
|         inner, |         inner, | ||||||
|   | |||||||
| @@ -1,17 +1,14 @@ | |||||||
| from webapp.display.layouts.models import MenuItem |  | ||||||
|  |  | ||||||
| from html5tagger import Builder, E  # type: ignore | from html5tagger import Builder, E  # type: ignore | ||||||
| from sanic import Request | from sanic import Request | ||||||
|  |  | ||||||
|  | from webapp.display.layouts.models import MenuItem | ||||||
|  |  | ||||||
|  |  | ||||||
| def do_navbar(builder: Builder, request: Request) -> None: | def do_navbar(builder: Builder, request: Request) -> None: | ||||||
|     navbar_items = [ |     navbar_items = [ | ||||||
|         _render_navbar_item(item, request) |         _render_navbar_item(item, request) for item in request.app.config.NAVBAR | ||||||
|         for item in request.app.config.NAVBAR |  | ||||||
|     ] |     ] | ||||||
|     container = E.div( |     container = E.div(_search_form(request), *navbar_items, class_="navbar-end") | ||||||
|         _search_form(request), *navbar_items, class_="navbar-end" |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     builder.nav( |     builder.nav( | ||||||
|         E.div(container, class_="navbar-menu"), |         E.div(container, class_="navbar-menu"), | ||||||
| @@ -46,10 +43,7 @@ def _render_navbar_item(item: MenuItem, request: Request) -> Builder: | |||||||
|         return E.div( |         return E.div( | ||||||
|             E.a(item.label, class_="navbar-link"), |             E.a(item.label, class_="navbar-link"), | ||||||
|             E.div( |             E.div( | ||||||
|                 *( |                 *(_render_navbar_item(subitem, request) for subitem in item.items), | ||||||
|                     _render_navbar_item(subitem, request) |  | ||||||
|                     for subitem in item.items |  | ||||||
|                 ), |  | ||||||
|                 class_="navbar-dropdown", |                 class_="navbar-dropdown", | ||||||
|             ), |             ), | ||||||
|             class_="navbar-item has-dropdown is-hoverable", |             class_="navbar-item has-dropdown is-hoverable", | ||||||
|   | |||||||
| @@ -1,9 +1,9 @@ | |||||||
| from webapp.display.layouts.models import MenuItem |  | ||||||
| from webapp.display.text import slugify |  | ||||||
|  |  | ||||||
| from html5tagger import Builder, E  # type: ignore | from html5tagger import Builder, E  # type: ignore | ||||||
| from sanic import Request | from sanic import Request | ||||||
|  |  | ||||||
|  | from webapp.display.layouts.models import MenuItem | ||||||
|  | from webapp.display.text import slugify | ||||||
|  |  | ||||||
|  |  | ||||||
| def do_sidebar(builder: Builder, request: Request) -> None: | def do_sidebar(builder: Builder, request: Request) -> None: | ||||||
|     builder.a(class_="burger")(E.span().span().span().span()) |     builder.a(class_="burger")(E.span().span().span().span()) | ||||||
| @@ -15,9 +15,7 @@ def _menu_items(request: Request) -> list[Builder]: | |||||||
|         _sanic_logo(request), |         _sanic_logo(request), | ||||||
|         *_sidebar_items(request), |         *_sidebar_items(request), | ||||||
|         E.hr(), |         E.hr(), | ||||||
|         E.p("Current with version ").strong( |         E.p("Current with version ").strong(request.app.config.GENERAL.current_version), | ||||||
|             request.app.config.GENERAL.current_version |  | ||||||
|         ), |  | ||||||
|         E.hr(), |         E.hr(), | ||||||
|         E.p("Want more? ").a( |         E.p("Want more? ").a( | ||||||
|             "sanicbook.com", href="https://sanicbook.com", target="_blank" |             "sanicbook.com", href="https://sanicbook.com", target="_blank" | ||||||
| @@ -73,9 +71,7 @@ def _single_sidebar_item(item: MenuItem, request: Request) -> Builder: | |||||||
|     kwargs = {} |     kwargs = {} | ||||||
|     classes: list[str] = [] |     classes: list[str] = [] | ||||||
|     li_classes = "menu-item" |     li_classes = "menu-item" | ||||||
|     _, page, _ = request.app.ctx.get_page( |     _, page, _ = request.app.ctx.get_page(request.ctx.language, item.path or "") | ||||||
|         request.ctx.language, item.path or "" |  | ||||||
|     ) |  | ||||||
|     if request.path == path: |     if request.path == path: | ||||||
|         classes.append("is-active") |         classes.append("is-active") | ||||||
|     if item.href: |     if item.href: | ||||||
|   | |||||||
| @@ -1,12 +1,12 @@ | |||||||
| from contextlib import contextmanager | from contextlib import contextmanager | ||||||
| from typing import Generator | from typing import Generator | ||||||
|  |  | ||||||
|  | from sanic import Request | ||||||
|  |  | ||||||
| from webapp.display.layouts.elements.footer import do_footer | from webapp.display.layouts.elements.footer import do_footer | ||||||
| from webapp.display.layouts.elements.navbar import do_navbar | from webapp.display.layouts.elements.navbar import do_navbar | ||||||
| from webapp.display.layouts.elements.sidebar import do_sidebar | from webapp.display.layouts.elements.sidebar import do_sidebar | ||||||
|  |  | ||||||
| from sanic import Request |  | ||||||
|  |  | ||||||
| from .base import BaseLayout | from .base import BaseLayout | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| import re | import re | ||||||
| from textwrap import dedent | from textwrap import dedent | ||||||
|  |  | ||||||
|  | from html5tagger import HTML, Builder, E  # type: ignore | ||||||
| from mistune import HTMLRenderer, create_markdown, escape | from mistune import HTMLRenderer, create_markdown, escape | ||||||
| from mistune.directives import RSTDirective, TableOfContents | from mistune.directives import RSTDirective, TableOfContents | ||||||
| from mistune.util import safe_entity | from mistune.util import safe_entity | ||||||
| @@ -8,8 +9,6 @@ from pygments import highlight | |||||||
| from pygments.formatters import html | from pygments.formatters import html | ||||||
| from pygments.lexers import get_lexer_by_name | from pygments.lexers import get_lexer_by_name | ||||||
|  |  | ||||||
| from html5tagger import HTML, Builder, E  # type: ignore |  | ||||||
|  |  | ||||||
| from .code_style import SanicCodeStyle | from .code_style import SanicCodeStyle | ||||||
| from .plugins.attrs import Attributes | from .plugins.attrs import Attributes | ||||||
| from .plugins.columns import Column | from .plugins.columns import Column | ||||||
| @@ -37,9 +36,9 @@ class DocsRenderer(HTMLRenderer): | |||||||
|                     class_="code-block__copy", |                     class_="code-block__copy", | ||||||
|                     onclick="copyCode(this)", |                     onclick="copyCode(this)", | ||||||
|                 ): |                 ): | ||||||
|                     builder.div( |                     builder.div(class_="code-block__rectangle code-block__filled").div( | ||||||
|                         class_="code-block__rectangle code-block__filled" |                         class_="code-block__rectangle code-block__outlined" | ||||||
|                     ).div(class_="code-block__rectangle code-block__outlined") |                     ) | ||||||
|             else: |             else: | ||||||
|                 builder.pre(E.code(escape(code))) |                 builder.pre(E.code(escape(code))) | ||||||
|         return str(builder) |         return str(builder) | ||||||
| @@ -47,9 +46,7 @@ class DocsRenderer(HTMLRenderer): | |||||||
|     def heading(self, text: str, level: int, **attrs) -> str: |     def heading(self, text: str, level: int, **attrs) -> str: | ||||||
|         ident = slugify(text) |         ident = slugify(text) | ||||||
|         if level > 1: |         if level > 1: | ||||||
|             text += self._make_tag( |             text += self._make_tag("a", {"href": f"#{ident}", "class": "anchor"}, "#") | ||||||
|                 "a", {"href": f"#{ident}", "class": "anchor"}, "#" |  | ||||||
|             ) |  | ||||||
|         return self._make_tag( |         return self._make_tag( | ||||||
|             f"h{level}", {"id": ident, "class": f"is-size-{level}"}, text |             f"h{level}", {"id": ident, "class": f"is-size-{level}"}, text | ||||||
|         ) |         ) | ||||||
| @@ -93,9 +90,7 @@ class DocsRenderer(HTMLRenderer): | |||||||
|     def _make_tag( |     def _make_tag( | ||||||
|         self, tag: str, attributes: dict[str, str], text: str | None = None |         self, tag: str, attributes: dict[str, str], text: str | None = None | ||||||
|     ) -> str: |     ) -> str: | ||||||
|         attrs = " ".join( |         attrs = " ".join(f'{key}="{value}"' for key, value in attributes.items()) | ||||||
|             f'{key}="{value}"' for key, value in attributes.items() |  | ||||||
|         ) |  | ||||||
|         if text is None: |         if text is None: | ||||||
|             return f"<{tag} {attrs} />" |             return f"<{tag} {attrs} />" | ||||||
|         return f"<{tag} {attrs}>{text}</{tag}>" |         return f"<{tag} {attrs}>{text}</{tag}>" | ||||||
|   | |||||||
| @@ -10,7 +10,6 @@ from html import escape | |||||||
| from docstring_parser import Docstring, DocstringParam, DocstringRaises | from docstring_parser import Docstring, DocstringParam, DocstringRaises | ||||||
| from docstring_parser import parse as parse_docstring | from docstring_parser import parse as parse_docstring | ||||||
| from docstring_parser.common import DocstringExample | from docstring_parser.common import DocstringExample | ||||||
|  |  | ||||||
| from html5tagger import HTML, Builder, E  # type: ignore | from html5tagger import HTML, Builder, E  # type: ignore | ||||||
|  |  | ||||||
| from ..markdown import render_markdown, slugify | from ..markdown import render_markdown, slugify | ||||||
| @@ -120,9 +119,7 @@ def _extract_docobjects(package_name: str) -> dict[str, DocObject]: | |||||||
|     docstrings = {} |     docstrings = {} | ||||||
|     package = importlib.import_module(package_name) |     package = importlib.import_module(package_name) | ||||||
|  |  | ||||||
|     for _, name, _ in pkgutil.walk_packages( |     for _, name, _ in pkgutil.walk_packages(package.__path__, package_name + "."): | ||||||
|         package.__path__, package_name + "." |  | ||||||
|     ): |  | ||||||
|         module = importlib.import_module(name) |         module = importlib.import_module(name) | ||||||
|         for obj_name, obj in inspect.getmembers(module): |         for obj_name, obj in inspect.getmembers(module): | ||||||
|             if ( |             if ( | ||||||
| @@ -156,9 +153,7 @@ def _docobject_to_html( | |||||||
| ) -> None: | ) -> None: | ||||||
|     anchor_id = slugify(docobject.full_name.replace(".", "-")) |     anchor_id = slugify(docobject.full_name.replace(".", "-")) | ||||||
|     anchor = E.a("#", class_="anchor", href=f"#{anchor_id}") |     anchor = E.a("#", class_="anchor", href=f"#{anchor_id}") | ||||||
|     class_name, heading = _define_heading_and_class( |     class_name, heading = _define_heading_and_class(docobject, anchor, as_method) | ||||||
|         docobject, anchor, as_method |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     with builder.div(class_=class_name): |     with builder.div(class_=class_name): | ||||||
|         builder(heading) |         builder(heading) | ||||||
| @@ -212,9 +207,7 @@ def _docobject_to_html( | |||||||
|  |  | ||||||
|         if docobject.docstring.params: |         if docobject.docstring.params: | ||||||
|             with builder.div(class_="box mt-5"): |             with builder.div(class_="box mt-5"): | ||||||
|                 builder.h5( |                 builder.h5("Parameters", class_="is-size-5 has-text-weight-bold") | ||||||
|                     "Parameters", class_="is-size-5 has-text-weight-bold" |  | ||||||
|                 ) |  | ||||||
|                 _render_params(builder, docobject.docstring.params) |                 _render_params(builder, docobject.docstring.params) | ||||||
|  |  | ||||||
|         if docobject.docstring.returns: |         if docobject.docstring.returns: | ||||||
| @@ -239,9 +232,7 @@ def _signature_to_html( | |||||||
|     parts = [] |     parts = [] | ||||||
|     parts.append("<span class='function-signature'>") |     parts.append("<span class='function-signature'>") | ||||||
|     for decorator in decorators: |     for decorator in decorators: | ||||||
|         parts.append( |         parts.append(f"<span class='function-decorator'>@{decorator}</span><br>") | ||||||
|             f"<span class='function-decorator'>@{decorator}</span><br>" |  | ||||||
|         ) |  | ||||||
|     parts.append( |     parts.append( | ||||||
|         f"<span class='is-italic'>{object_type}</span> " |         f"<span class='is-italic'>{object_type}</span> " | ||||||
|         f"<span class='has-text-weight-bold'>{name}</span>(" |         f"<span class='has-text-weight-bold'>{name}</span>(" | ||||||
| @@ -255,9 +246,7 @@ def _signature_to_html( | |||||||
|         annotation = "" |         annotation = "" | ||||||
|         if param.annotation != inspect.Parameter.empty: |         if param.annotation != inspect.Parameter.empty: | ||||||
|             annotation = escape(str(param.annotation)) |             annotation = escape(str(param.annotation)) | ||||||
|             parts.append( |             parts.append(f": <span class='param-annotation'>{annotation}</span>") | ||||||
|                 f": <span class='param-annotation'>{annotation}</span>" |  | ||||||
|             ) |  | ||||||
|         if param.default != inspect.Parameter.empty: |         if param.default != inspect.Parameter.empty: | ||||||
|             default = escape(str(param.default)) |             default = escape(str(param.default)) | ||||||
|             if annotation == "str": |             if annotation == "str": | ||||||
| @@ -268,9 +257,7 @@ def _signature_to_html( | |||||||
|     parts.append(")") |     parts.append(")") | ||||||
|     if signature.return_annotation != inspect.Signature.empty: |     if signature.return_annotation != inspect.Signature.empty: | ||||||
|         return_annotation = escape(str(signature.return_annotation)) |         return_annotation = escape(str(signature.return_annotation)) | ||||||
|         parts.append( |         parts.append(f": -> <span class='return-annotation'>{return_annotation}</span>") | ||||||
|             f": -> <span class='return-annotation'>{return_annotation}</span>" |  | ||||||
|         ) |  | ||||||
|     parts.append("</span>") |     parts.append("</span>") | ||||||
|     return "".join(parts) |     return "".join(parts) | ||||||
|  |  | ||||||
| @@ -318,10 +305,7 @@ def _render_params(builder: Builder, params: list[DocstringParam]) -> None: | |||||||
|             builder.dd( |             builder.dd( | ||||||
|                 HTML( |                 HTML( | ||||||
|                     render_markdown( |                     render_markdown( | ||||||
|                         param.description |                         param.description or param.arg_name or param.type_name or "" | ||||||
|                         or param.arg_name |  | ||||||
|                         or param.type_name |  | ||||||
|                         or "" |  | ||||||
|                     ) |                     ) | ||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
| @@ -334,11 +318,7 @@ def _render_raises(builder: Builder, raises: list[DocstringRaises]) -> None: | |||||||
|             with builder.dl(class_="mt-2"): |             with builder.dl(class_="mt-2"): | ||||||
|                 builder.dt(raise_.type_name, class_="is-family-monospace") |                 builder.dt(raise_.type_name, class_="is-family-monospace") | ||||||
|                 builder.dd( |                 builder.dd( | ||||||
|                     HTML( |                     HTML(render_markdown(raise_.description or raise_.type_name or "")) | ||||||
|                         render_markdown( |  | ||||||
|                             raise_.description or raise_.type_name or "" |  | ||||||
|                         ) |  | ||||||
|                     ) |  | ||||||
|                 ) |                 ) | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -354,11 +334,7 @@ def _render_returns(builder: Builder, docobject: DocObject) -> None: | |||||||
|         if not return_type or return_type == inspect.Signature.empty: |         if not return_type or return_type == inspect.Signature.empty: | ||||||
|             return_type = "N/A" |             return_type = "N/A" | ||||||
|  |  | ||||||
|         term = ( |         term = "Return" if not docobject.docstring.returns.is_generator else "Yields" | ||||||
|             "Return" |  | ||||||
|             if not docobject.docstring.returns.is_generator |  | ||||||
|             else "Yields" |  | ||||||
|         ) |  | ||||||
|         builder.h5(term, class_="is-size-5 has-text-weight-bold") |         builder.h5(term, class_="is-size-5 has-text-weight-bold") | ||||||
|         with builder.dl(class_="mt-2"): |         with builder.dl(class_="mt-2"): | ||||||
|             builder.dt(return_type, class_="is-family-monospace") |             builder.dt(return_type, class_="is-family-monospace") | ||||||
| @@ -373,17 +349,11 @@ def _render_returns(builder: Builder, docobject: DocObject) -> None: | |||||||
|             ) |             ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def _render_examples( | def _render_examples(builder: Builder, examples: list[DocstringExample]) -> None: | ||||||
|     builder: Builder, examples: list[DocstringExample] |  | ||||||
| ) -> None: |  | ||||||
|     with builder.div(class_="box mt-5"): |     with builder.div(class_="box mt-5"): | ||||||
|         builder.h5("Examples", class_="is-size-5 has-text-weight-bold") |         builder.h5("Examples", class_="is-size-5 has-text-weight-bold") | ||||||
|         for example in examples: |         for example in examples: | ||||||
|             with builder.div(class_="mt-2"): |             with builder.div(class_="mt-2"): | ||||||
|                 builder( |                 builder( | ||||||
|                     HTML( |                     HTML(render_markdown(example.description or example.snippet or "")) | ||||||
|                         render_markdown( |  | ||||||
|                             example.description or example.snippet or "" |  | ||||||
|                         ) |  | ||||||
|                     ) |  | ||||||
|                 ) |                 ) | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ from __future__ import annotations | |||||||
|  |  | ||||||
| from dataclasses import dataclass, field | from dataclasses import dataclass, field | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from typing import Type |  | ||||||
|  |  | ||||||
| from frontmatter import parse | from frontmatter import parse | ||||||
|  |  | ||||||
| @@ -12,10 +11,8 @@ from ..layouts.main import MainLayout | |||||||
| from ..markdown import render_markdown | from ..markdown import render_markdown | ||||||
| from .docobject import organize_docobjects | from .docobject import organize_docobjects | ||||||
|  |  | ||||||
| _PAGE_CACHE: dict[ | _PAGE_CACHE: dict[str, dict[str, tuple[Page | None, Page | None, Page | None]]] = {} | ||||||
|     str, dict[str, tuple[Page | None, Page | None, Page | None]] | _LAYOUTS_CACHE: dict[str, type[BaseLayout]] = { | ||||||
| ] = {} |  | ||||||
| _LAYOUTS_CACHE: dict[str, Type[BaseLayout]] = { |  | ||||||
|     "home": HomeLayout, |     "home": HomeLayout, | ||||||
|     "main": MainLayout, |     "main": MainLayout, | ||||||
| } | } | ||||||
| @@ -43,7 +40,7 @@ class Page: | |||||||
|  |  | ||||||
|     DEFAULT_LANGUAGE = _DEFAULT |     DEFAULT_LANGUAGE = _DEFAULT | ||||||
|  |  | ||||||
|     def get_layout(self) -> Type[BaseLayout]: |     def get_layout(self) -> type[BaseLayout]: | ||||||
|         return _LAYOUTS_CACHE[self.meta.layout] |         return _LAYOUTS_CACHE[self.meta.layout] | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|   | |||||||
| @@ -1,13 +1,12 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| from contextlib import contextmanager | from contextlib import contextmanager | ||||||
| from typing import Type |  | ||||||
|  |  | ||||||
| from webapp.display.base import BaseRenderer |  | ||||||
|  |  | ||||||
| from html5tagger import HTML, Builder  # type: ignore | from html5tagger import HTML, Builder  # type: ignore | ||||||
| from sanic import Request | from sanic import Request | ||||||
|  |  | ||||||
|  | from webapp.display.base import BaseRenderer | ||||||
|  |  | ||||||
| from ..layouts.base import BaseLayout | from ..layouts.base import BaseLayout | ||||||
| from .page import Page | from .page import Page | ||||||
|  |  | ||||||
| @@ -21,13 +20,9 @@ class PageRenderer(BaseRenderer): | |||||||
|         self._body(request, builder, language, path) |         self._body(request, builder, language, path) | ||||||
|         return builder |         return builder | ||||||
|  |  | ||||||
|     def _body( |     def _body(self, request: Request, builder: Builder, language: str, path: str): | ||||||
|         self, request: Request, builder: Builder, language: str, path: str |  | ||||||
|     ): |  | ||||||
|         prev_page, current_page, next_page = Page.get(language, path) |         prev_page, current_page, next_page = Page.get(language, path) | ||||||
|         request.ctx.language = ( |         request.ctx.language = Page.DEFAULT_LANGUAGE if language == "api" else language | ||||||
|             Page.DEFAULT_LANGUAGE if language == "api" else language |  | ||||||
|         ) |  | ||||||
|         request.ctx.current_page = current_page |         request.ctx.current_page = current_page | ||||||
|         request.ctx.previous_page = prev_page |         request.ctx.previous_page = prev_page | ||||||
|         request.ctx.next_page = next_page |         request.ctx.next_page = next_page | ||||||
| @@ -39,9 +34,7 @@ class PageRenderer(BaseRenderer): | |||||||
|  |  | ||||||
|     @contextmanager |     @contextmanager | ||||||
|     def _base(self, request: Request, builder: Builder, page: Page | None): |     def _base(self, request: Request, builder: Builder, page: Page | None): | ||||||
|         layout_type: Type[BaseLayout] = ( |         layout_type: type[BaseLayout] = page.get_layout() if page else BaseLayout | ||||||
|             page.get_layout() if page else BaseLayout |  | ||||||
|         ) |  | ||||||
|         layout = layout_type(builder) |         layout = layout_type(builder) | ||||||
|         with layout(request, builder.full): |         with layout(request, builder.full): | ||||||
|             yield |             yield | ||||||
|   | |||||||
| @@ -2,12 +2,11 @@ from re import Match | |||||||
| from textwrap import dedent | from textwrap import dedent | ||||||
| from typing import Any | from typing import Any | ||||||
|  |  | ||||||
|  | from html5tagger import HTML, E | ||||||
| from mistune.block_parser import BlockParser | from mistune.block_parser import BlockParser | ||||||
| from mistune.core import BlockState | from mistune.core import BlockState | ||||||
| from mistune.directives import DirectivePlugin | from mistune.directives import DirectivePlugin | ||||||
|  |  | ||||||
| from html5tagger import HTML, E |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Attributes(DirectivePlugin): | class Attributes(DirectivePlugin): | ||||||
|     def __call__(self, directive, md): |     def __call__(self, directive, md): | ||||||
| @@ -16,9 +15,7 @@ class Attributes(DirectivePlugin): | |||||||
|         if md.renderer.NAME == "html": |         if md.renderer.NAME == "html": | ||||||
|             md.renderer.register("attrs", self._render) |             md.renderer.register("attrs", self._render) | ||||||
|  |  | ||||||
|     def parse( |     def parse(self, block: BlockParser, m: Match, state: BlockState) -> dict[str, Any]: | ||||||
|         self, block: BlockParser, m: Match, state: BlockState |  | ||||||
|     ) -> dict[str, Any]: |  | ||||||
|         info = m.groupdict() |         info = m.groupdict() | ||||||
|         options = dict(self.parse_options(m)) |         options = dict(self.parse_options(m)) | ||||||
|         new_state = block.state_cls() |         new_state = block.state_cls() | ||||||
|   | |||||||
| @@ -10,9 +10,7 @@ from mistune.markdown import Markdown | |||||||
|  |  | ||||||
|  |  | ||||||
| class Column(DirectivePlugin): | class Column(DirectivePlugin): | ||||||
|     def parse( |     def parse(self, block: BlockParser, m: Match, state: BlockState) -> dict[str, Any]: | ||||||
|         self, block: BlockParser, m: Match, state: BlockState |  | ||||||
|     ) -> dict[str, Any]: |  | ||||||
|         info = m.groupdict() |         info = m.groupdict() | ||||||
|  |  | ||||||
|         new_state = block.state_cls() |         new_state = block.state_cls() | ||||||
| @@ -36,9 +34,7 @@ class Column(DirectivePlugin): | |||||||
|  |  | ||||||
|     def _render_column(self, renderer: HTMLRenderer, text: str, **attrs): |     def _render_column(self, renderer: HTMLRenderer, text: str, **attrs): | ||||||
|         start = ( |         start = ( | ||||||
|             '<div class="columns mt-3 is-multiline">\n' |             '<div class="columns mt-3 is-multiline">\n' if attrs.get("first") else "" | ||||||
|             if attrs.get("first") |  | ||||||
|             else "" |  | ||||||
|         ) |         ) | ||||||
|         end = "</div>\n" if attrs.get("last") else "" |         end = "</div>\n" if attrs.get("last") else "" | ||||||
|         col = f'<div class="column is-half">{text}</div>\n' |         col = f'<div class="column is-half">{text}</div>\n' | ||||||
|   | |||||||
| @@ -16,16 +16,12 @@ class Hook(DirectivePlugin): | |||||||
|             for type_ in ("column", "tab"): |             for type_ in ("column", "tab"): | ||||||
|                 if token["type"] == type_: |                 if token["type"] == type_: | ||||||
|                     maybe_next = ( |                     maybe_next = ( | ||||||
|                         state.tokens[idx + 1] |                         state.tokens[idx + 1] if idx + 1 < len(state.tokens) else None | ||||||
|                         if idx + 1 < len(state.tokens) |  | ||||||
|                         else None |  | ||||||
|                     ) |                     ) | ||||||
|                     token.setdefault("attrs", {}) |                     token.setdefault("attrs", {}) | ||||||
|                     if prev and prev["type"] != type_: |                     if prev and prev["type"] != type_: | ||||||
|                         token["attrs"]["first"] = True |                         token["attrs"]["first"] = True | ||||||
|                     if ( |                     if (maybe_next and maybe_next["type"] != type_) or not maybe_next: | ||||||
|                         maybe_next and maybe_next["type"] != type_ |  | ||||||
|                     ) or not maybe_next: |  | ||||||
|                         token["attrs"]["last"] = True |                         token["attrs"]["last"] = True | ||||||
|  |  | ||||||
|             prev = token |             prev = token | ||||||
|   | |||||||
| @@ -3,19 +3,16 @@ from re import Match | |||||||
| from textwrap import dedent | from textwrap import dedent | ||||||
| from typing import Any | from typing import Any | ||||||
|  |  | ||||||
|  | from html5tagger import HTML, E | ||||||
| from mistune import HTMLRenderer | from mistune import HTMLRenderer | ||||||
| from mistune.block_parser import BlockParser | from mistune.block_parser import BlockParser | ||||||
| from mistune.core import BlockState | from mistune.core import BlockState | ||||||
| from mistune.directives import DirectivePlugin, RSTDirective | from mistune.directives import DirectivePlugin, RSTDirective | ||||||
| from mistune.markdown import Markdown | from mistune.markdown import Markdown | ||||||
|  |  | ||||||
| from html5tagger import HTML, E |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class Mermaid(DirectivePlugin): | class Mermaid(DirectivePlugin): | ||||||
|     def parse( |     def parse(self, block: BlockParser, m: Match, state: BlockState) -> dict[str, Any]: | ||||||
|         self, block: BlockParser, m: Match, state: BlockState |  | ||||||
|     ) -> dict[str, Any]: |  | ||||||
|         info = m.groupdict() |         info = m.groupdict() | ||||||
|  |  | ||||||
|         new_state = block.state_cls() |         new_state = block.state_cls() | ||||||
|   | |||||||
| @@ -1,6 +1,5 @@ | |||||||
| from mistune.directives import Admonition |  | ||||||
|  |  | ||||||
| from html5tagger import HTML, E | from html5tagger import HTML, E | ||||||
|  | from mistune.directives import Admonition | ||||||
|  |  | ||||||
|  |  | ||||||
| class Notification(Admonition): | class Notification(Admonition): | ||||||
| @@ -20,12 +19,8 @@ class Notification(Admonition): | |||||||
|  |  | ||||||
|         if md.renderer.NAME == "html": |         if md.renderer.NAME == "html": | ||||||
|             md.renderer.register("admonition", self._render_admonition) |             md.renderer.register("admonition", self._render_admonition) | ||||||
|             md.renderer.register( |             md.renderer.register("admonition_title", self._render_admonition_title) | ||||||
|                 "admonition_title", self._render_admonition_title |             md.renderer.register("admonition_content", self._render_admonition_content) | ||||||
|             ) |  | ||||||
|             md.renderer.register( |  | ||||||
|                 "admonition_content", self._render_admonition_content |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|     def _render_admonition(self, _, text, name, **attrs) -> str: |     def _render_admonition(self, _, text, name, **attrs) -> str: | ||||||
|         return str( |         return str( | ||||||
|   | |||||||
| @@ -10,9 +10,7 @@ from mistune.markdown import Markdown | |||||||
|  |  | ||||||
|  |  | ||||||
| class Tabs(DirectivePlugin): | class Tabs(DirectivePlugin): | ||||||
|     def parse( |     def parse(self, block: BlockParser, m: Match, state: BlockState) -> dict[str, Any]: | ||||||
|         self, block: BlockParser, m: Match, state: BlockState |  | ||||||
|     ) -> dict[str, Any]: |  | ||||||
|         info = m.groupdict() |         info = m.groupdict() | ||||||
|  |  | ||||||
|         new_state = block.state_cls() |         new_state = block.state_cls() | ||||||
| @@ -41,9 +39,7 @@ class Tabs(DirectivePlugin): | |||||||
|     def _render_tab(self, renderer: HTMLRenderer, text: str, **attrs): |     def _render_tab(self, renderer: HTMLRenderer, text: str, **attrs): | ||||||
|         start = '<div class="tabs mt-6"><ul>\n' if attrs.get("first") else "" |         start = '<div class="tabs mt-6"><ul>\n' if attrs.get("first") else "" | ||||||
|         end = ( |         end = ( | ||||||
|             '</ul></div><div class="tab-display"></div>\n' |             '</ul></div><div class="tab-display"></div>\n' if attrs.get("last") else "" | ||||||
|             if attrs.get("last") |  | ||||||
|             else "" |  | ||||||
|         ) |         ) | ||||||
|         content = f'<div class="tab-content">{text}</div>\n' |         content = f'<div class="tab-content">{text}</div>\n' | ||||||
|         tab = f'<li><a>{attrs["title"]}</a>{content}</li>\n' |         tab = f'<li><a>{attrs["title"]}</a>{content}</li>\n' | ||||||
|   | |||||||
| @@ -1,11 +1,11 @@ | |||||||
| from contextlib import contextmanager | from contextlib import contextmanager | ||||||
| from urllib.parse import unquote | from urllib.parse import unquote | ||||||
|  |  | ||||||
| from webapp.display.search.search import Searcher |  | ||||||
|  |  | ||||||
| from html5tagger import Builder, E  # type: ignore | from html5tagger import Builder, E  # type: ignore | ||||||
| from sanic import Request | from sanic import Request | ||||||
|  |  | ||||||
|  | from webapp.display.search.search import Searcher | ||||||
|  |  | ||||||
| from ..base import BaseRenderer | from ..base import BaseRenderer | ||||||
| from ..layouts.main import MainLayout | from ..layouts.main import MainLayout | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,6 +5,7 @@ from pathlib import Path | |||||||
| from typing import ClassVar | from typing import ClassVar | ||||||
|  |  | ||||||
| from msgspec import Struct | from msgspec import Struct | ||||||
|  |  | ||||||
| from webapp.display.page import Page | from webapp.display.page import Page | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -91,9 +92,7 @@ def _inverse_document_frequency(docs: list[Document]) -> dict[str, float]: | |||||||
|     return {word: num_docs / count for word, count in word_count.items()} |     return {word: num_docs / count for word, count in word_count.items()} | ||||||
|  |  | ||||||
|  |  | ||||||
| def _tf_idf_vector( | def _tf_idf_vector(document: Document, idf: dict[str, float]) -> dict[str, float]: | ||||||
|     document: Document, idf: dict[str, float] |  | ||||||
| ) -> dict[str, float]: |  | ||||||
|     """Calculate the TF-IDF vector for a document.""" |     """Calculate the TF-IDF vector for a document.""" | ||||||
|     return { |     return { | ||||||
|         word: tf * idf[word] |         word: tf * idf[word] | ||||||
| @@ -102,9 +101,7 @@ def _tf_idf_vector( | |||||||
|     } |     } | ||||||
|  |  | ||||||
|  |  | ||||||
| def _cosine_similarity( | def _cosine_similarity(vec1: dict[str, float], vec2: dict[str, float]) -> float: | ||||||
|     vec1: dict[str, float], vec2: dict[str, float] |  | ||||||
| ) -> float: |  | ||||||
|     """Calculate the cosine similarity between two vectors.""" |     """Calculate the cosine similarity between two vectors.""" | ||||||
|     if not vec1 or not vec2: |     if not vec1 or not vec2: | ||||||
|         return 0.0 |         return 0.0 | ||||||
| @@ -126,9 +123,7 @@ def _search( | |||||||
|     tf_idf_query = _tf_idf_vector( |     tf_idf_query = _tf_idf_vector( | ||||||
|         Document(page=dummy_page, language=language).process(stemmer), idf |         Document(page=dummy_page, language=language).process(stemmer), idf | ||||||
|     ) |     ) | ||||||
|     similarities = [ |     similarities = [_cosine_similarity(tf_idf_query, vector) for vector in vectors] | ||||||
|         _cosine_similarity(tf_idf_query, vector) for vector in vectors |  | ||||||
|     ] |  | ||||||
|     return [ |     return [ | ||||||
|         (similarity, document) |         (similarity, document) | ||||||
|         for similarity, document in sorted( |         for similarity, document in sorted( | ||||||
| @@ -155,16 +150,13 @@ class Searcher: | |||||||
|         } |         } | ||||||
|         self._vectors = { |         self._vectors = { | ||||||
|             language: [ |             language: [ | ||||||
|                 _tf_idf_vector(document, self._idf[language]) |                 _tf_idf_vector(document, self._idf[language]) for document in documents | ||||||
|                 for document in documents |  | ||||||
|             ] |             ] | ||||||
|             for language, documents in self._documents.items() |             for language, documents in self._documents.items() | ||||||
|         } |         } | ||||||
|         self._stemmer = stemmer |         self._stemmer = stemmer | ||||||
|  |  | ||||||
|     def search( |     def search(self, query: str, language: str) -> list[tuple[float, Document]]: | ||||||
|         self, query: str, language: str |  | ||||||
|     ) -> list[tuple[float, Document]]: |  | ||||||
|         return _search( |         return _search( | ||||||
|             query, |             query, | ||||||
|             language, |             language, | ||||||
|   | |||||||
| @@ -1,11 +1,11 @@ | |||||||
| # from urllib.parse import unquote | # from urllib.parse import unquote | ||||||
|  |  | ||||||
|  | from sanic import Blueprint, Request, Sanic, html | ||||||
|  |  | ||||||
| from webapp.display.page import Page | from webapp.display.page import Page | ||||||
| from webapp.display.search.renderer import SearchRenderer | from webapp.display.search.renderer import SearchRenderer | ||||||
| from webapp.display.search.search import Document, Searcher, Stemmer | from webapp.display.search.search import Document, Searcher, Stemmer | ||||||
|  |  | ||||||
| from sanic import Blueprint, Request, Sanic, html |  | ||||||
|  |  | ||||||
| bp = Blueprint("search", url_prefix="/<language>/search") | bp = Blueprint("search", url_prefix="/<language>/search") | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,6 +1,7 @@ | |||||||
| from pathlib import Path | from pathlib import Path | ||||||
|  |  | ||||||
| from msgspec import yaml | from msgspec import yaml | ||||||
|  |  | ||||||
| from webapp.display.layouts.models import GeneralConfig, MenuItem | from webapp.display.layouts.models import GeneralConfig, MenuItem | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,5 +1,7 @@ | |||||||
| from pathlib import Path | from pathlib import Path | ||||||
|  |  | ||||||
|  | from sanic import Request, Sanic, html, redirect | ||||||
|  |  | ||||||
| from webapp.display.layouts.models import MenuItem | from webapp.display.layouts.models import MenuItem | ||||||
| from webapp.display.page import Page, PageRenderer | from webapp.display.page import Page, PageRenderer | ||||||
| from webapp.endpoint.view import bp | from webapp.endpoint.view import bp | ||||||
| @@ -7,8 +9,6 @@ from webapp.worker.config import load_config, load_menu | |||||||
| from webapp.worker.reload import setup_livereload | from webapp.worker.reload import setup_livereload | ||||||
| from webapp.worker.style import setup_style | from webapp.worker.style import setup_style | ||||||
|  |  | ||||||
| from sanic import Request, Sanic, html, redirect |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def _compile_sidebar_order(items: list[MenuItem]) -> list[str]: | def _compile_sidebar_order(items: list[MenuItem]) -> list[str]: | ||||||
|     order = [] |     order = [] | ||||||
| @@ -28,13 +28,9 @@ def create_app(root: Path) -> Sanic: | |||||||
|     app.config.STYLE_DIR = root / "style" |     app.config.STYLE_DIR = root / "style" | ||||||
|     app.config.NODE_MODULES_DIR = root / "node_modules" |     app.config.NODE_MODULES_DIR = root / "node_modules" | ||||||
|     app.config.LANGUAGES = ["en"] |     app.config.LANGUAGES = ["en"] | ||||||
|     app.config.SIDEBAR = load_menu( |     app.config.SIDEBAR = load_menu(app.config.CONFIG_DIR / "en" / "sidebar.yaml") | ||||||
|         app.config.CONFIG_DIR / "en" / "sidebar.yaml" |  | ||||||
|     ) |  | ||||||
|     app.config.NAVBAR = load_menu(app.config.CONFIG_DIR / "en" / "navbar.yaml") |     app.config.NAVBAR = load_menu(app.config.CONFIG_DIR / "en" / "navbar.yaml") | ||||||
|     app.config.GENERAL = load_config( |     app.config.GENERAL = load_config(app.config.CONFIG_DIR / "en" / "general.yaml") | ||||||
|         app.config.CONFIG_DIR / "en" / "general.yaml" |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     setup_livereload(app) |     setup_livereload(app) | ||||||
|     setup_style(app) |     setup_style(app) | ||||||
| @@ -66,8 +62,6 @@ def create_app(root: Path) -> Sanic: | |||||||
|  |  | ||||||
|     @app.on_request |     @app.on_request | ||||||
|     async def set_language(request: Request): |     async def set_language(request: Request): | ||||||
|         request.ctx.language = request.match_info.get( |         request.ctx.language = request.match_info.get("language", Page.DEFAULT_LANGUAGE) | ||||||
|             "language", Page.DEFAULT_LANGUAGE |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|     return app |     return app | ||||||
|   | |||||||
| @@ -5,7 +5,6 @@ from queue import Empty, Queue | |||||||
| from typing import Any | from typing import Any | ||||||
|  |  | ||||||
| import ujson | import ujson | ||||||
|  |  | ||||||
| from sanic import Request, Sanic, Websocket | from sanic import Request, Sanic, Websocket | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -54,16 +53,12 @@ class Livereload: | |||||||
|         "serverName": SERVER_NAME, |         "serverName": SERVER_NAME, | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     def __init__( |     def __init__(self, reload_queue: Queue, debug: bool, state: dict[str, Any]): | ||||||
|         self, reload_queue: Queue, debug: bool, state: dict[str, Any] |  | ||||||
|     ): |  | ||||||
|         self.reload_queue = reload_queue |         self.reload_queue = reload_queue | ||||||
|         self.app = Sanic(self.SERVER_NAME) |         self.app = Sanic(self.SERVER_NAME) | ||||||
|         self.debug = debug |         self.debug = debug | ||||||
|         self.state = state |         self.state = state | ||||||
|         self.app.static( |         self.app.static("/livereload.js", Path(__file__).parent / "livereload.js") | ||||||
|             "/livereload.js", Path(__file__).parent / "livereload.js" |  | ||||||
|         ) |  | ||||||
|         self.app.add_websocket_route( |         self.app.add_websocket_route( | ||||||
|             self.livereload_handler, "/livereload", name="livereload" |             self.livereload_handler, "/livereload", name="livereload" | ||||||
|         ) |         ) | ||||||
| @@ -109,7 +104,5 @@ class Livereload: | |||||||
|                 break |                 break | ||||||
|  |  | ||||||
|  |  | ||||||
| def _run_reload_server( | def _run_reload_server(reload_queue: Queue, debug: bool, state: dict[str, Any]): | ||||||
|     reload_queue: Queue, debug: bool, state: dict[str, Any] |  | ||||||
| ): |  | ||||||
|     Livereload(reload_queue, debug, state).run() |     Livereload(reload_queue, debug, state).run() | ||||||
|   | |||||||
| @@ -1,10 +1,10 @@ | |||||||
| # from scss.compiler import compile_string | # from scss.compiler import compile_string | ||||||
|  |  | ||||||
| from pygments.formatters import html | from pygments.formatters import html | ||||||
| from sass import compile as compile_scss |  | ||||||
| from webapp.display.code_style import SanicCodeStyle |  | ||||||
|  |  | ||||||
| from sanic import Sanic | from sanic import Sanic | ||||||
|  | from sass import compile as compile_scss | ||||||
|  |  | ||||||
|  | from webapp.display.code_style import SanicCodeStyle | ||||||
|  |  | ||||||
|  |  | ||||||
| def setup_style(app: Sanic) -> None: | def setup_style(app: Sanic) -> None: | ||||||
|   | |||||||
| @@ -2,20 +2,28 @@ | |||||||
| requires = ["setuptools", "wheel"] | requires = ["setuptools", "wheel"] | ||||||
| build-backend = "setuptools.build_meta" | build-backend = "setuptools.build_meta" | ||||||
|  |  | ||||||
| [tool.black] | [tool.ruff] | ||||||
| line-length = 79 | extend-select = ["I", "W", "UP", "C4", "ISC"] | ||||||
|  | # Worth selecting but still too broken: ASYNC, S, B, DTZ, FA | ||||||
|  | ignore = [ | ||||||
|  | 	"D100", | ||||||
|  | 	"D101", | ||||||
|  | 	"D102", | ||||||
|  | 	"D103", | ||||||
|  | 	"E402", | ||||||
|  | 	"E741", | ||||||
|  | 	"F811", | ||||||
|  | 	"F821", | ||||||
|  | 	# ruff format complains about these: | ||||||
|  | 	"ISC001", | ||||||
|  | 	"W191", | ||||||
|  | ] | ||||||
|  | show-source = true | ||||||
|  | show-fixes = true | ||||||
|  |  | ||||||
| [tool.isort] | [tool.ruff.isort] | ||||||
| atomic = true | known-first-party = ["sanic"] | ||||||
| default_section = "THIRDPARTY" | known-third-party = ["pytest"] | ||||||
| include_trailing_comma = true |  | ||||||
| known_first_party = "sanic" |  | ||||||
| known_third_party = "pytest" |  | ||||||
| line_length = 79 |  | ||||||
| lines_after_imports = 2 |  | ||||||
| lines_between_types = 1 |  | ||||||
| multi_line_output = 3 |  | ||||||
| profile = "black" |  | ||||||
|  |  | ||||||
| [[tool.mypy.overrides]] | [[tool.mypy.overrides]] | ||||||
| module = [ | module = [ | ||||||
|   | |||||||
| @@ -36,7 +36,6 @@ from sanic.response import ( | |||||||
| ) | ) | ||||||
| from sanic.server.websockets.impl import WebsocketImplProtocol as Websocket | from sanic.server.websockets.impl import WebsocketImplProtocol as Websocket | ||||||
|  |  | ||||||
|  |  | ||||||
| DefaultSanic: TypeAlias = "Sanic[Config, SimpleNamespace]" | DefaultSanic: TypeAlias = "Sanic[Config, SimpleNamespace]" | ||||||
| """ | """ | ||||||
| A type alias for a Sanic app with a default config and namespace. | A type alias for a Sanic app with a default config and namespace. | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| from sanic.cli.app import SanicCLI | from sanic.cli.app import SanicCLI | ||||||
| from sanic.compat import OS_IS_WINDOWS, enable_windows_color_support | from sanic.compat import OS_IS_WINDOWS, enable_windows_color_support | ||||||
|  |  | ||||||
|  |  | ||||||
| if OS_IS_WINDOWS: | if OS_IS_WINDOWS: | ||||||
|     enable_windows_color_support() |     enable_windows_color_support() | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										375
									
								
								sanic/app.py
									
									
									
									
									
								
							
							
						
						
									
										375
									
								
								sanic/app.py
									
									
									
									
									
								
							| @@ -5,7 +5,6 @@ import logging | |||||||
| import logging.config | import logging.config | ||||||
| import re | import re | ||||||
| import sys | import sys | ||||||
|  |  | ||||||
| from asyncio import ( | from asyncio import ( | ||||||
|     AbstractEventLoop, |     AbstractEventLoop, | ||||||
|     CancelledError, |     CancelledError, | ||||||
| @@ -32,19 +31,12 @@ from typing import ( | |||||||
|     Callable, |     Callable, | ||||||
|     ClassVar, |     ClassVar, | ||||||
|     Coroutine, |     Coroutine, | ||||||
|     Deque, |  | ||||||
|     Dict, |  | ||||||
|     Generic, |     Generic, | ||||||
|     Iterable, |     Iterable, | ||||||
|     Iterator, |     Iterator, | ||||||
|     List, |  | ||||||
|     Literal, |     Literal, | ||||||
|     Optional, |  | ||||||
|     Set, |  | ||||||
|     Tuple, |  | ||||||
|     Type, |     Type, | ||||||
|     TypeVar, |     TypeVar, | ||||||
|     Union, |  | ||||||
|     cast, |     cast, | ||||||
|     overload, |     overload, | ||||||
| ) | ) | ||||||
| @@ -96,7 +88,6 @@ from sanic.worker.inspector import Inspector | |||||||
| from sanic.worker.loader import CertLoader | from sanic.worker.loader import CertLoader | ||||||
| from sanic.worker.manager import WorkerManager | from sanic.worker.manager import WorkerManager | ||||||
|  |  | ||||||
|  |  | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     try: |     try: | ||||||
|         from sanic_ext import Extend  # type: ignore |         from sanic_ext import Extend  # type: ignore | ||||||
| @@ -173,7 +164,7 @@ class Sanic( | |||||||
|         "websocket_tasks", |         "websocket_tasks", | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     _app_registry: ClassVar[Dict[str, "Sanic"]] = {} |     _app_registry: ClassVar[dict[str, Sanic]] = {} | ||||||
|     test_mode: ClassVar[bool] = False |     test_mode: ClassVar[bool] = False | ||||||
|  |  | ||||||
|     @overload |     @overload | ||||||
| @@ -182,19 +173,19 @@ class Sanic( | |||||||
|         name: str, |         name: str, | ||||||
|         config: None = None, |         config: None = None, | ||||||
|         ctx: None = None, |         ctx: None = None, | ||||||
|         router: Optional[Router] = None, |         router: Router | None = None, | ||||||
|         signal_router: Optional[SignalRouter] = None, |         signal_router: SignalRouter | None = None, | ||||||
|         error_handler: Optional[ErrorHandler] = None, |         error_handler: ErrorHandler | None = None, | ||||||
|         env_prefix: Optional[str] = SANIC_PREFIX, |         env_prefix: str | None = SANIC_PREFIX, | ||||||
|         request_class: Optional[Type[Request]] = None, |         request_class: type[Request] | None = None, | ||||||
|         strict_slashes: bool = False, |         strict_slashes: bool = False, | ||||||
|         log_config: Optional[Dict[str, Any]] = None, |         log_config: dict[str, Any] | None = None, | ||||||
|         configure_logging: bool = True, |         configure_logging: bool = True, | ||||||
|         dumps: Optional[Callable[..., AnyStr]] = None, |         dumps: Callable[..., AnyStr] | None = None, | ||||||
|         loads: Optional[Callable[..., Any]] = None, |         loads: Callable[..., Any] | None = None, | ||||||
|         inspector: bool = False, |         inspector: bool = False, | ||||||
|         inspector_class: Optional[Type[Inspector]] = None, |         inspector_class: type[Inspector] | None = None, | ||||||
|         certloader_class: Optional[Type[CertLoader]] = None, |         certloader_class: type[CertLoader] | None = None, | ||||||
|     ) -> None: |     ) -> None: | ||||||
|         ... |         ... | ||||||
|  |  | ||||||
| @@ -202,21 +193,21 @@ class Sanic( | |||||||
|     def __init__( |     def __init__( | ||||||
|         self: Sanic[config_type, SimpleNamespace], |         self: Sanic[config_type, SimpleNamespace], | ||||||
|         name: str, |         name: str, | ||||||
|         config: Optional[config_type] = None, |         config: config_type | None = None, | ||||||
|         ctx: None = None, |         ctx: None = None, | ||||||
|         router: Optional[Router] = None, |         router: Router | None = None, | ||||||
|         signal_router: Optional[SignalRouter] = None, |         signal_router: SignalRouter | None = None, | ||||||
|         error_handler: Optional[ErrorHandler] = None, |         error_handler: ErrorHandler | None = None, | ||||||
|         env_prefix: Optional[str] = SANIC_PREFIX, |         env_prefix: str | None = SANIC_PREFIX, | ||||||
|         request_class: Optional[Type[Request]] = None, |         request_class: type[Request] | None = None, | ||||||
|         strict_slashes: bool = False, |         strict_slashes: bool = False, | ||||||
|         log_config: Optional[Dict[str, Any]] = None, |         log_config: dict[str, Any] | None = None, | ||||||
|         configure_logging: bool = True, |         configure_logging: bool = True, | ||||||
|         dumps: Optional[Callable[..., AnyStr]] = None, |         dumps: Callable[..., AnyStr] | None = None, | ||||||
|         loads: Optional[Callable[..., Any]] = None, |         loads: Callable[..., Any] | None = None, | ||||||
|         inspector: bool = False, |         inspector: bool = False, | ||||||
|         inspector_class: Optional[Type[Inspector]] = None, |         inspector_class: type[Inspector] | None = None, | ||||||
|         certloader_class: Optional[Type[CertLoader]] = None, |         certloader_class: type[CertLoader] | None = None, | ||||||
|     ) -> None: |     ) -> None: | ||||||
|         ... |         ... | ||||||
|  |  | ||||||
| @@ -225,20 +216,20 @@ class Sanic( | |||||||
|         self: Sanic[Config, ctx_type], |         self: Sanic[Config, ctx_type], | ||||||
|         name: str, |         name: str, | ||||||
|         config: None = None, |         config: None = None, | ||||||
|         ctx: Optional[ctx_type] = None, |         ctx: ctx_type | None = None, | ||||||
|         router: Optional[Router] = None, |         router: Router | None = None, | ||||||
|         signal_router: Optional[SignalRouter] = None, |         signal_router: SignalRouter | None = None, | ||||||
|         error_handler: Optional[ErrorHandler] = None, |         error_handler: ErrorHandler | None = None, | ||||||
|         env_prefix: Optional[str] = SANIC_PREFIX, |         env_prefix: str | None = SANIC_PREFIX, | ||||||
|         request_class: Optional[Type[Request]] = None, |         request_class: type[Request] | None = None, | ||||||
|         strict_slashes: bool = False, |         strict_slashes: bool = False, | ||||||
|         log_config: Optional[Dict[str, Any]] = None, |         log_config: dict[str, Any] | None = None, | ||||||
|         configure_logging: bool = True, |         configure_logging: bool = True, | ||||||
|         dumps: Optional[Callable[..., AnyStr]] = None, |         dumps: Callable[..., AnyStr] | None = None, | ||||||
|         loads: Optional[Callable[..., Any]] = None, |         loads: Callable[..., Any] | None = None, | ||||||
|         inspector: bool = False, |         inspector: bool = False, | ||||||
|         inspector_class: Optional[Type[Inspector]] = None, |         inspector_class: type[Inspector] | None = None, | ||||||
|         certloader_class: Optional[Type[CertLoader]] = None, |         certloader_class: type[CertLoader] | None = None, | ||||||
|     ) -> None: |     ) -> None: | ||||||
|         ... |         ... | ||||||
|  |  | ||||||
| @@ -246,42 +237,42 @@ class Sanic( | |||||||
|     def __init__( |     def __init__( | ||||||
|         self: Sanic[config_type, ctx_type], |         self: Sanic[config_type, ctx_type], | ||||||
|         name: str, |         name: str, | ||||||
|         config: Optional[config_type] = None, |         config: config_type | None = None, | ||||||
|         ctx: Optional[ctx_type] = None, |         ctx: ctx_type | None = None, | ||||||
|         router: Optional[Router] = None, |         router: Router | None = None, | ||||||
|         signal_router: Optional[SignalRouter] = None, |         signal_router: SignalRouter | None = None, | ||||||
|         error_handler: Optional[ErrorHandler] = None, |         error_handler: ErrorHandler | None = None, | ||||||
|         env_prefix: Optional[str] = SANIC_PREFIX, |         env_prefix: str | None = SANIC_PREFIX, | ||||||
|         request_class: Optional[Type[Request]] = None, |         request_class: type[Request] | None = None, | ||||||
|         strict_slashes: bool = False, |         strict_slashes: bool = False, | ||||||
|         log_config: Optional[Dict[str, Any]] = None, |         log_config: dict[str, Any] | None = None, | ||||||
|         configure_logging: bool = True, |         configure_logging: bool = True, | ||||||
|         dumps: Optional[Callable[..., AnyStr]] = None, |         dumps: Callable[..., AnyStr] | None = None, | ||||||
|         loads: Optional[Callable[..., Any]] = None, |         loads: Callable[..., Any] | None = None, | ||||||
|         inspector: bool = False, |         inspector: bool = False, | ||||||
|         inspector_class: Optional[Type[Inspector]] = None, |         inspector_class: type[Inspector] | None = None, | ||||||
|         certloader_class: Optional[Type[CertLoader]] = None, |         certloader_class: type[CertLoader] | None = None, | ||||||
|     ) -> None: |     ) -> None: | ||||||
|         ... |         ... | ||||||
|  |  | ||||||
|     def __init__( |     def __init__( | ||||||
|         self, |         self, | ||||||
|         name: str, |         name: str, | ||||||
|         config: Optional[config_type] = None, |         config: config_type | None = None, | ||||||
|         ctx: Optional[ctx_type] = None, |         ctx: ctx_type | None = None, | ||||||
|         router: Optional[Router] = None, |         router: Router | None = None, | ||||||
|         signal_router: Optional[SignalRouter] = None, |         signal_router: SignalRouter | None = None, | ||||||
|         error_handler: Optional[ErrorHandler] = None, |         error_handler: ErrorHandler | None = None, | ||||||
|         env_prefix: Optional[str] = SANIC_PREFIX, |         env_prefix: str | None = SANIC_PREFIX, | ||||||
|         request_class: Optional[Type[Request]] = None, |         request_class: type[Request] | None = None, | ||||||
|         strict_slashes: bool = False, |         strict_slashes: bool = False, | ||||||
|         log_config: Optional[Dict[str, Any]] = None, |         log_config: dict[str, Any] | None = None, | ||||||
|         configure_logging: bool = True, |         configure_logging: bool = True, | ||||||
|         dumps: Optional[Callable[..., AnyStr]] = None, |         dumps: Callable[..., AnyStr] | None = None, | ||||||
|         loads: Optional[Callable[..., Any]] = None, |         loads: Callable[..., Any] | None = None, | ||||||
|         inspector: bool = False, |         inspector: bool = False, | ||||||
|         inspector_class: Optional[Type[Inspector]] = None, |         inspector_class: type[Inspector] | None = None, | ||||||
|         certloader_class: Optional[Type[CertLoader]] = None, |         certloader_class: type[CertLoader] | None = None, | ||||||
|     ) -> None: |     ) -> None: | ||||||
|         super().__init__(name=name) |         super().__init__(name=name) | ||||||
|         # logging |         # logging | ||||||
| @@ -303,41 +294,39 @@ class Sanic( | |||||||
|             self.config.INSPECTOR = inspector |             self.config.INSPECTOR = inspector | ||||||
|  |  | ||||||
|         # Then we can do the rest |         # Then we can do the rest | ||||||
|         self._asgi_app: Optional[ASGIApp] = None |         self._asgi_app: ASGIApp | None = None | ||||||
|         self._asgi_lifespan: Optional[Lifespan] = None |         self._asgi_lifespan: Lifespan | None = None | ||||||
|         self._asgi_client: Any = None |         self._asgi_client: Any = None | ||||||
|         self._blueprint_order: List[Blueprint] = [] |         self._blueprint_order: list[Blueprint] = [] | ||||||
|         self._delayed_tasks: List[str] = [] |         self._delayed_tasks: list[str] = [] | ||||||
|         self._future_registry: FutureRegistry = FutureRegistry() |         self._future_registry: FutureRegistry = FutureRegistry() | ||||||
|         self._inspector: Optional[Inspector] = None |         self._inspector: Inspector | None = None | ||||||
|         self._manager: Optional[WorkerManager] = None |         self._manager: WorkerManager | None = None | ||||||
|         self._state: ApplicationState = ApplicationState(app=self) |         self._state: ApplicationState = ApplicationState(app=self) | ||||||
|         self._task_registry: Dict[str, Union[Task, None]] = {} |         self._task_registry: dict[str, Task | None] = {} | ||||||
|         self._test_client: Any = None |         self._test_client: Any = None | ||||||
|         self._test_manager: Any = None |         self._test_manager: Any = None | ||||||
|         self.asgi = False |         self.asgi = False | ||||||
|         self.auto_reload = False |         self.auto_reload = False | ||||||
|         self.blueprints: Dict[str, Blueprint] = {} |         self.blueprints: dict[str, Blueprint] = {} | ||||||
|         self.certloader_class: Type[CertLoader] = ( |         self.certloader_class: type[CertLoader] = certloader_class or CertLoader | ||||||
|             certloader_class or CertLoader |  | ||||||
|         ) |  | ||||||
|         self.configure_logging: bool = configure_logging |         self.configure_logging: bool = configure_logging | ||||||
|         self.ctx: ctx_type = cast(ctx_type, ctx or SimpleNamespace()) |         self.ctx: ctx_type = cast(ctx_type, ctx or SimpleNamespace()) | ||||||
|         self.error_handler: ErrorHandler = error_handler or ErrorHandler() |         self.error_handler: ErrorHandler = error_handler or ErrorHandler() | ||||||
|         self.inspector_class: Type[Inspector] = inspector_class or Inspector |         self.inspector_class: type[Inspector] = inspector_class or Inspector | ||||||
|         self.listeners: Dict[str, List[ListenerType[Any]]] = defaultdict(list) |         self.listeners: dict[str, list[ListenerType[Any]]] = defaultdict(list) | ||||||
|         self.named_request_middleware: Dict[str, Deque[Middleware]] = {} |         self.named_request_middleware: dict[str, deque[Middleware]] = {} | ||||||
|         self.named_response_middleware: Dict[str, Deque[Middleware]] = {} |         self.named_response_middleware: dict[str, deque[Middleware]] = {} | ||||||
|         self.request_class: Type[Request] = request_class or Request |         self.request_class: type[Request] = request_class or Request | ||||||
|         self.request_middleware: Deque[Middleware] = deque() |         self.request_middleware: deque[Middleware] = deque() | ||||||
|         self.response_middleware: Deque[Middleware] = deque() |         self.response_middleware: deque[Middleware] = deque() | ||||||
|         self.router: Router = router or Router() |         self.router: Router = router or Router() | ||||||
|         self.shared_ctx: SharedContext = SharedContext() |         self.shared_ctx: SharedContext = SharedContext() | ||||||
|         self.signal_router: SignalRouter = signal_router or SignalRouter() |         self.signal_router: SignalRouter = signal_router or SignalRouter() | ||||||
|         self.sock: Optional[socket] = None |         self.sock: socket | None = None | ||||||
|         self.strict_slashes: bool = strict_slashes |         self.strict_slashes: bool = strict_slashes | ||||||
|         self.websocket_enabled: bool = False |         self.websocket_enabled: bool = False | ||||||
|         self.websocket_tasks: Set[Future[Any]] = set() |         self.websocket_tasks: set[Future[Any]] = set() | ||||||
|  |  | ||||||
|         # Register alternative method names |         # Register alternative method names | ||||||
|         self.go_fast = self.run |         self.go_fast = self.run | ||||||
| @@ -396,15 +385,11 @@ class Sanic( | |||||||
|         try: |         try: | ||||||
|             _event = ListenerEvent[event.upper()] |             _event = ListenerEvent[event.upper()] | ||||||
|         except (ValueError, AttributeError): |         except (ValueError, AttributeError): | ||||||
|             valid = ", ".join( |             valid = ", ".join(x.lower() for x in ListenerEvent.__members__.keys()) | ||||||
|                 map(lambda x: x.lower(), ListenerEvent.__members__.keys()) |  | ||||||
|             ) |  | ||||||
|             raise BadRequest(f"Invalid event: {event}. Use one of: {valid}") |             raise BadRequest(f"Invalid event: {event}. Use one of: {valid}") | ||||||
|  |  | ||||||
|         if "." in _event: |         if "." in _event: | ||||||
|             self.signal(_event.value)( |             self.signal(_event.value)(partial(self._listener, listener=listener)) | ||||||
|                 partial(self._listener, listener=listener) |  | ||||||
|             ) |  | ||||||
|         else: |         else: | ||||||
|             self.listeners[_event.value].append(listener) |             self.listeners[_event.value].append(listener) | ||||||
|  |  | ||||||
| @@ -412,11 +397,11 @@ class Sanic( | |||||||
|  |  | ||||||
|     def register_middleware( |     def register_middleware( | ||||||
|         self, |         self, | ||||||
|         middleware: Union[MiddlewareType, Middleware], |         middleware: MiddlewareType | Middleware, | ||||||
|         attach_to: str = "request", |         attach_to: str = "request", | ||||||
|         *, |         *, | ||||||
|         priority: Union[Default, int] = _default, |         priority: Default | int = _default, | ||||||
|     ) -> Union[MiddlewareType, Middleware]: |     ) -> MiddlewareType | Middleware: | ||||||
|         """Register a middleware to be called before a request is handled. |         """Register a middleware to be called before a request is handled. | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
| @@ -461,7 +446,7 @@ class Sanic( | |||||||
|         route_names: Iterable[str], |         route_names: Iterable[str], | ||||||
|         attach_to: str = "request", |         attach_to: str = "request", | ||||||
|         *, |         *, | ||||||
|         priority: Union[Default, int] = _default, |         priority: Default | int = _default, | ||||||
|     ): |     ): | ||||||
|         """Used to register named middleqare (middleware typically on blueprints) |         """Used to register named middleqare (middleware typically on blueprints) | ||||||
|  |  | ||||||
| @@ -512,7 +497,7 @@ class Sanic( | |||||||
|     def _apply_exception_handler( |     def _apply_exception_handler( | ||||||
|         self, |         self, | ||||||
|         handler: FutureException, |         handler: FutureException, | ||||||
|         route_names: Optional[List[str]] = None, |         route_names: list[str] | None = None, | ||||||
|     ): |     ): | ||||||
|         """Decorate a function to be registered as a handler for exceptions |         """Decorate a function to be registered as a handler for exceptions | ||||||
|  |  | ||||||
| @@ -531,9 +516,7 @@ class Sanic( | |||||||
|     def _apply_listener(self, listener: FutureListener): |     def _apply_listener(self, listener: FutureListener): | ||||||
|         return self.register_listener(listener.listener, listener.event) |         return self.register_listener(listener.listener, listener.event) | ||||||
|  |  | ||||||
|     def _apply_route( |     def _apply_route(self, route: FutureRoute, overwrite: bool = False) -> list[Route]: | ||||||
|         self, route: FutureRoute, overwrite: bool = False |  | ||||||
|     ) -> List[Route]: |  | ||||||
|         params = route._asdict() |         params = route._asdict() | ||||||
|         params["overwrite"] = overwrite |         params["overwrite"] = overwrite | ||||||
|         websocket = params.pop("websocket", False) |         websocket = params.pop("websocket", False) | ||||||
| @@ -567,7 +550,7 @@ class Sanic( | |||||||
|     def _apply_middleware( |     def _apply_middleware( | ||||||
|         self, |         self, | ||||||
|         middleware: FutureMiddleware, |         middleware: FutureMiddleware, | ||||||
|         route_names: Optional[List[str]] = None, |         route_names: list[str] | None = None, | ||||||
|     ): |     ): | ||||||
|         with self.amend(): |         with self.amend(): | ||||||
|             if route_names: |             if route_names: | ||||||
| @@ -588,8 +571,8 @@ class Sanic( | |||||||
|         self, |         self, | ||||||
|         event: str, |         event: str, | ||||||
|         *, |         *, | ||||||
|         condition: Optional[Dict[str, str]] = None, |         condition: dict[str, str] | None = None, | ||||||
|         context: Optional[Dict[str, Any]] = None, |         context: dict[str, Any] | None = None, | ||||||
|         fail_not_found: bool = True, |         fail_not_found: bool = True, | ||||||
|         inline: Literal[True], |         inline: Literal[True], | ||||||
|         reverse: bool = False, |         reverse: bool = False, | ||||||
| @@ -601,8 +584,8 @@ class Sanic( | |||||||
|         self, |         self, | ||||||
|         event: str, |         event: str, | ||||||
|         *, |         *, | ||||||
|         condition: Optional[Dict[str, str]] = None, |         condition: dict[str, str] | None = None, | ||||||
|         context: Optional[Dict[str, Any]] = None, |         context: dict[str, Any] | None = None, | ||||||
|         fail_not_found: bool = True, |         fail_not_found: bool = True, | ||||||
|         inline: Literal[False] = False, |         inline: Literal[False] = False, | ||||||
|         reverse: bool = False, |         reverse: bool = False, | ||||||
| @@ -613,12 +596,12 @@ class Sanic( | |||||||
|         self, |         self, | ||||||
|         event: str, |         event: str, | ||||||
|         *, |         *, | ||||||
|         condition: Optional[Dict[str, str]] = None, |         condition: dict[str, str] | None = None, | ||||||
|         context: Optional[Dict[str, Any]] = None, |         context: dict[str, Any] | None = None, | ||||||
|         fail_not_found: bool = True, |         fail_not_found: bool = True, | ||||||
|         inline: bool = False, |         inline: bool = False, | ||||||
|         reverse: bool = False, |         reverse: bool = False, | ||||||
|     ) -> Coroutine[Any, Any, Awaitable[Union[Task, Any]]]: |     ) -> Coroutine[Any, Any, Awaitable[Task | Any]]: | ||||||
|         """Dispatches an event to the signal router. |         """Dispatches an event to the signal router. | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
| @@ -662,9 +645,7 @@ class Sanic( | |||||||
|             fail_not_found=fail_not_found, |             fail_not_found=fail_not_found, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     async def event( |     async def event(self, event: str, timeout: int | float | None = None) -> None: | ||||||
|         self, event: str, timeout: Optional[Union[int, float]] = None |  | ||||||
|     ) -> None: |  | ||||||
|         """Wait for a specific event to be triggered. |         """Wait for a specific event to be triggered. | ||||||
|  |  | ||||||
|         This method waits for a named event to be triggered and can be used |         This method waits for a named event to be triggered and can be used | ||||||
| @@ -749,9 +730,7 @@ class Sanic( | |||||||
|         async def report(exception: Exception) -> None: |         async def report(exception: Exception) -> None: | ||||||
|             await handler(self, exception) |             await handler(self, exception) | ||||||
|  |  | ||||||
|         self.add_signal( |         self.add_signal(handler=report, event=Event.SERVER_EXCEPTION_REPORT.value) | ||||||
|             handler=report, event=Event.SERVER_EXCEPTION_REPORT.value |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         return report |         return report | ||||||
|  |  | ||||||
| @@ -780,13 +759,13 @@ class Sanic( | |||||||
|  |  | ||||||
|     def blueprint( |     def blueprint( | ||||||
|         self, |         self, | ||||||
|         blueprint: Union[Blueprint, Iterable[Blueprint], BlueprintGroup], |         blueprint: Blueprint | (Iterable[Blueprint] | BlueprintGroup), | ||||||
|         *, |         *, | ||||||
|         url_prefix: Optional[str] = None, |         url_prefix: str | None = None, | ||||||
|         version: Optional[Union[int, float, str]] = None, |         version: int | float | str | None = None, | ||||||
|         strict_slashes: Optional[bool] = None, |         strict_slashes: bool | None = None, | ||||||
|         version_prefix: Optional[str] = None, |         version_prefix: str | None = None, | ||||||
|         name_prefix: Optional[str] = None, |         name_prefix: str | None = None, | ||||||
|     ) -> None: |     ) -> None: | ||||||
|         """Register a blueprint on the application. |         """Register a blueprint on the application. | ||||||
|  |  | ||||||
| @@ -812,7 +791,7 @@ class Sanic( | |||||||
|             app.blueprint(bp, url_prefix='/blueprint') |             app.blueprint(bp, url_prefix='/blueprint') | ||||||
|             ``` |             ``` | ||||||
|         """  # noqa: E501 |         """  # noqa: E501 | ||||||
|         options: Dict[str, Any] = {} |         options: dict[str, Any] = {} | ||||||
|         if url_prefix is not None: |         if url_prefix is not None: | ||||||
|             options["url_prefix"] = url_prefix |             options["url_prefix"] = url_prefix | ||||||
|         if version is not None: |         if version is not None: | ||||||
| @@ -825,7 +804,7 @@ class Sanic( | |||||||
|             options["name_prefix"] = name_prefix |             options["name_prefix"] = name_prefix | ||||||
|         if isinstance(blueprint, (Iterable, BlueprintGroup)): |         if isinstance(blueprint, (Iterable, BlueprintGroup)): | ||||||
|             for item in blueprint: |             for item in blueprint: | ||||||
|                 params: Dict[str, Any] = {**options} |                 params: dict[str, Any] = {**options} | ||||||
|                 if isinstance(blueprint, BlueprintGroup): |                 if isinstance(blueprint, BlueprintGroup): | ||||||
|                     merge_from = [ |                     merge_from = [ | ||||||
|                         options.get("url_prefix", ""), |                         options.get("url_prefix", ""), | ||||||
| @@ -840,14 +819,12 @@ class Sanic( | |||||||
|  |  | ||||||
|                     for _attr in ["version", "strict_slashes"]: |                     for _attr in ["version", "strict_slashes"]: | ||||||
|                         if getattr(item, _attr) is None: |                         if getattr(item, _attr) is None: | ||||||
|                             params[_attr] = getattr( |                             params[_attr] = getattr(blueprint, _attr) or options.get( | ||||||
|                                 blueprint, _attr |                                 _attr | ||||||
|                             ) or options.get(_attr) |                             ) | ||||||
|                     if item.version_prefix == "/v": |                     if item.version_prefix == "/v": | ||||||
|                         if blueprint.version_prefix == "/v": |                         if blueprint.version_prefix == "/v": | ||||||
|                             params["version_prefix"] = options.get( |                             params["version_prefix"] = options.get("version_prefix") | ||||||
|                                 "version_prefix" |  | ||||||
|                             ) |  | ||||||
|                         else: |                         else: | ||||||
|                             params["version_prefix"] = blueprint.version_prefix |                             params["version_prefix"] = blueprint.version_prefix | ||||||
|                     name_prefix = getattr(blueprint, "name_prefix", None) |                     name_prefix = getattr(blueprint, "name_prefix", None) | ||||||
| @@ -857,17 +834,14 @@ class Sanic( | |||||||
|             return |             return | ||||||
|         if blueprint.name in self.blueprints: |         if blueprint.name in self.blueprints: | ||||||
|             assert self.blueprints[blueprint.name] is blueprint, ( |             assert self.blueprints[blueprint.name] is blueprint, ( | ||||||
|                 'A blueprint with the name "%s" is already registered.  ' |                 f'A blueprint with the name "{blueprint.name}" is already registered.  ' | ||||||
|                 "Blueprint names must be unique." % (blueprint.name,) |                 "Blueprint names must be unique." | ||||||
|             ) |             ) | ||||||
|         else: |         else: | ||||||
|             self.blueprints[blueprint.name] = blueprint |             self.blueprints[blueprint.name] = blueprint | ||||||
|             self._blueprint_order.append(blueprint) |             self._blueprint_order.append(blueprint) | ||||||
|  |  | ||||||
|         if ( |         if self.strict_slashes is not None and blueprint.strict_slashes is None: | ||||||
|             self.strict_slashes is not None |  | ||||||
|             and blueprint.strict_slashes is None |  | ||||||
|         ): |  | ||||||
|             blueprint.strict_slashes = self.strict_slashes |             blueprint.strict_slashes = self.strict_slashes | ||||||
|         blueprint.register(self, options) |         blueprint.register(self, options) | ||||||
|  |  | ||||||
| @@ -923,7 +897,7 @@ class Sanic( | |||||||
|             # http://subdomain.example.com/view-name |             # http://subdomain.example.com/view-name | ||||||
|         """  # noqa: E501 |         """  # noqa: E501 | ||||||
|         # find the route by the supplied view name |         # find the route by the supplied view name | ||||||
|         kw: Dict[str, str] = {} |         kw: dict[str, str] = {} | ||||||
|         # special static files url_for |         # special static files url_for | ||||||
|  |  | ||||||
|         if "." not in view_name: |         if "." not in view_name: | ||||||
| @@ -937,9 +911,7 @@ class Sanic( | |||||||
|  |  | ||||||
|         route = self.router.find_route_by_view_name(view_name, **kw) |         route = self.router.find_route_by_view_name(view_name, **kw) | ||||||
|         if not route: |         if not route: | ||||||
|             raise URLBuildError( |             raise URLBuildError(f"Endpoint with name `{view_name}` was not found") | ||||||
|                 f"Endpoint with name `{view_name}` was not found" |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|         uri = route.path |         uri = route.path | ||||||
|  |  | ||||||
| @@ -978,9 +950,7 @@ class Sanic( | |||||||
|         scheme = kwargs.pop("_scheme", "") |         scheme = kwargs.pop("_scheme", "") | ||||||
|         if route.extra.hosts and external: |         if route.extra.hosts and external: | ||||||
|             if not host and len(route.extra.hosts) > 1: |             if not host and len(route.extra.hosts) > 1: | ||||||
|                 raise ValueError( |                 raise ValueError(f"Host is ambiguous: {', '.join(route.extra.hosts)}") | ||||||
|                     f"Host is ambiguous: {', '.join(route.extra.hosts)}" |  | ||||||
|                 ) |  | ||||||
|             elif host and host not in route.extra.hosts: |             elif host and host not in route.extra.hosts: | ||||||
|                 raise ValueError( |                 raise ValueError( | ||||||
|                     f"Requested host ({host}) is not available for this " |                     f"Requested host ({host}) is not available for this " | ||||||
| @@ -1096,10 +1066,7 @@ class Sanic( | |||||||
|             context={"request": request, "exception": exception}, |             context={"request": request, "exception": exception}, | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|         if ( |         if request.stream is not None and request.stream.stage is not Stage.HANDLER: | ||||||
|             request.stream is not None |  | ||||||
|             and request.stream.stage is not Stage.HANDLER |  | ||||||
|         ): |  | ||||||
|             error_logger.exception(exception, exc_info=True) |             error_logger.exception(exception, exc_info=True) | ||||||
|             logger.error( |             logger.error( | ||||||
|                 "The error response will not be sent to the client for " |                 "The error response will not be sent to the client for " | ||||||
| @@ -1146,10 +1113,7 @@ class Sanic( | |||||||
|                     response = self.error_handler.default(request, e) |                     response = self.error_handler.default(request, e) | ||||||
|                 elif self.debug: |                 elif self.debug: | ||||||
|                     response = HTTPResponse( |                     response = HTTPResponse( | ||||||
|                         ( |                         (f"Error while handling error: {e}\n" f"Stack: {format_exc()}"), | ||||||
|                             f"Error while handling error: {e}\n" |  | ||||||
|                             f"Stack: {format_exc()}" |  | ||||||
|                         ), |  | ||||||
|                         status=500, |                         status=500, | ||||||
|                     ) |                     ) | ||||||
|                 else: |                 else: | ||||||
| @@ -1194,9 +1158,7 @@ class Sanic( | |||||||
|             ) |             ) | ||||||
|             await response.eof() |             await response.eof() | ||||||
|         else: |         else: | ||||||
|             raise ServerError( |             raise ServerError(f"Invalid response type {response!r} (need HTTPResponse)") | ||||||
|                 f"Invalid response type {response!r} (need HTTPResponse)" |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|     async def handle_request(self, request: Request) -> None:  # no cov |     async def handle_request(self, request: Request) -> None:  # no cov | ||||||
|         """Handles a request by dispatching it to the appropriate handler. |         """Handles a request by dispatching it to the appropriate handler. | ||||||
| @@ -1221,13 +1183,11 @@ class Sanic( | |||||||
|  |  | ||||||
|         # Define `response` var here to remove warnings about |         # Define `response` var here to remove warnings about | ||||||
|         # allocation before assignment below. |         # allocation before assignment below. | ||||||
|         response: Optional[ |         response: ( | ||||||
|             Union[ |             BaseHTTPResponse | ||||||
|                 BaseHTTPResponse, |             | (Coroutine[Any, Any, BaseHTTPResponse | None] | ResponseStream) | ||||||
|                 Coroutine[Any, Any, Optional[BaseHTTPResponse]], |             | None | ||||||
|                 ResponseStream, |         ) = None | ||||||
|             ] |  | ||||||
|         ] = None |  | ||||||
|         run_middleware = True |         run_middleware = True | ||||||
|         try: |         try: | ||||||
|             await self.dispatch( |             await self.dispatch( | ||||||
| @@ -1285,11 +1245,9 @@ class Sanic( | |||||||
|  |  | ||||||
|                 if handler is None: |                 if handler is None: | ||||||
|                     raise ServerError( |                     raise ServerError( | ||||||
|                         ( |  | ||||||
|                         "'None' was returned while requesting a " |                         "'None' was returned while requesting a " | ||||||
|                         "handler from the router" |                         "handler from the router" | ||||||
|                     ) |                     ) | ||||||
|                     ) |  | ||||||
|  |  | ||||||
|                 # Run response handler |                 # Run response handler | ||||||
|                 await self.dispatch( |                 await self.dispatch( | ||||||
| @@ -1347,17 +1305,14 @@ class Sanic( | |||||||
|             else: |             else: | ||||||
|                 if not hasattr(handler, "is_websocket"): |                 if not hasattr(handler, "is_websocket"): | ||||||
|                     raise ServerError( |                     raise ServerError( | ||||||
|                         f"Invalid response type {response!r} " |                         f"Invalid response type {response!r} " "(need HTTPResponse)" | ||||||
|                         "(need HTTPResponse)" |  | ||||||
|                     ) |                     ) | ||||||
|  |  | ||||||
|         except CancelledError:  # type: ignore |         except CancelledError:  # type: ignore | ||||||
|             raise |             raise | ||||||
|         except Exception as e: |         except Exception as e: | ||||||
|             # Response Generation Failed |             # Response Generation Failed | ||||||
|             await self.handle_exception( |             await self.handle_exception(request, e, run_middleware=run_middleware) | ||||||
|                 request, e, run_middleware=run_middleware |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|     async def _websocket_handler( |     async def _websocket_handler( | ||||||
|         self, handler, request, *args, subprotocols=None, **kwargs |         self, handler, request, *args, subprotocols=None, **kwargs | ||||||
| @@ -1436,9 +1391,7 @@ class Sanic( | |||||||
|     # Execution |     # Execution | ||||||
|     # -------------------------------------------------------------------- # |     # -------------------------------------------------------------------- # | ||||||
|  |  | ||||||
|     async def _run_request_middleware( |     async def _run_request_middleware(self, request, middleware_collection):  # no cov | ||||||
|         self, request, middleware_collection |  | ||||||
|     ):  # no cov |  | ||||||
|         request._request_middleware_started = True |         request._request_middleware_started = True | ||||||
|  |  | ||||||
|         for middleware in middleware_collection: |         for middleware in middleware_collection: | ||||||
| @@ -1515,9 +1468,7 @@ class Sanic( | |||||||
|             task.cancel() |             task.cancel() | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     async def _listener( |     async def _listener(app: Sanic, loop: AbstractEventLoop, listener: ListenerType): | ||||||
|         app: Sanic, loop: AbstractEventLoop, listener: ListenerType |  | ||||||
|     ): |  | ||||||
|         try: |         try: | ||||||
|             maybe_coro = listener(app)  # type: ignore |             maybe_coro = listener(app)  # type: ignore | ||||||
|         except TypeError: |         except TypeError: | ||||||
| @@ -1546,9 +1497,7 @@ class Sanic( | |||||||
|                 if isawaitable(task): |                 if isawaitable(task): | ||||||
|                     await task |                     await task | ||||||
|             except CancelledError: |             except CancelledError: | ||||||
|                 error_logger.warning( |                 error_logger.warning(f"Task {task} was cancelled before it completed.") | ||||||
|                     f"Task {task} was cancelled before it completed." |  | ||||||
|                 ) |  | ||||||
|                 raise |                 raise | ||||||
|             except Exception as e: |             except Exception as e: | ||||||
|                 await app.dispatch( |                 await app.dispatch( | ||||||
| @@ -1566,7 +1515,7 @@ class Sanic( | |||||||
|         app, |         app, | ||||||
|         loop, |         loop, | ||||||
|         *, |         *, | ||||||
|         name: Optional[str] = None, |         name: str | None = None, | ||||||
|         register: bool = True, |         register: bool = True, | ||||||
|     ) -> Task: |     ) -> Task: | ||||||
|         if not isinstance(task, Future): |         if not isinstance(task, Future): | ||||||
| @@ -1628,11 +1577,11 @@ class Sanic( | |||||||
|  |  | ||||||
|     def add_task( |     def add_task( | ||||||
|         self, |         self, | ||||||
|         task: Union[Future[Any], Coroutine[Any, Any, Any], Awaitable[Any]], |         task: Future[Any] | (Coroutine[Any, Any, Any] | Awaitable[Any]), | ||||||
|         *, |         *, | ||||||
|         name: Optional[str] = None, |         name: str | None = None, | ||||||
|         register: bool = True, |         register: bool = True, | ||||||
|     ) -> Optional[Task[Any]]: |     ) -> Task[Any] | None: | ||||||
|         """Schedule a task to run later, after the loop has started. |         """Schedule a task to run later, after the loop has started. | ||||||
|  |  | ||||||
|         While this is somewhat similar to `asyncio.create_task`, it can be |         While this is somewhat similar to `asyncio.create_task`, it can be | ||||||
| @@ -1657,18 +1606,14 @@ class Sanic( | |||||||
|         """  # noqa: E501 |         """  # noqa: E501 | ||||||
|         try: |         try: | ||||||
|             loop = self.loop  # Will raise SanicError if loop is not started |             loop = self.loop  # Will raise SanicError if loop is not started | ||||||
|             return self._loop_add_task( |             return self._loop_add_task(task, self, loop, name=name, register=register) | ||||||
|                 task, self, loop, name=name, register=register |  | ||||||
|             ) |  | ||||||
|         except SanicException: |         except SanicException: | ||||||
|             task_name = f"sanic.delayed_task.{hash(task)}" |             task_name = f"sanic.delayed_task.{hash(task)}" | ||||||
|             if not self._delayed_tasks: |             if not self._delayed_tasks: | ||||||
|                 self.after_server_start(partial(self.dispatch_delayed_tasks)) |                 self.after_server_start(partial(self.dispatch_delayed_tasks)) | ||||||
|  |  | ||||||
|             if name: |             if name: | ||||||
|                 raise RuntimeError( |                 raise RuntimeError("Cannot name task outside of a running application") | ||||||
|                     "Cannot name task outside of a running application" |  | ||||||
|                 ) |  | ||||||
|  |  | ||||||
|             self.signal(task_name)(partial(self.run_delayed_task, task=task)) |             self.signal(task_name)(partial(self.run_delayed_task, task=task)) | ||||||
|             self._delayed_tasks.append(task_name) |             self._delayed_tasks.append(task_name) | ||||||
| @@ -1679,18 +1624,14 @@ class Sanic( | |||||||
|         ... |         ... | ||||||
|  |  | ||||||
|     @overload |     @overload | ||||||
|     def get_task( |     def get_task(self, name: str, *, raise_exception: Literal[False]) -> Task | None: | ||||||
|         self, name: str, *, raise_exception: Literal[False] |  | ||||||
|     ) -> Optional[Task]: |  | ||||||
|         ... |         ... | ||||||
|  |  | ||||||
|     @overload |     @overload | ||||||
|     def get_task(self, name: str, *, raise_exception: bool) -> Optional[Task]: |     def get_task(self, name: str, *, raise_exception: bool) -> Task | None: | ||||||
|         ... |         ... | ||||||
|  |  | ||||||
|     def get_task( |     def get_task(self, name: str, *, raise_exception: bool = True) -> Task | None: | ||||||
|         self, name: str, *, raise_exception: bool = True |  | ||||||
|     ) -> Optional[Task]: |  | ||||||
|         """Get a named task. |         """Get a named task. | ||||||
|  |  | ||||||
|         This method is used to get a task by its name. Optionally, you can |         This method is used to get a task by its name. Optionally, you can | ||||||
| @@ -1708,15 +1649,13 @@ class Sanic( | |||||||
|             return self._task_registry[name] |             return self._task_registry[name] | ||||||
|         except KeyError: |         except KeyError: | ||||||
|             if raise_exception: |             if raise_exception: | ||||||
|                 raise SanicException( |                 raise SanicException(f'Registered task named "{name}" not found.') | ||||||
|                     f'Registered task named "{name}" not found.' |  | ||||||
|                 ) |  | ||||||
|             return None |             return None | ||||||
|  |  | ||||||
|     async def cancel_task( |     async def cancel_task( | ||||||
|         self, |         self, | ||||||
|         name: str, |         name: str, | ||||||
|         msg: Optional[str] = None, |         msg: str | None = None, | ||||||
|         *, |         *, | ||||||
|         raise_exception: bool = True, |         raise_exception: bool = True, | ||||||
|     ) -> None: |     ) -> None: | ||||||
| @@ -1751,7 +1690,7 @@ class Sanic( | |||||||
|         """  # noqa: E501 |         """  # noqa: E501 | ||||||
|         task = self.get_task(name, raise_exception=raise_exception) |         task = self.get_task(name, raise_exception=raise_exception) | ||||||
|         if task and not task.cancelled(): |         if task and not task.cancelled(): | ||||||
|             args: Tuple[str, ...] = () |             args: tuple[str, ...] = () | ||||||
|             if msg: |             if msg: | ||||||
|                 if sys.version_info >= (3, 9): |                 if sys.version_info >= (3, 9): | ||||||
|                     args = (msg,) |                     args = (msg,) | ||||||
| @@ -1784,7 +1723,7 @@ class Sanic( | |||||||
|         } |         } | ||||||
|  |  | ||||||
|     def shutdown_tasks( |     def shutdown_tasks( | ||||||
|         self, timeout: Optional[float] = None, increment: float = 0.1 |         self, timeout: float | None = None, increment: float = 0.1 | ||||||
|     ) -> None: |     ) -> None: | ||||||
|         """Cancel all tasks except the server task. |         """Cancel all tasks except the server task. | ||||||
|  |  | ||||||
| @@ -1822,11 +1761,7 @@ class Sanic( | |||||||
|             Iterable[Task[Any]]: The tasks that are currently registered with |             Iterable[Task[Any]]: The tasks that are currently registered with | ||||||
|                 the application. |                 the application. | ||||||
|         """ |         """ | ||||||
|         return ( |         return (task for task in iter(self._task_registry.values()) if task is not None) | ||||||
|             task |  | ||||||
|             for task in iter(self._task_registry.values()) |  | ||||||
|             if task is not None |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|     # -------------------------------------------------------------------- # |     # -------------------------------------------------------------------- # | ||||||
|     # ASGI |     # ASGI | ||||||
| @@ -1853,7 +1788,7 @@ class Sanic( | |||||||
|     # Configuration |     # Configuration | ||||||
|     # -------------------------------------------------------------------- # |     # -------------------------------------------------------------------- # | ||||||
|  |  | ||||||
|     def update_config(self, config: Union[bytes, str, dict, Any]) -> None: |     def update_config(self, config: Any) -> None: | ||||||
|         """Update the application configuration. |         """Update the application configuration. | ||||||
|  |  | ||||||
|         This method is used to update the application configuration. It can |         This method is used to update the application configuration. It can | ||||||
| @@ -1863,7 +1798,7 @@ class Sanic( | |||||||
|         See [Configuration](/en/guide/deployment/configuration) for details. |         See [Configuration](/en/guide/deployment/configuration) for details. | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
|             config (Union[bytes, str, dict, Any]): The configuration object, |             config (bytes | str | dict | Any): The configuration object, | ||||||
|                 dictionary, or path to a configuration file. |                 dictionary, or path to a configuration file. | ||||||
|         """ |         """ | ||||||
|  |  | ||||||
| @@ -1903,7 +1838,7 @@ class Sanic( | |||||||
|         return self._state |         return self._state | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def reload_dirs(self) -> Set[Path]: |     def reload_dirs(self) -> set[Path]: | ||||||
|         """The directories that are monitored for auto-reload. |         """The directories that are monitored for auto-reload. | ||||||
|  |  | ||||||
|         Returns: |         Returns: | ||||||
| @@ -1948,9 +1883,9 @@ class Sanic( | |||||||
|     def extend( |     def extend( | ||||||
|         self, |         self, | ||||||
|         *, |         *, | ||||||
|         extensions: Optional[List[Type[Extension]]] = None, |         extensions: list[type[Extension]] | None = None, | ||||||
|         built_in_extensions: bool = True, |         built_in_extensions: bool = True, | ||||||
|         config: Optional[Union[Config, Dict[str, Any]]] = None, |         config: Config | dict[str, Any] | None = None, | ||||||
|         **kwargs, |         **kwargs, | ||||||
|     ) -> Extend: |     ) -> Extend: | ||||||
|         """Extend Sanic with additional functionality using Sanic Extensions. |         """Extend Sanic with additional functionality using Sanic Extensions. | ||||||
| @@ -2068,9 +2003,7 @@ class Sanic( | |||||||
|             del cls._app_registry[name] |             del cls._app_registry[name] | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def get_app( |     def get_app(cls, name: str | None = None, *, force_create: bool = False) -> Sanic: | ||||||
|         cls, name: Optional[str] = None, *, force_create: bool = False |  | ||||||
|     ) -> Sanic: |  | ||||||
|         """Retrieve an instantiated Sanic instance by name. |         """Retrieve an instantiated Sanic instance by name. | ||||||
|  |  | ||||||
|         This method is best used when needing to get access to an already |         This method is best used when needing to get access to an already | ||||||
| @@ -2277,9 +2210,7 @@ class Sanic( | |||||||
|         self.finalize() |         self.finalize() | ||||||
|  |  | ||||||
|         route_names = [route.extra.ident for route in self.router.routes] |         route_names = [route.extra.ident for route in self.router.routes] | ||||||
|         duplicates = { |         duplicates = {name for name in route_names if route_names.count(name) > 1} | ||||||
|             name for name in route_names if route_names.count(name) > 1 |  | ||||||
|         } |  | ||||||
|         if duplicates: |         if duplicates: | ||||||
|             names = ", ".join(duplicates) |             names = ", ".join(duplicates) | ||||||
|             message = ( |             message = ( | ||||||
| @@ -2316,7 +2247,7 @@ class Sanic( | |||||||
|         self, |         self, | ||||||
|         concern: str, |         concern: str, | ||||||
|         action: str, |         action: str, | ||||||
|         loop: Optional[AbstractEventLoop] = None, |         loop: AbstractEventLoop | None = None, | ||||||
|     ) -> None: |     ) -> None: | ||||||
|         event = f"server.{concern}.{action}" |         event = f"server.{concern}.{action}" | ||||||
|         if action not in ("before", "after") or concern not in ( |         if action not in ("before", "after") or concern not in ( | ||||||
| @@ -2324,9 +2255,7 @@ class Sanic( | |||||||
|             "shutdown", |             "shutdown", | ||||||
|         ): |         ): | ||||||
|             raise SanicException(f"Invalid server event: {event}") |             raise SanicException(f"Invalid server event: {event}") | ||||||
|         logger.debug( |         logger.debug(f"Triggering server events: {event}", extra={"verbosity": 1}) | ||||||
|             f"Triggering server events: {event}", extra={"verbosity": 1} |  | ||||||
|         ) |  | ||||||
|         reverse = concern == "shutdown" |         reverse = concern == "shutdown" | ||||||
|         if loop is None: |         if loop is None: | ||||||
|             loop = self.loop |             loop = self.loop | ||||||
| @@ -2347,7 +2276,7 @@ class Sanic( | |||||||
|  |  | ||||||
|     def refresh( |     def refresh( | ||||||
|         self, |         self, | ||||||
|         passthru: Optional[Dict[str, Any]] = None, |         passthru: dict[str, Any] | None = None, | ||||||
|     ) -> Sanic: |     ) -> Sanic: | ||||||
|         """Refresh the application instance. **This is used internally by Sanic**. |         """Refresh the application instance. **This is used internally by Sanic**. | ||||||
|  |  | ||||||
| @@ -2392,9 +2321,7 @@ class Sanic( | |||||||
|             Inspector: An instance of Inspector. |             Inspector: An instance of Inspector. | ||||||
|         """ |         """ | ||||||
|         if environ.get("SANIC_WORKER_PROCESS") or not self._inspector: |         if environ.get("SANIC_WORKER_PROCESS") or not self._inspector: | ||||||
|             raise SanicException( |             raise SanicException("Can only access the inspector from the main process") | ||||||
|                 "Can only access the inspector from the main process" |  | ||||||
|             ) |  | ||||||
|         return self._inspector |         return self._inspector | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
| @@ -2427,7 +2354,5 @@ class Sanic( | |||||||
|         """ |         """ | ||||||
|  |  | ||||||
|         if environ.get("SANIC_WORKER_PROCESS") or not self._manager: |         if environ.get("SANIC_WORKER_PROCESS") or not self._manager: | ||||||
|             raise SanicException( |             raise SanicException("Can only access the manager from the main process") | ||||||
|                 "Can only access the manager from the main process" |  | ||||||
|             ) |  | ||||||
|         return self._manager |         return self._manager | ||||||
|   | |||||||
| @@ -4,7 +4,6 @@ from contextlib import suppress | |||||||
| from importlib import import_module | from importlib import import_module | ||||||
| from typing import TYPE_CHECKING | from typing import TYPE_CHECKING | ||||||
|  |  | ||||||
|  |  | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     from sanic import Sanic |     from sanic import Sanic | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,11 +1,9 @@ | |||||||
| import re | import re | ||||||
| import sys | import sys | ||||||
|  |  | ||||||
| from os import environ | from os import environ | ||||||
|  |  | ||||||
| from sanic.helpers import is_atty | from sanic.helpers import is_atty | ||||||
|  |  | ||||||
|  |  | ||||||
| BASE_LOGO = """ | BASE_LOGO = """ | ||||||
|  |  | ||||||
|                  Sanic |                  Sanic | ||||||
| @@ -63,10 +61,7 @@ def get_logo(full: bool = False, coffee: bool = False) -> str: | |||||||
|         else BASE_LOGO |         else BASE_LOGO | ||||||
|     ) |     ) | ||||||
|  |  | ||||||
|     if ( |     if sys.platform == "darwin" and environ.get("TERM_PROGRAM") == "Apple_Terminal": | ||||||
|         sys.platform == "darwin" |  | ||||||
|         and environ.get("TERM_PROGRAM") == "Apple_Terminal" |  | ||||||
|     ): |  | ||||||
|         logo = ansi_pattern.sub("", logo) |         logo = ansi_pattern.sub("", logo) | ||||||
|  |  | ||||||
|     return logo |     return logo | ||||||
|   | |||||||
| @@ -79,9 +79,7 @@ class MOTDTTY(MOTD): | |||||||
|     def set_variables(self):  # no  cov |     def set_variables(self):  # no  cov | ||||||
|         """Set the variables used for display.""" |         """Set the variables used for display.""" | ||||||
|         fallback = (108, 24) |         fallback = (108, 24) | ||||||
|         terminal_width = max( |         terminal_width = max(get_terminal_size(fallback=fallback).columns, fallback[0]) | ||||||
|             get_terminal_size(fallback=fallback).columns, fallback[0] |  | ||||||
|         ) |  | ||||||
|         self.max_value_width = terminal_width - fallback[0] + 36 |         self.max_value_width = terminal_width - fallback[0] + 36 | ||||||
|  |  | ||||||
|         self.key_width = 4 |         self.key_width = 4 | ||||||
|   | |||||||
| @@ -1,12 +1,10 @@ | |||||||
| import os | import os | ||||||
| import sys | import sys | ||||||
| import time | import time | ||||||
|  |  | ||||||
| from contextlib import contextmanager | from contextlib import contextmanager | ||||||
| from queue import Queue | from queue import Queue | ||||||
| from threading import Thread | from threading import Thread | ||||||
|  |  | ||||||
|  |  | ||||||
| if os.name == "nt":  # noqa | if os.name == "nt":  # noqa | ||||||
|     import ctypes  # noqa |     import ctypes  # noqa | ||||||
|  |  | ||||||
| @@ -47,21 +45,16 @@ class Spinner:  # noqa | |||||||
|     @staticmethod |     @staticmethod | ||||||
|     def cursor(): |     def cursor(): | ||||||
|         while True: |         while True: | ||||||
|             for cursor in "|/-\\": |             yield from "|/-\\" | ||||||
|                 yield cursor |  | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def hide(): |     def hide(): | ||||||
|         if os.name == "nt": |         if os.name == "nt": | ||||||
|             ci = _CursorInfo() |             ci = _CursorInfo() | ||||||
|             handle = ctypes.windll.kernel32.GetStdHandle(-11) |             handle = ctypes.windll.kernel32.GetStdHandle(-11) | ||||||
|             ctypes.windll.kernel32.GetConsoleCursorInfo( |             ctypes.windll.kernel32.GetConsoleCursorInfo(handle, ctypes.byref(ci)) | ||||||
|                 handle, ctypes.byref(ci) |  | ||||||
|             ) |  | ||||||
|             ci.visible = False |             ci.visible = False | ||||||
|             ctypes.windll.kernel32.SetConsoleCursorInfo( |             ctypes.windll.kernel32.SetConsoleCursorInfo(handle, ctypes.byref(ci)) | ||||||
|                 handle, ctypes.byref(ci) |  | ||||||
|             ) |  | ||||||
|         elif os.name == "posix": |         elif os.name == "posix": | ||||||
|             sys.stdout.write("\033[?25l") |             sys.stdout.write("\033[?25l") | ||||||
|             sys.stdout.flush() |             sys.stdout.flush() | ||||||
| @@ -71,13 +64,9 @@ class Spinner:  # noqa | |||||||
|         if os.name == "nt": |         if os.name == "nt": | ||||||
|             ci = _CursorInfo() |             ci = _CursorInfo() | ||||||
|             handle = ctypes.windll.kernel32.GetStdHandle(-11) |             handle = ctypes.windll.kernel32.GetStdHandle(-11) | ||||||
|             ctypes.windll.kernel32.GetConsoleCursorInfo( |             ctypes.windll.kernel32.GetConsoleCursorInfo(handle, ctypes.byref(ci)) | ||||||
|                 handle, ctypes.byref(ci) |  | ||||||
|             ) |  | ||||||
|             ci.visible = True |             ci.visible = True | ||||||
|             ctypes.windll.kernel32.SetConsoleCursorInfo( |             ctypes.windll.kernel32.SetConsoleCursorInfo(handle, ctypes.byref(ci)) | ||||||
|                 handle, ctypes.byref(ci) |  | ||||||
|             ) |  | ||||||
|         elif os.name == "posix": |         elif os.name == "posix": | ||||||
|             sys.stdout.write("\033[?25h") |             sys.stdout.write("\033[?25h") | ||||||
|             sys.stdout.flush() |             sys.stdout.flush() | ||||||
|   | |||||||
| @@ -1,18 +1,16 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| import logging | import logging | ||||||
|  |  | ||||||
| from dataclasses import dataclass, field | from dataclasses import dataclass, field | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from socket import socket | from socket import socket | ||||||
| from ssl import SSLContext | from ssl import SSLContext | ||||||
| from typing import TYPE_CHECKING, Any, Dict, List, Optional, Set, Union | from typing import TYPE_CHECKING, Any | ||||||
|  |  | ||||||
| from sanic.application.constants import Mode, Server, ServerStage | from sanic.application.constants import Mode, Server, ServerStage | ||||||
| from sanic.log import VerbosityFilter, logger | from sanic.log import VerbosityFilter, logger | ||||||
| from sanic.server.async_server import AsyncioServer | from sanic.server.async_server import AsyncioServer | ||||||
|  |  | ||||||
|  |  | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     from sanic import Sanic |     from sanic import Sanic | ||||||
|  |  | ||||||
| @@ -21,9 +19,9 @@ if TYPE_CHECKING: | |||||||
| class ApplicationServerInfo: | class ApplicationServerInfo: | ||||||
|     """Information about a server instance.""" |     """Information about a server instance.""" | ||||||
|  |  | ||||||
|     settings: Dict[str, Any] |     settings: dict[str, Any] | ||||||
|     stage: ServerStage = field(default=ServerStage.STOPPED) |     stage: ServerStage = field(default=ServerStage.STOPPED) | ||||||
|     server: Optional[AsyncioServer] = field(default=None) |     server: AsyncioServer | None = field(default=None) | ||||||
|  |  | ||||||
|  |  | ||||||
| @dataclass | @dataclass | ||||||
| @@ -40,11 +38,11 @@ class ApplicationState: | |||||||
|     fast: bool = field(default=False) |     fast: bool = field(default=False) | ||||||
|     host: str = field(default="") |     host: str = field(default="") | ||||||
|     port: int = field(default=0) |     port: int = field(default=0) | ||||||
|     ssl: Optional[SSLContext] = field(default=None) |     ssl: SSLContext | None = field(default=None) | ||||||
|     sock: Optional[socket] = field(default=None) |     sock: socket | None = field(default=None) | ||||||
|     unix: Optional[str] = field(default=None) |     unix: str | None = field(default=None) | ||||||
|     mode: Mode = field(default=Mode.PRODUCTION) |     mode: Mode = field(default=Mode.PRODUCTION) | ||||||
|     reload_dirs: Set[Path] = field(default_factory=set) |     reload_dirs: set[Path] = field(default_factory=set) | ||||||
|     auto_reload: bool = field(default=False) |     auto_reload: bool = field(default=False) | ||||||
|     server: Server = field(default=Server.SANIC) |     server: Server = field(default=Server.SANIC) | ||||||
|     is_running: bool = field(default=False) |     is_running: bool = field(default=False) | ||||||
| @@ -53,7 +51,7 @@ class ApplicationState: | |||||||
|     verbosity: int = field(default=0) |     verbosity: int = field(default=0) | ||||||
|     workers: int = field(default=0) |     workers: int = field(default=0) | ||||||
|     primary: bool = field(default=True) |     primary: bool = field(default=True) | ||||||
|     server_info: List[ApplicationServerInfo] = field(default_factory=list) |     server_info: list[ApplicationServerInfo] = field(default_factory=list) | ||||||
|  |  | ||||||
|     # This property relates to the ApplicationState instance and should |     # This property relates to the ApplicationState instance and should | ||||||
|     # not be changed except in the __post_init__ method |     # not be changed except in the __post_init__ method | ||||||
| @@ -64,14 +62,12 @@ class ApplicationState: | |||||||
|  |  | ||||||
|     def __setattr__(self, name: str, value: Any) -> None: |     def __setattr__(self, name: str, value: Any) -> None: | ||||||
|         if self._init and name == "_init": |         if self._init and name == "_init": | ||||||
|             raise RuntimeError( |             raise RuntimeError("Cannot change the value of _init after instantiation") | ||||||
|                 "Cannot change the value of _init after instantiation" |  | ||||||
|             ) |  | ||||||
|         super().__setattr__(name, value) |         super().__setattr__(name, value) | ||||||
|         if self._init and hasattr(self, f"set_{name}"): |         if self._init and hasattr(self, f"set_{name}"): | ||||||
|             getattr(self, f"set_{name}")(value) |             getattr(self, f"set_{name}")(value) | ||||||
|  |  | ||||||
|     def set_mode(self, value: Union[str, Mode]): |     def set_mode(self, value: str | Mode): | ||||||
|         if hasattr(self.app, "error_handler"): |         if hasattr(self.app, "error_handler"): | ||||||
|             self.app.error_handler.debug = self.app.debug |             self.app.error_handler.debug = self.app.debug | ||||||
|         if getattr(self.app, "configure_logging", False) and self.app.debug: |         if getattr(self.app, "configure_logging", False) and self.app.debug: | ||||||
| @@ -107,9 +103,7 @@ class ApplicationState: | |||||||
|  |  | ||||||
|         if all(info.stage is ServerStage.SERVING for info in self.server_info): |         if all(info.stage is ServerStage.SERVING for info in self.server_info): | ||||||
|             return ServerStage.SERVING |             return ServerStage.SERVING | ||||||
|         elif any( |         elif any(info.stage is ServerStage.SERVING for info in self.server_info): | ||||||
|             info.stage is ServerStage.SERVING for info in self.server_info |  | ||||||
|         ): |  | ||||||
|             return ServerStage.PARTIAL |             return ServerStage.PARTIAL | ||||||
|  |  | ||||||
|         return ServerStage.STOPPED |         return ServerStage.STOPPED | ||||||
|   | |||||||
| @@ -1,8 +1,7 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| import warnings | import warnings | ||||||
|  | from typing import TYPE_CHECKING | ||||||
| from typing import TYPE_CHECKING, Optional |  | ||||||
|  |  | ||||||
| from sanic.compat import Header | from sanic.compat import Header | ||||||
| from sanic.exceptions import BadRequest, ServerError | from sanic.exceptions import BadRequest, ServerError | ||||||
| @@ -15,7 +14,6 @@ from sanic.response import BaseHTTPResponse | |||||||
| from sanic.server import ConnInfo | from sanic.server import ConnInfo | ||||||
| from sanic.server.websockets.connection import WebSocketConnection | from sanic.server.websockets.connection import WebSocketConnection | ||||||
|  |  | ||||||
|  |  | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     from sanic import Sanic |     from sanic import Sanic | ||||||
|  |  | ||||||
| @@ -109,9 +107,9 @@ class ASGIApp: | |||||||
|     request: Request |     request: Request | ||||||
|     transport: MockTransport |     transport: MockTransport | ||||||
|     lifespan: Lifespan |     lifespan: Lifespan | ||||||
|     ws: Optional[WebSocketConnection] |     ws: WebSocketConnection | None | ||||||
|     stage: Stage |     stage: Stage | ||||||
|     response: Optional[BaseHTTPResponse] |     response: BaseHTTPResponse | None | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     async def create( |     async def create( | ||||||
| @@ -142,9 +140,7 @@ class ASGIApp: | |||||||
|                 ] |                 ] | ||||||
|             ) |             ) | ||||||
|         except UnicodeDecodeError: |         except UnicodeDecodeError: | ||||||
|             raise BadRequest( |             raise BadRequest("Header names can only contain US-ASCII characters") | ||||||
|                 "Header names can only contain US-ASCII characters" |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|         if scope["type"] == "http": |         if scope["type"] == "http": | ||||||
|             version = scope["http_version"] |             version = scope["http_version"] | ||||||
| @@ -153,9 +149,7 @@ class ASGIApp: | |||||||
|             version = "1.1" |             version = "1.1" | ||||||
|             method = "GET" |             method = "GET" | ||||||
|  |  | ||||||
|             instance.ws = instance.transport.create_websocket_connection( |             instance.ws = instance.transport.create_websocket_connection(send, receive) | ||||||
|                 send, receive |  | ||||||
|             ) |  | ||||||
|         else: |         else: | ||||||
|             raise ServerError("Received unknown ASGI scope") |             raise ServerError("Received unknown ASGI scope") | ||||||
|  |  | ||||||
| @@ -189,7 +183,7 @@ class ASGIApp: | |||||||
|  |  | ||||||
|         return instance |         return instance | ||||||
|  |  | ||||||
|     async def read(self) -> Optional[bytes]: |     async def read(self) -> bytes | None: | ||||||
|         """ |         """ | ||||||
|         Read and stream the body in chunks from an incoming ASGI message. |         Read and stream the body in chunks from an incoming ASGI message. | ||||||
|         """ |         """ | ||||||
|   | |||||||
| @@ -1,5 +1,4 @@ | |||||||
| import re | import re | ||||||
|  |  | ||||||
| from typing import Any, Optional | from typing import Any, Optional | ||||||
|  |  | ||||||
| from sanic.base.meta import SanicMeta | from sanic.base.meta import SanicMeta | ||||||
| @@ -11,7 +10,6 @@ from sanic.mixins.routes import RouteMixin | |||||||
| from sanic.mixins.signals import SignalMixin | from sanic.mixins.signals import SignalMixin | ||||||
| from sanic.mixins.static import StaticMixin | from sanic.mixins.static import StaticMixin | ||||||
|  |  | ||||||
|  |  | ||||||
| VALID_NAME = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_\-]*$") | VALID_NAME = re.compile(r"^[a-zA-Z_][a-zA-Z0-9_\-]*$") | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -26,9 +24,7 @@ class BaseSanic( | |||||||
| ): | ): | ||||||
|     __slots__ = ("name",) |     __slots__ = ("name",) | ||||||
|  |  | ||||||
|     def __init__( |     def __init__(self, name: Optional[str] = None, *args: Any, **kwargs: Any) -> None: | ||||||
|         self, name: Optional[str] = None, *args: Any, **kwargs: Any |  | ||||||
|     ) -> None: |  | ||||||
|         class_name = self.__class__.__name__ |         class_name = self.__class__.__name__ | ||||||
|  |  | ||||||
|         if name is None: |         if name is None: | ||||||
|   | |||||||
| @@ -1,4 +1,3 @@ | |||||||
| from .blueprints import BlueprintGroup | from .blueprints import BlueprintGroup | ||||||
|  |  | ||||||
|  |  | ||||||
| __all__ = ["BlueprintGroup"]  # noqa: F405 | __all__ = ["BlueprintGroup"]  # noqa: F405 | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ from __future__ import annotations | |||||||
|  |  | ||||||
| import asyncio | import asyncio | ||||||
| import sys | import sys | ||||||
|  |  | ||||||
| from collections import defaultdict | from collections import defaultdict | ||||||
| from collections.abc import MutableSequence | from collections.abc import MutableSequence | ||||||
| from copy import deepcopy | from copy import deepcopy | ||||||
| @@ -14,15 +13,9 @@ from typing import ( | |||||||
|     TYPE_CHECKING, |     TYPE_CHECKING, | ||||||
|     Any, |     Any, | ||||||
|     Callable, |     Callable, | ||||||
|     Dict, |  | ||||||
|     Iterable, |     Iterable, | ||||||
|     Iterator, |     Iterator, | ||||||
|     List, |  | ||||||
|     Optional, |  | ||||||
|     Sequence, |     Sequence, | ||||||
|     Set, |  | ||||||
|     Tuple, |  | ||||||
|     Union, |  | ||||||
|     overload, |     overload, | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -39,7 +32,6 @@ from sanic.models.handler_types import ( | |||||||
|     RouteHandler, |     RouteHandler, | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  |  | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     from sanic import Sanic |     from sanic import Sanic | ||||||
|  |  | ||||||
| @@ -122,10 +114,10 @@ class Blueprint(BaseSanic): | |||||||
|     def __init__( |     def __init__( | ||||||
|         self, |         self, | ||||||
|         name: str, |         name: str, | ||||||
|         url_prefix: Optional[str] = None, |         url_prefix: str | None = None, | ||||||
|         host: Optional[Union[List[str], str]] = None, |         host: list[str] | str | None = None, | ||||||
|         version: Optional[Union[int, str, float]] = None, |         version: int | str | float | None = None, | ||||||
|         strict_slashes: Optional[bool] = None, |         strict_slashes: bool | None = None, | ||||||
|         version_prefix: str = "/v", |         version_prefix: str = "/v", | ||||||
|     ): |     ): | ||||||
|         super().__init__(name=name) |         super().__init__(name=name) | ||||||
| @@ -136,9 +128,7 @@ class Blueprint(BaseSanic): | |||||||
|         self.host = host |         self.host = host | ||||||
|         self.strict_slashes = strict_slashes |         self.strict_slashes = strict_slashes | ||||||
|         self.url_prefix = ( |         self.url_prefix = ( | ||||||
|             url_prefix[:-1] |             url_prefix[:-1] if url_prefix and url_prefix.endswith("/") else url_prefix | ||||||
|             if url_prefix and url_prefix.endswith("/") |  | ||||||
|             else url_prefix |  | ||||||
|         ) |         ) | ||||||
|         self.version = version |         self.version = version | ||||||
|         self.version_prefix = version_prefix |         self.version_prefix = version_prefix | ||||||
| @@ -161,7 +151,7 @@ class Blueprint(BaseSanic): | |||||||
|         return f"Blueprint({args})" |         return f"Blueprint({args})" | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def apps(self) -> Set[Sanic]: |     def apps(self) -> set[Sanic]: | ||||||
|         """Get the set of apps that this blueprint is registered to. |         """Get the set of apps that this blueprint is registered to. | ||||||
|  |  | ||||||
|         Returns: |         Returns: | ||||||
| @@ -172,9 +162,7 @@ class Blueprint(BaseSanic): | |||||||
|                 an app. |                 an app. | ||||||
|         """ |         """ | ||||||
|         if not self._apps: |         if not self._apps: | ||||||
|             raise SanicException( |             raise SanicException(f"{self} has not yet been registered to an app") | ||||||
|                 f"{self} has not yet been registered to an app" |  | ||||||
|             ) |  | ||||||
|         return self._apps |         return self._apps | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
| @@ -196,23 +184,23 @@ class Blueprint(BaseSanic): | |||||||
|  |  | ||||||
|     def reset(self) -> None: |     def reset(self) -> None: | ||||||
|         """Reset the blueprint to its initial state.""" |         """Reset the blueprint to its initial state.""" | ||||||
|         self._apps: Set[Sanic] = set() |         self._apps: set[Sanic] = set() | ||||||
|         self._allow_route_overwrite = False |         self._allow_route_overwrite = False | ||||||
|         self.exceptions: List[RouteHandler] = [] |         self.exceptions: list[RouteHandler] = [] | ||||||
|         self.listeners: Dict[str, List[ListenerType[Any]]] = {} |         self.listeners: dict[str, list[ListenerType[Any]]] = {} | ||||||
|         self.middlewares: List[MiddlewareType] = [] |         self.middlewares: list[MiddlewareType] = [] | ||||||
|         self.routes: List[Route] = [] |         self.routes: list[Route] = [] | ||||||
|         self.statics: List[RouteHandler] = [] |         self.statics: list[RouteHandler] = [] | ||||||
|         self.websocket_routes: List[Route] = [] |         self.websocket_routes: list[Route] = [] | ||||||
|  |  | ||||||
|     def copy( |     def copy( | ||||||
|         self, |         self, | ||||||
|         name: str, |         name: str, | ||||||
|         url_prefix: Optional[Union[str, Default]] = _default, |         url_prefix: str | Default | None = _default, | ||||||
|         version: Optional[Union[int, str, float, Default]] = _default, |         version: int | str | float | Default | None = _default, | ||||||
|         version_prefix: Union[str, Default] = _default, |         version_prefix: str | Default = _default, | ||||||
|         allow_route_overwrite: Union[bool, Default] = _default, |         allow_route_overwrite: bool | Default = _default, | ||||||
|         strict_slashes: Optional[Union[bool, Default]] = _default, |         strict_slashes: bool | Default | None = _default, | ||||||
|         with_registration: bool = True, |         with_registration: bool = True, | ||||||
|         with_ctx: bool = False, |         with_ctx: bool = False, | ||||||
|     ): |     ): | ||||||
| @@ -277,12 +265,12 @@ class Blueprint(BaseSanic): | |||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def group( |     def group( | ||||||
|         *blueprints: Union[Blueprint, BlueprintGroup], |         *blueprints: Blueprint | BlueprintGroup, | ||||||
|         url_prefix: Optional[str] = None, |         url_prefix: str | None = None, | ||||||
|         version: Optional[Union[int, str, float]] = None, |         version: int | str | float | None = None, | ||||||
|         strict_slashes: Optional[bool] = None, |         strict_slashes: bool | None = None, | ||||||
|         version_prefix: str = "/v", |         version_prefix: str = "/v", | ||||||
|         name_prefix: Optional[str] = "", |         name_prefix: str | None = "", | ||||||
|     ) -> BlueprintGroup: |     ) -> BlueprintGroup: | ||||||
|         """Group multiple blueprints (or other blueprint groups) together. |         """Group multiple blueprints (or other blueprint groups) together. | ||||||
|  |  | ||||||
| @@ -353,9 +341,7 @@ class Blueprint(BaseSanic): | |||||||
|         opt_strict_slashes = options.get("strict_slashes", None) |         opt_strict_slashes = options.get("strict_slashes", None) | ||||||
|         opt_version_prefix = options.get("version_prefix", self.version_prefix) |         opt_version_prefix = options.get("version_prefix", self.version_prefix) | ||||||
|         opt_name_prefix = options.get("name_prefix", None) |         opt_name_prefix = options.get("name_prefix", None) | ||||||
|         error_format = options.get( |         error_format = options.get("error_format", app.config.FALLBACK_ERROR_FORMAT) | ||||||
|             "error_format", app.config.FALLBACK_ERROR_FORMAT |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         routes = [] |         routes = [] | ||||||
|         middleware = [] |         middleware = [] | ||||||
| @@ -381,9 +367,7 @@ class Blueprint(BaseSanic): | |||||||
|                     version_prefix = prefix |                     version_prefix = prefix | ||||||
|                     break |                     break | ||||||
|  |  | ||||||
|             version = self._extract_value( |             version = self._extract_value(future.version, opt_version, self.version) | ||||||
|                 future.version, opt_version, self.version |  | ||||||
|             ) |  | ||||||
|             strict_slashes = self._extract_value( |             strict_slashes = self._extract_value( | ||||||
|                 future.strict_slashes, opt_strict_slashes, self.strict_slashes |                 future.strict_slashes, opt_strict_slashes, self.strict_slashes | ||||||
|             ) |             ) | ||||||
| @@ -419,22 +403,16 @@ class Blueprint(BaseSanic): | |||||||
|                 continue |                 continue | ||||||
|  |  | ||||||
|             registered.add(apply_route) |             registered.add(apply_route) | ||||||
|             route = app._apply_route( |             route = app._apply_route(apply_route, overwrite=self._allow_route_overwrite) | ||||||
|                 apply_route, overwrite=self._allow_route_overwrite |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|             # If it is a copied BP, then make sure all of the names of routes |             # If it is a copied BP, then make sure all of the names of routes | ||||||
|             # matchup with the new BP name |             # matchup with the new BP name | ||||||
|             if self.copied_from: |             if self.copied_from: | ||||||
|                 for r in route: |                 for r in route: | ||||||
|                     r.name = r.name.replace(self.copied_from, self.name) |                     r.name = r.name.replace(self.copied_from, self.name) | ||||||
|                     r.extra.ident = r.extra.ident.replace( |                     r.extra.ident = r.extra.ident.replace(self.copied_from, self.name) | ||||||
|                         self.copied_from, self.name |  | ||||||
|                     ) |  | ||||||
|  |  | ||||||
|             operation = ( |             operation = routes.extend if isinstance(route, list) else routes.append | ||||||
|                 routes.extend if isinstance(route, list) else routes.append |  | ||||||
|             ) |  | ||||||
|             operation(route) |             operation(route) | ||||||
|  |  | ||||||
|         # Static Files |         # Static Files | ||||||
| @@ -479,7 +457,7 @@ class Blueprint(BaseSanic): | |||||||
|                 continue |                 continue | ||||||
|             future.condition.update({"__blueprint__": self.name}) |             future.condition.update({"__blueprint__": self.name}) | ||||||
|             # Force exclusive to be False |             # Force exclusive to be False | ||||||
|             app._apply_signal(tuple((*future[:-1], False))) |             app._apply_signal((*future[:-1], False)) | ||||||
|  |  | ||||||
|         self.routes += [route for route in routes if isinstance(route, Route)] |         self.routes += [route for route in routes if isinstance(route, Route)] | ||||||
|         self.websocket_routes += [ |         self.websocket_routes += [ | ||||||
| @@ -512,11 +490,9 @@ class Blueprint(BaseSanic): | |||||||
|         condition = kwargs.pop("condition", {}) |         condition = kwargs.pop("condition", {}) | ||||||
|         condition.update({"__blueprint__": self.name}) |         condition.update({"__blueprint__": self.name}) | ||||||
|         kwargs["condition"] = condition |         kwargs["condition"] = condition | ||||||
|         await asyncio.gather( |         await asyncio.gather(*[app.dispatch(*args, **kwargs) for app in self.apps]) | ||||||
|             *[app.dispatch(*args, **kwargs) for app in self.apps] |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|     def event(self, event: str, timeout: Optional[Union[int, float]] = None): |     def event(self, event: str, timeout: int | float | None = None): | ||||||
|         """Wait for a signal event to be dispatched. |         """Wait for a signal event to be dispatched. | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
| @@ -550,7 +526,7 @@ class Blueprint(BaseSanic): | |||||||
|         return value |         return value | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def _setup_uri(base: str, prefix: Optional[str]): |     def _setup_uri(base: str, prefix: str | None): | ||||||
|         uri = base |         uri = base | ||||||
|         if prefix: |         if prefix: | ||||||
|             uri = prefix |             uri = prefix | ||||||
| @@ -563,7 +539,7 @@ class Blueprint(BaseSanic): | |||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def register_futures( |     def register_futures( | ||||||
|         apps: Set[Sanic], bp: Blueprint, futures: Sequence[Tuple[Any, ...]] |         apps: set[Sanic], bp: Blueprint, futures: Sequence[tuple[Any, ...]] | ||||||
|     ): |     ): | ||||||
|         """Register futures to the apps. |         """Register futures to the apps. | ||||||
|  |  | ||||||
| @@ -575,7 +551,7 @@ class Blueprint(BaseSanic): | |||||||
|         """ |         """ | ||||||
|  |  | ||||||
|         for app in apps: |         for app in apps: | ||||||
|             app._future_registry.update(set((bp, item) for item in futures)) |             app._future_registry.update({(bp, item) for item in futures}) | ||||||
|  |  | ||||||
|  |  | ||||||
| if sys.version_info < (3, 9): | if sys.version_info < (3, 9): | ||||||
| @@ -667,13 +643,13 @@ class BlueprintGroup(bpg_base): | |||||||
|  |  | ||||||
|     def __init__( |     def __init__( | ||||||
|         self, |         self, | ||||||
|         url_prefix: Optional[str] = None, |         url_prefix: str | None = None, | ||||||
|         version: Optional[Union[int, str, float]] = None, |         version: int | str | float | None = None, | ||||||
|         strict_slashes: Optional[bool] = None, |         strict_slashes: bool | None = None, | ||||||
|         version_prefix: str = "/v", |         version_prefix: str = "/v", | ||||||
|         name_prefix: Optional[str] = "", |         name_prefix: str | None = "", | ||||||
|     ): |     ): | ||||||
|         self._blueprints: List[Blueprint] = [] |         self._blueprints: list[Blueprint] = [] | ||||||
|         self._url_prefix = url_prefix |         self._url_prefix = url_prefix | ||||||
|         self._version = version |         self._version = version | ||||||
|         self._version_prefix = version_prefix |         self._version_prefix = version_prefix | ||||||
| @@ -681,7 +657,7 @@ class BlueprintGroup(bpg_base): | |||||||
|         self._name_prefix = name_prefix |         self._name_prefix = name_prefix | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def url_prefix(self) -> Optional[Union[int, str, float]]: |     def url_prefix(self) -> int | str | float | None: | ||||||
|         """The URL prefix for the Blueprint Group. |         """The URL prefix for the Blueprint Group. | ||||||
|  |  | ||||||
|         Returns: |         Returns: | ||||||
| @@ -691,7 +667,7 @@ class BlueprintGroup(bpg_base): | |||||||
|         return self._url_prefix |         return self._url_prefix | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def blueprints(self) -> List[Blueprint]: |     def blueprints(self) -> list[Blueprint]: | ||||||
|         """A list of all the available blueprints under this group. |         """A list of all the available blueprints under this group. | ||||||
|  |  | ||||||
|         Returns: |         Returns: | ||||||
| @@ -701,7 +677,7 @@ class BlueprintGroup(bpg_base): | |||||||
|         return self._blueprints |         return self._blueprints | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def version(self) -> Optional[Union[str, int, float]]: |     def version(self) -> str | int | float | None: | ||||||
|         """API Version for the Blueprint Group, if any. |         """API Version for the Blueprint Group, if any. | ||||||
|  |  | ||||||
|         Returns: |         Returns: | ||||||
| @@ -710,7 +686,7 @@ class BlueprintGroup(bpg_base): | |||||||
|         return self._version |         return self._version | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def strict_slashes(self) -> Optional[bool]: |     def strict_slashes(self) -> bool | None: | ||||||
|         """Whether to enforce strict slashes for the Blueprint Group. |         """Whether to enforce strict slashes for the Blueprint Group. | ||||||
|  |  | ||||||
|         Returns: |         Returns: | ||||||
| @@ -728,7 +704,7 @@ class BlueprintGroup(bpg_base): | |||||||
|         return self._version_prefix |         return self._version_prefix | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def name_prefix(self) -> Optional[str]: |     def name_prefix(self) -> str | None: | ||||||
|         """Name prefix for the Blueprint Group. |         """Name prefix for the Blueprint Group. | ||||||
|  |  | ||||||
|         This is mainly needed when blueprints are copied in order to |         This is mainly needed when blueprints are copied in order to | ||||||
| @@ -755,9 +731,7 @@ class BlueprintGroup(bpg_base): | |||||||
|     def __getitem__(self, item: slice) -> MutableSequence[Blueprint]: |     def __getitem__(self, item: slice) -> MutableSequence[Blueprint]: | ||||||
|         ... |         ... | ||||||
|  |  | ||||||
|     def __getitem__( |     def __getitem__(self, item: int | slice) -> Blueprint | MutableSequence[Blueprint]: | ||||||
|         self, item: Union[int, slice] |  | ||||||
|     ) -> Union[Blueprint, MutableSequence[Blueprint]]: |  | ||||||
|         """Get the Blueprint object at the specified index. |         """Get the Blueprint object at the specified index. | ||||||
|  |  | ||||||
|         This method returns a blueprint inside the group specified by |         This method returns a blueprint inside the group specified by | ||||||
| @@ -785,8 +759,8 @@ class BlueprintGroup(bpg_base): | |||||||
|  |  | ||||||
|     def __setitem__( |     def __setitem__( | ||||||
|         self, |         self, | ||||||
|         index: Union[int, slice], |         index: int | slice, | ||||||
|         item: Union[Blueprint, Iterable[Blueprint]], |         item: Blueprint | Iterable[Blueprint], | ||||||
|     ) -> None: |     ) -> None: | ||||||
|         """Set the Blueprint object at the specified index. |         """Set the Blueprint object at the specified index. | ||||||
|  |  | ||||||
| @@ -824,7 +798,7 @@ class BlueprintGroup(bpg_base): | |||||||
|     def __delitem__(self, index: slice) -> None: |     def __delitem__(self, index: slice) -> None: | ||||||
|         ... |         ... | ||||||
|  |  | ||||||
|     def __delitem__(self, index: Union[int, slice]) -> None: |     def __delitem__(self, index: int | slice) -> None: | ||||||
|         """Delete the Blueprint object at the specified index. |         """Delete the Blueprint object at the specified index. | ||||||
|  |  | ||||||
|         Abstract method implemented to turn the `BlueprintGroup` class |         Abstract method implemented to turn the `BlueprintGroup` class | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| import os | import os | ||||||
| import shutil | import shutil | ||||||
| import sys | import sys | ||||||
|  |  | ||||||
| from argparse import Namespace | from argparse import Namespace | ||||||
| from functools import partial | from functools import partial | ||||||
| from textwrap import indent | from textwrap import indent | ||||||
| @@ -57,9 +56,7 @@ Or, a path to a directory to run as a simple HTTP server: | |||||||
|         ) |         ) | ||||||
|         self.parser._positionals.title = "Required\n========\n  Positional" |         self.parser._positionals.title = "Required\n========\n  Positional" | ||||||
|         self.parser._optionals.title = "Optional\n========\n  General" |         self.parser._optionals.title = "Optional\n========\n  General" | ||||||
|         self.main_process = ( |         self.main_process = os.environ.get("SANIC_RELOADER_PROCESS", "") != "true" | ||||||
|             os.environ.get("SANIC_RELOADER_PROCESS", "") != "true" |  | ||||||
|         ) |  | ||||||
|         self.args: Namespace = Namespace() |         self.args: Namespace = Namespace() | ||||||
|         self.groups: List[Group] = [] |         self.groups: List[Group] = [] | ||||||
|         self.inspecting = False |         self.inspecting = False | ||||||
| @@ -127,11 +124,7 @@ Or, a path to a directory to run as a simple HTTP server: | |||||||
|                         key = key.lstrip("-") |                         key = key.lstrip("-") | ||||||
|                     except ValueError: |                     except ValueError: | ||||||
|                         value = False if arg.startswith("--no-") else True |                         value = False if arg.startswith("--no-") else True | ||||||
|                         key = ( |                         key = arg.replace("--no-", "").lstrip("-").replace("-", "_") | ||||||
|                             arg.replace("--no-", "") |  | ||||||
|                             .lstrip("-") |  | ||||||
|                             .replace("-", "_") |  | ||||||
|                         ) |  | ||||||
|                     setattr(self.args, key, value) |                     setattr(self.args, key, value) | ||||||
|  |  | ||||||
|         kwargs = {**self.args.__dict__} |         kwargs = {**self.args.__dict__} | ||||||
| @@ -181,8 +174,7 @@ Or, a path to a directory to run as a simple HTTP server: | |||||||
|                     "  Example Module: project.sanic_server.app" |                     "  Example Module: project.sanic_server.app" | ||||||
|                 ) |                 ) | ||||||
|                 error_logger.error( |                 error_logger.error( | ||||||
|                     "\nThe error below might have caused the above one:\n" |                     "\nThe error below might have caused the above one:\n" f"{e.msg}" | ||||||
|                     f"{e.msg}" |  | ||||||
|                 ) |                 ) | ||||||
|                 sys.exit(1) |                 sys.exit(1) | ||||||
|             else: |             else: | ||||||
| @@ -196,7 +188,7 @@ Or, a path to a directory to run as a simple HTTP server: | |||||||
|         if self.args.tlshost: |         if self.args.tlshost: | ||||||
|             ssl.append(None) |             ssl.append(None) | ||||||
|         if self.args.cert is not None or self.args.key is not None: |         if self.args.cert is not None or self.args.key is not None: | ||||||
|             ssl.append(dict(cert=self.args.cert, key=self.args.key)) |             ssl.append({"cert": self.args.cert, "key": self.args.key}) | ||||||
|         if self.args.tls: |         if self.args.tls: | ||||||
|             ssl += self.args.tls |             ssl += self.args.tls | ||||||
|         if not ssl: |         if not ssl: | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| from argparse import ArgumentParser, _ArgumentGroup | from argparse import ArgumentParser, _ArgumentGroup | ||||||
| from typing import List, Optional, Type, Union |  | ||||||
|  |  | ||||||
| from sanic_routing import __version__ as __routing_version__ | from sanic_routing import __version__ as __routing_version__ | ||||||
|  |  | ||||||
| @@ -10,14 +9,14 @@ from sanic.http.constants import HTTP | |||||||
|  |  | ||||||
|  |  | ||||||
| class Group: | class Group: | ||||||
|     name: Optional[str] |     name: str | None | ||||||
|     container: Union[ArgumentParser, _ArgumentGroup] |     container: ArgumentParser | _ArgumentGroup | ||||||
|     _registry: List[Type[Group]] = [] |     _registry: list[type[Group]] = [] | ||||||
|  |  | ||||||
|     def __init_subclass__(cls) -> None: |     def __init_subclass__(cls) -> None: | ||||||
|         Group._registry.append(cls) |         Group._registry.append(cls) | ||||||
|  |  | ||||||
|     def __init__(self, parser: ArgumentParser, title: Optional[str]): |     def __init__(self, parser: ArgumentParser, title: str | None): | ||||||
|         self.parser = parser |         self.parser = parser | ||||||
|  |  | ||||||
|         if title: |         if title: | ||||||
| @@ -245,10 +244,7 @@ class DevelopmentGroup(Group): | |||||||
|             "--auto-reload", |             "--auto-reload", | ||||||
|             dest="auto_reload", |             dest="auto_reload", | ||||||
|             action="store_true", |             action="store_true", | ||||||
|             help=( |             help=("Watch source directory for file changes and reload on " "changes"), | ||||||
|                 "Watch source directory for file changes and reload on " |  | ||||||
|                 "changes" |  | ||||||
|             ), |  | ||||||
|         ) |         ) | ||||||
|         self.container.add_argument( |         self.container.add_argument( | ||||||
|             "-R", |             "-R", | ||||||
|   | |||||||
| @@ -1,10 +1,9 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| import sys | import sys | ||||||
|  |  | ||||||
| from http.client import RemoteDisconnected | from http.client import RemoteDisconnected | ||||||
| from textwrap import indent | from textwrap import indent | ||||||
| from typing import Any, Dict, Optional | from typing import Any | ||||||
| from urllib.error import URLError | from urllib.error import URLError | ||||||
| from urllib.request import Request as URequest | from urllib.request import Request as URequest | ||||||
| from urllib.request import urlopen | from urllib.request import urlopen | ||||||
| @@ -13,7 +12,6 @@ from sanic.application.logo import get_logo | |||||||
| from sanic.application.motd import MOTDTTY | from sanic.application.motd import MOTDTTY | ||||||
| from sanic.log import Colors | from sanic.log import Colors | ||||||
|  |  | ||||||
|  |  | ||||||
| try:  # no cov | try:  # no cov | ||||||
|     from ujson import dumps, loads |     from ujson import dumps, loads | ||||||
| except ModuleNotFoundError:  # no cov | except ModuleNotFoundError:  # no cov | ||||||
| @@ -27,7 +25,7 @@ class InspectorClient: | |||||||
|         port: int, |         port: int, | ||||||
|         secure: bool, |         secure: bool, | ||||||
|         raw: bool, |         raw: bool, | ||||||
|         api_key: Optional[str], |         api_key: str | None, | ||||||
|     ) -> None: |     ) -> None: | ||||||
|         self.scheme = "https" if secure else "http" |         self.scheme = "https" if secure else "http" | ||||||
|         self.host = host |         self.host = host | ||||||
| @@ -47,11 +45,7 @@ class InspectorClient: | |||||||
|             return |             return | ||||||
|         result = self.request(action, **kwargs).get("result") |         result = self.request(action, **kwargs).get("result") | ||||||
|         if result: |         if result: | ||||||
|             out = ( |             out = dumps(result) if isinstance(result, (list, dict)) else str(result) | ||||||
|                 dumps(result) |  | ||||||
|                 if isinstance(result, (list, dict)) |  | ||||||
|                 else str(result) |  | ||||||
|             ) |  | ||||||
|             sys.stdout.write(out + "\n") |             sys.stdout.write(out + "\n") | ||||||
|  |  | ||||||
|     def info(self) -> None: |     def info(self) -> None: | ||||||
| @@ -89,7 +83,7 @@ class InspectorClient: | |||||||
|  |  | ||||||
|     def request(self, action: str, method: str = "POST", **kwargs: Any) -> Any: |     def request(self, action: str, method: str = "POST", **kwargs: Any) -> Any: | ||||||
|         url = f"{self.base_url}/{action}" |         url = f"{self.base_url}/{action}" | ||||||
|         params: Dict[str, Any] = {"method": method, "headers": {}} |         params: dict[str, Any] = {"method": method, "headers": {}} | ||||||
|         if kwargs: |         if kwargs: | ||||||
|             params["data"] = dumps(kwargs).encode() |             params["data"] = dumps(kwargs).encode() | ||||||
|             params["headers"]["content-type"] = "application/json" |             params["headers"]["content-type"] = "application/json" | ||||||
|   | |||||||
| @@ -3,25 +3,16 @@ import os | |||||||
| import platform | import platform | ||||||
| import signal | import signal | ||||||
| import sys | import sys | ||||||
|  |  | ||||||
| from contextlib import contextmanager | from contextlib import contextmanager | ||||||
| from enum import Enum | from enum import Enum | ||||||
| from typing import Awaitable, Union | from typing import Awaitable, Literal, Union | ||||||
|  |  | ||||||
| from multidict import CIMultiDict  # type: ignore | from multidict import CIMultiDict  # type: ignore | ||||||
|  |  | ||||||
| from sanic.helpers import Default | from sanic.helpers import Default | ||||||
| from sanic.log import error_logger | from sanic.log import error_logger | ||||||
|  |  | ||||||
|  | StartMethod = Union[Default, Literal["fork"], Literal["forkserver"], Literal["spawn"]] | ||||||
| if sys.version_info < (3, 8):  # no cov |  | ||||||
|     StartMethod = Union[Default, str] |  | ||||||
| else:  # no cov |  | ||||||
|     from typing import Literal |  | ||||||
|  |  | ||||||
|     StartMethod = Union[ |  | ||||||
|         Default, Literal["fork"], Literal["forkserver"], Literal["spawn"] |  | ||||||
|     ] |  | ||||||
|  |  | ||||||
| OS_IS_WINDOWS = os.name == "nt" | OS_IS_WINDOWS = os.name == "nt" | ||||||
| PYPY_IMPLEMENTATION = platform.python_implementation() == "PyPy" | PYPY_IMPLEMENTATION = platform.python_implementation() == "PyPy" | ||||||
| @@ -142,7 +133,10 @@ if use_trio:  # pragma: no cover | |||||||
|         return trio.Path(path).stat() |         return trio.Path(path).stat() | ||||||
|  |  | ||||||
|     open_async = trio.open_file |     open_async = trio.open_file | ||||||
|     CancelledErrors = tuple([asyncio.CancelledError, trio.Cancelled]) |     CancelledErrors: tuple[type[BaseException], ...] = ( | ||||||
|  |         asyncio.CancelledError, | ||||||
|  |         trio.Cancelled, | ||||||
|  |     ) | ||||||
| else: | else: | ||||||
|     if PYPY_IMPLEMENTATION: |     if PYPY_IMPLEMENTATION: | ||||||
|         pypy_os_module_patch() |         pypy_os_module_patch() | ||||||
| @@ -156,7 +150,7 @@ else: | |||||||
|     async def open_async(file, mode="r", **kwargs): |     async def open_async(file, mode="r", **kwargs): | ||||||
|         return aio_open(file, mode, **kwargs) |         return aio_open(file, mode, **kwargs) | ||||||
|  |  | ||||||
|     CancelledErrors = tuple([asyncio.CancelledError]) |     CancelledErrors = (asyncio.CancelledError,) | ||||||
|  |  | ||||||
|  |  | ||||||
| def ctrlc_workaround_for_windows(app): | def ctrlc_workaround_for_windows(app): | ||||||
|   | |||||||
| @@ -1,12 +1,10 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| import sys |  | ||||||
|  |  | ||||||
| from abc import ABCMeta | from abc import ABCMeta | ||||||
| from inspect import getmembers, isclass, isdatadescriptor | from inspect import getmembers, isclass, isdatadescriptor | ||||||
| from os import environ | from os import environ | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from typing import Any, Callable, Dict, Optional, Sequence, Union | from typing import Any, Callable, Literal, Sequence, Union | ||||||
| from warnings import filterwarnings | from warnings import filterwarnings | ||||||
|  |  | ||||||
| from sanic.constants import LocalCertCreator | from sanic.constants import LocalCertCreator | ||||||
| @@ -16,10 +14,6 @@ from sanic.http import Http | |||||||
| from sanic.log import error_logger | from sanic.log import error_logger | ||||||
| from sanic.utils import load_module_from_file_location, str_to_bool | from sanic.utils import load_module_from_file_location, str_to_bool | ||||||
|  |  | ||||||
|  |  | ||||||
| if sys.version_info >= (3, 8): |  | ||||||
|     from typing import Literal |  | ||||||
|  |  | ||||||
| FilterWarningType = Union[ | FilterWarningType = Union[ | ||||||
|     Literal["default"], |     Literal["default"], | ||||||
|     Literal["error"], |     Literal["error"], | ||||||
| @@ -28,8 +22,6 @@ if sys.version_info >= (3, 8): | |||||||
|     Literal["module"], |     Literal["module"], | ||||||
|     Literal["once"], |     Literal["once"], | ||||||
| ] | ] | ||||||
| else: |  | ||||||
|     FilterWarningType = str |  | ||||||
|  |  | ||||||
| SANIC_PREFIX = "SANIC_" | SANIC_PREFIX = "SANIC_" | ||||||
|  |  | ||||||
| @@ -100,25 +92,25 @@ class Config(dict, metaclass=DescriptorMeta): | |||||||
|     EVENT_AUTOREGISTER: bool |     EVENT_AUTOREGISTER: bool | ||||||
|     DEPRECATION_FILTER: FilterWarningType |     DEPRECATION_FILTER: FilterWarningType | ||||||
|     FORWARDED_FOR_HEADER: str |     FORWARDED_FOR_HEADER: str | ||||||
|     FORWARDED_SECRET: Optional[str] |     FORWARDED_SECRET: str | None | ||||||
|     GRACEFUL_SHUTDOWN_TIMEOUT: float |     GRACEFUL_SHUTDOWN_TIMEOUT: float | ||||||
|     INSPECTOR: bool |     INSPECTOR: bool | ||||||
|     INSPECTOR_HOST: str |     INSPECTOR_HOST: str | ||||||
|     INSPECTOR_PORT: int |     INSPECTOR_PORT: int | ||||||
|     INSPECTOR_TLS_KEY: Union[Path, str, Default] |     INSPECTOR_TLS_KEY: Path | str | Default | ||||||
|     INSPECTOR_TLS_CERT: Union[Path, str, Default] |     INSPECTOR_TLS_CERT: Path | str | Default | ||||||
|     INSPECTOR_API_KEY: str |     INSPECTOR_API_KEY: str | ||||||
|     KEEP_ALIVE_TIMEOUT: int |     KEEP_ALIVE_TIMEOUT: int | ||||||
|     KEEP_ALIVE: bool |     KEEP_ALIVE: bool | ||||||
|     LOCAL_CERT_CREATOR: Union[str, LocalCertCreator] |     LOCAL_CERT_CREATOR: str | LocalCertCreator | ||||||
|     LOCAL_TLS_KEY: Union[Path, str, Default] |     LOCAL_TLS_KEY: Path | str | Default | ||||||
|     LOCAL_TLS_CERT: Union[Path, str, Default] |     LOCAL_TLS_CERT: Path | str | Default | ||||||
|     LOCALHOST: str |     LOCALHOST: str | ||||||
|     MOTD: bool |     MOTD: bool | ||||||
|     MOTD_DISPLAY: Dict[str, str] |     MOTD_DISPLAY: dict[str, str] | ||||||
|     NOISY_EXCEPTIONS: bool |     NOISY_EXCEPTIONS: bool | ||||||
|     PROXIES_COUNT: Optional[int] |     PROXIES_COUNT: int | None | ||||||
|     REAL_IP_HEADER: Optional[str] |     REAL_IP_HEADER: str | None | ||||||
|     REQUEST_BUFFER_SIZE: int |     REQUEST_BUFFER_SIZE: int | ||||||
|     REQUEST_MAX_HEADER_SIZE: int |     REQUEST_MAX_HEADER_SIZE: int | ||||||
|     REQUEST_ID_HEADER: str |     REQUEST_ID_HEADER: str | ||||||
| @@ -127,21 +119,19 @@ class Config(dict, metaclass=DescriptorMeta): | |||||||
|     RESPONSE_TIMEOUT: int |     RESPONSE_TIMEOUT: int | ||||||
|     SERVER_NAME: str |     SERVER_NAME: str | ||||||
|     TLS_CERT_PASSWORD: str |     TLS_CERT_PASSWORD: str | ||||||
|     TOUCHUP: Union[Default, bool] |     TOUCHUP: Default | bool | ||||||
|     USE_UVLOOP: Union[Default, bool] |     USE_UVLOOP: Default | bool | ||||||
|     WEBSOCKET_MAX_SIZE: int |     WEBSOCKET_MAX_SIZE: int | ||||||
|     WEBSOCKET_PING_INTERVAL: int |     WEBSOCKET_PING_INTERVAL: int | ||||||
|     WEBSOCKET_PING_TIMEOUT: int |     WEBSOCKET_PING_TIMEOUT: int | ||||||
|  |  | ||||||
|     def __init__( |     def __init__( | ||||||
|         self, |         self, | ||||||
|         defaults: Optional[ |         defaults: dict[str, str | bool | int | float | None] | None = None, | ||||||
|             Dict[str, Union[str, bool, int, float, None]] |         env_prefix: str | None = SANIC_PREFIX, | ||||||
|         ] = None, |         keep_alive: bool | None = None, | ||||||
|         env_prefix: Optional[str] = SANIC_PREFIX, |  | ||||||
|         keep_alive: Optional[bool] = None, |  | ||||||
|         *, |         *, | ||||||
|         converters: Optional[Sequence[Callable[[str], Any]]] = None, |         converters: Sequence[Callable[[str], Any]] | None = None, | ||||||
|     ): |     ): | ||||||
|         defaults = defaults or {} |         defaults = defaults or {} | ||||||
|         super().__init__({**DEFAULT_CONFIG, **defaults}) |         super().__init__({**DEFAULT_CONFIG, **defaults}) | ||||||
| @@ -209,7 +199,7 @@ class Config(dict, metaclass=DescriptorMeta): | |||||||
|             ``` |             ``` | ||||||
|         """ |         """ | ||||||
|         kwargs.update({k: v for item in other for k, v in dict(item).items()}) |         kwargs.update({k: v for item in other for k, v in dict(item).items()}) | ||||||
|         setters: Dict[str, Any] = { |         setters: dict[str, Any] = { | ||||||
|             k: kwargs.pop(k) |             k: kwargs.pop(k) | ||||||
|             for k in {**kwargs}.keys() |             for k in {**kwargs}.keys() | ||||||
|             if k in self.__class__.__setters__ |             if k in self.__class__.__setters__ | ||||||
| @@ -237,9 +227,7 @@ class Config(dict, metaclass=DescriptorMeta): | |||||||
|         if attr == "LOCAL_CERT_CREATOR" and not isinstance( |         if attr == "LOCAL_CERT_CREATOR" and not isinstance( | ||||||
|             self.LOCAL_CERT_CREATOR, LocalCertCreator |             self.LOCAL_CERT_CREATOR, LocalCertCreator | ||||||
|         ): |         ): | ||||||
|             self.LOCAL_CERT_CREATOR = LocalCertCreator[ |             self.LOCAL_CERT_CREATOR = LocalCertCreator[self.LOCAL_CERT_CREATOR.upper()] | ||||||
|                 self.LOCAL_CERT_CREATOR.upper() |  | ||||||
|             ] |  | ||||||
|         elif attr == "DEPRECATION_FILTER": |         elif attr == "DEPRECATION_FILTER": | ||||||
|             self._configure_warnings() |             self._configure_warnings() | ||||||
|  |  | ||||||
| @@ -276,7 +264,7 @@ class Config(dict, metaclass=DescriptorMeta): | |||||||
|             module=r"sanic.*", |             module=r"sanic.*", | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def _check_error_format(self, format: Optional[str] = None): |     def _check_error_format(self, format: str | None = None): | ||||||
|         check_error_format(format or self.FALLBACK_ERROR_FORMAT) |         check_error_format(format or self.FALLBACK_ERROR_FORMAT) | ||||||
|  |  | ||||||
|     def load_environment_vars(self, prefix=SANIC_PREFIX): |     def load_environment_vars(self, prefix=SANIC_PREFIX): | ||||||
| @@ -332,7 +320,7 @@ class Config(dict, metaclass=DescriptorMeta): | |||||||
|                 except ValueError: |                 except ValueError: | ||||||
|                     pass |                     pass | ||||||
|  |  | ||||||
|     def update_config(self, config: Union[bytes, str, dict, Any]): |     def update_config(self, config: bytes | str | dict | Any): | ||||||
|         """Update app.config. |         """Update app.config. | ||||||
|  |  | ||||||
|         .. note:: |         .. note:: | ||||||
|   | |||||||
| @@ -1,4 +1,3 @@ | |||||||
| from .response import Cookie, CookieJar | from .response import Cookie, CookieJar | ||||||
|  |  | ||||||
|  |  | ||||||
| __all__ = ("Cookie", "CookieJar") | __all__ = ("Cookie", "CookieJar") | ||||||
|   | |||||||
| @@ -1,12 +1,10 @@ | |||||||
| import re | import re | ||||||
|  |  | ||||||
| from typing import Any, Dict, List, Optional | from typing import Any, Dict, List, Optional | ||||||
|  |  | ||||||
| from sanic.cookies.response import Cookie | from sanic.cookies.response import Cookie | ||||||
| from sanic.log import deprecation | from sanic.log import deprecation | ||||||
| from sanic.request.parameters import RequestParameters | from sanic.request.parameters import RequestParameters | ||||||
|  |  | ||||||
|  |  | ||||||
| COOKIE_NAME_RESERVED_CHARS = re.compile( | COOKIE_NAME_RESERVED_CHARS = re.compile( | ||||||
|     '[\x00-\x1F\x7F-\xFF()<>@,;:\\\\"/[\\]?={} \x09]' |     '[\x00-\x1F\x7F-\xFF()<>@,;:\\\\"/[\\]?={} \x09]' | ||||||
| ) | ) | ||||||
| @@ -149,9 +147,7 @@ class CookieRequestParameters(RequestParameters): | |||||||
|         except KeyError: |         except KeyError: | ||||||
|             return super().get(name, default) |             return super().get(name, default) | ||||||
|  |  | ||||||
|     def getlist( |     def getlist(self, name: str, default: Optional[Any] = None) -> Optional[Any]: | ||||||
|         self, name: str, default: Optional[Any] = None |  | ||||||
|     ) -> Optional[Any]: |  | ||||||
|         try: |         try: | ||||||
|             return self._get_prefixed_cookie(name) |             return self._get_prefixed_cookie(name) | ||||||
|         except KeyError: |         except KeyError: | ||||||
|   | |||||||
| @@ -2,21 +2,15 @@ from __future__ import annotations | |||||||
|  |  | ||||||
| import re | import re | ||||||
| import string | import string | ||||||
| import sys |  | ||||||
|  |  | ||||||
| from datetime import datetime | from datetime import datetime | ||||||
| from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union | from typing import TYPE_CHECKING, Any, Union | ||||||
|  |  | ||||||
| from sanic.exceptions import ServerError | from sanic.exceptions import ServerError | ||||||
| from sanic.log import deprecation | from sanic.log import deprecation | ||||||
|  |  | ||||||
|  |  | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     from sanic.compat import Header |     from sanic.compat import Header | ||||||
|  |  | ||||||
| if sys.version_info < (3, 8):  # no cov |  | ||||||
|     SameSite = str |  | ||||||
| else:  # no cov |  | ||||||
| from typing import Literal | from typing import Literal | ||||||
|  |  | ||||||
| SameSite = Union[ | SameSite = Union[ | ||||||
| @@ -180,7 +174,7 @@ class CookieJar(dict): | |||||||
|         return CookieJar.HEADER_KEY |         return CookieJar.HEADER_KEY | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def cookie_headers(self) -> Dict[str, str]:  # no cov |     def cookie_headers(self) -> dict[str, str]:  # no cov | ||||||
|         """Deprecated in v24.3""" |         """Deprecated in v24.3""" | ||||||
|         deprecation( |         deprecation( | ||||||
|             "The CookieJar.coookie_headers property has been deprecated " |             "The CookieJar.coookie_headers property has been deprecated " | ||||||
| @@ -191,7 +185,7 @@ class CookieJar(dict): | |||||||
|         return {key: self.header_key for key in self} |         return {key: self.header_key for key in self} | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def cookies(self) -> List[Cookie]: |     def cookies(self) -> list[Cookie]: | ||||||
|         """A list of cookies in the CookieJar. |         """A list of cookies in the CookieJar. | ||||||
|  |  | ||||||
|         Returns: |         Returns: | ||||||
| @@ -203,10 +197,10 @@ class CookieJar(dict): | |||||||
|         self, |         self, | ||||||
|         key: str, |         key: str, | ||||||
|         path: str = "/", |         path: str = "/", | ||||||
|         domain: Optional[str] = None, |         domain: str | None = None, | ||||||
|         host_prefix: bool = False, |         host_prefix: bool = False, | ||||||
|         secure_prefix: bool = False, |         secure_prefix: bool = False, | ||||||
|     ) -> Optional[Cookie]: |     ) -> Cookie | None: | ||||||
|         """Fetch a cookie from the CookieJar. |         """Fetch a cookie from the CookieJar. | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
| @@ -236,7 +230,7 @@ class CookieJar(dict): | |||||||
|         self, |         self, | ||||||
|         key: str, |         key: str, | ||||||
|         path: str = "/", |         path: str = "/", | ||||||
|         domain: Optional[str] = None, |         domain: str | None = None, | ||||||
|         host_prefix: bool = False, |         host_prefix: bool = False, | ||||||
|         secure_prefix: bool = False, |         secure_prefix: bool = False, | ||||||
|     ) -> bool: |     ) -> bool: | ||||||
| @@ -271,14 +265,14 @@ class CookieJar(dict): | |||||||
|         value: str, |         value: str, | ||||||
|         *, |         *, | ||||||
|         path: str = "/", |         path: str = "/", | ||||||
|         domain: Optional[str] = None, |         domain: str | None = None, | ||||||
|         secure: bool = True, |         secure: bool = True, | ||||||
|         max_age: Optional[int] = None, |         max_age: int | None = None, | ||||||
|         expires: Optional[datetime] = None, |         expires: datetime | None = None, | ||||||
|         httponly: bool = False, |         httponly: bool = False, | ||||||
|         samesite: Optional[SameSite] = "Lax", |         samesite: SameSite | None = "Lax", | ||||||
|         partitioned: bool = False, |         partitioned: bool = False, | ||||||
|         comment: Optional[str] = None, |         comment: str | None = None, | ||||||
|         host_prefix: bool = False, |         host_prefix: bool = False, | ||||||
|         secure_prefix: bool = False, |         secure_prefix: bool = False, | ||||||
|     ) -> Cookie: |     ) -> Cookie: | ||||||
| @@ -362,7 +356,7 @@ class CookieJar(dict): | |||||||
|         key: str, |         key: str, | ||||||
|         *, |         *, | ||||||
|         path: str = "/", |         path: str = "/", | ||||||
|         domain: Optional[str] = None, |         domain: str | None = None, | ||||||
|         host_prefix: bool = False, |         host_prefix: bool = False, | ||||||
|         secure_prefix: bool = False, |         secure_prefix: bool = False, | ||||||
|     ) -> None: |     ) -> None: | ||||||
| @@ -390,7 +384,7 @@ class CookieJar(dict): | |||||||
|         :type secure_prefix: bool |         :type secure_prefix: bool | ||||||
|         """ |         """ | ||||||
|         # remove it from header |         # remove it from header | ||||||
|         cookies: List[Cookie] = self.headers.popall(self.HEADER_KEY, []) |         cookies: list[Cookie] = self.headers.popall(self.HEADER_KEY, []) | ||||||
|         for cookie in cookies: |         for cookie in cookies: | ||||||
|             if ( |             if ( | ||||||
|                 cookie.key != Cookie.make_key(key, host_prefix, secure_prefix) |                 cookie.key != Cookie.make_key(key, host_prefix, secure_prefix) | ||||||
| @@ -481,14 +475,14 @@ class Cookie(dict): | |||||||
|         value: str, |         value: str, | ||||||
|         *, |         *, | ||||||
|         path: str = "/", |         path: str = "/", | ||||||
|         domain: Optional[str] = None, |         domain: str | None = None, | ||||||
|         secure: bool = True, |         secure: bool = True, | ||||||
|         max_age: Optional[int] = None, |         max_age: int | None = None, | ||||||
|         expires: Optional[datetime] = None, |         expires: datetime | None = None, | ||||||
|         httponly: bool = False, |         httponly: bool = False, | ||||||
|         samesite: Optional[SameSite] = "Lax", |         samesite: SameSite | None = "Lax", | ||||||
|         partitioned: bool = False, |         partitioned: bool = False, | ||||||
|         comment: Optional[str] = None, |         comment: str | None = None, | ||||||
|         host_prefix: bool = False, |         host_prefix: bool = False, | ||||||
|         secure_prefix: bool = False, |         secure_prefix: bool = False, | ||||||
|     ): |     ): | ||||||
| @@ -502,9 +496,7 @@ class Cookie(dict): | |||||||
|                     "Cannot set host_prefix on a cookie without secure=True" |                     "Cannot set host_prefix on a cookie without secure=True" | ||||||
|                 ) |                 ) | ||||||
|             if path != "/": |             if path != "/": | ||||||
|                 raise ServerError( |                 raise ServerError("Cannot set host_prefix on a cookie unless path='/'") | ||||||
|                     "Cannot set host_prefix on a cookie unless path='/'" |  | ||||||
|                 ) |  | ||||||
|             if domain: |             if domain: | ||||||
|                 raise ServerError( |                 raise ServerError( | ||||||
|                     "Cannot set host_prefix on a cookie with a defined domain" |                     "Cannot set host_prefix on a cookie with a defined domain" | ||||||
| @@ -561,7 +553,7 @@ class Cookie(dict): | |||||||
|     # in v24.3 when this is no longer a dict |     # in v24.3 when this is no longer a dict | ||||||
|     def _set_value(self, key: str, value: Any) -> None: |     def _set_value(self, key: str, value: Any) -> None: | ||||||
|         if key not in self._keys: |         if key not in self._keys: | ||||||
|             raise KeyError("Unknown cookie property: %s=%s" % (key, value)) |             raise KeyError(f"Unknown cookie property: {key}={value}") | ||||||
|  |  | ||||||
|         if value is not None: |         if value is not None: | ||||||
|             if key.lower() == "max-age" and not str(value).isdigit(): |             if key.lower() == "max-age" and not str(value).isdigit(): | ||||||
| @@ -604,21 +596,18 @@ class Cookie(dict): | |||||||
|  |  | ||||||
|     def __str__(self): |     def __str__(self): | ||||||
|         """Format as a Set-Cookie header value.""" |         """Format as a Set-Cookie header value.""" | ||||||
|         output = ["%s=%s" % (self.key, _quote(self.value))] |         output = [f"{self.key}={_quote(self.value)}"] | ||||||
|         key_index = list(self._keys) |         key_index = list(self._keys) | ||||||
|         for key, value in sorted( |         for key, value in sorted(self.items(), key=lambda x: key_index.index(x[0])): | ||||||
|             self.items(), key=lambda x: key_index.index(x[0]) |  | ||||||
|         ): |  | ||||||
|             if value is not None and value is not False: |             if value is not None and value is not False: | ||||||
|                 if key == "max-age": |                 if key == "max-age": | ||||||
|                     try: |                     try: | ||||||
|                         output.append("%s=%d" % (self._keys[key], value)) |                         output.append("%s=%d" % (self._keys[key], value)) | ||||||
|                     except TypeError: |                     except TypeError: | ||||||
|                         output.append("%s=%s" % (self._keys[key], value)) |                         output.append(f"{self._keys[key]}={value}") | ||||||
|                 elif key == "expires": |                 elif key == "expires": | ||||||
|                     output.append( |                     output.append( | ||||||
|                         "%s=%s" |                         "{}={}".format( | ||||||
|                         % ( |  | ||||||
|                             self._keys[key], |                             self._keys[key], | ||||||
|                             value.strftime("%a, %d-%b-%Y %T GMT"), |                             value.strftime("%a, %d-%b-%Y %T GMT"), | ||||||
|                         ) |                         ) | ||||||
| @@ -626,7 +615,7 @@ class Cookie(dict): | |||||||
|                 elif key in self._flags: |                 elif key in self._flags: | ||||||
|                     output.append(self._keys[key]) |                     output.append(self._keys[key]) | ||||||
|                 else: |                 else: | ||||||
|                     output.append("%s=%s" % (self._keys[key], value)) |                     output.append(f"{self._keys[key]}={value}") | ||||||
|  |  | ||||||
|         return "; ".join(output) |         return "; ".join(output) | ||||||
|  |  | ||||||
| @@ -640,7 +629,7 @@ class Cookie(dict): | |||||||
|         self._set_value("path", value) |         self._set_value("path", value) | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def expires(self) -> Optional[datetime]:  # no cov |     def expires(self) -> datetime | None:  # no cov | ||||||
|         """The expiration date of the cookie. Defaults to `None`.""" |         """The expiration date of the cookie. Defaults to `None`.""" | ||||||
|         return self.get("expires") |         return self.get("expires") | ||||||
|  |  | ||||||
| @@ -649,7 +638,7 @@ class Cookie(dict): | |||||||
|         self._set_value("expires", value) |         self._set_value("expires", value) | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def comment(self) -> Optional[str]:  # no cov |     def comment(self) -> str | None:  # no cov | ||||||
|         """A comment for the cookie. Defaults to `None`.""" |         """A comment for the cookie. Defaults to `None`.""" | ||||||
|         return self.get("comment") |         return self.get("comment") | ||||||
|  |  | ||||||
| @@ -658,7 +647,7 @@ class Cookie(dict): | |||||||
|         self._set_value("comment", value) |         self._set_value("comment", value) | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def domain(self) -> Optional[str]:  # no cov |     def domain(self) -> str | None:  # no cov | ||||||
|         """The domain of the cookie. Defaults to `None`.""" |         """The domain of the cookie. Defaults to `None`.""" | ||||||
|         return self.get("domain") |         return self.get("domain") | ||||||
|  |  | ||||||
| @@ -667,7 +656,7 @@ class Cookie(dict): | |||||||
|         self._set_value("domain", value) |         self._set_value("domain", value) | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def max_age(self) -> Optional[int]:  # no cov |     def max_age(self) -> int | None:  # no cov | ||||||
|         """The maximum age of the cookie in seconds. Defaults to `None`.""" |         """The maximum age of the cookie in seconds. Defaults to `None`.""" | ||||||
|         return self.get("max-age") |         return self.get("max-age") | ||||||
|  |  | ||||||
| @@ -694,7 +683,7 @@ class Cookie(dict): | |||||||
|         self._set_value("httponly", value) |         self._set_value("httponly", value) | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def samesite(self) -> Optional[SameSite]:  # no cov |     def samesite(self) -> SameSite | None:  # no cov | ||||||
|         """The SameSite attribute for the cookie. Defaults to `"Lax"`.""" |         """The SameSite attribute for the cookie. Defaults to `"Lax"`.""" | ||||||
|         return self.get("samesite") |         return self.get("samesite") | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,7 +16,6 @@ from __future__ import annotations | |||||||
|  |  | ||||||
| import sys | import sys | ||||||
| import typing as t | import typing as t | ||||||
|  |  | ||||||
| from functools import partial | from functools import partial | ||||||
| from traceback import extract_tb | from traceback import extract_tb | ||||||
|  |  | ||||||
| @@ -26,7 +25,6 @@ from sanic.log import deprecation, logger | |||||||
| from sanic.pages.error import ErrorPage | from sanic.pages.error import ErrorPage | ||||||
| from sanic.response import html, json, text | from sanic.response import html, json, text | ||||||
|  |  | ||||||
|  |  | ||||||
| dumps: t.Callable[..., str] | dumps: t.Callable[..., str] | ||||||
| try: | try: | ||||||
|     from ujson import dumps |     from ujson import dumps | ||||||
| @@ -73,7 +71,7 @@ class BaseRenderer: | |||||||
|         self.debug = debug |         self.debug = debug | ||||||
|  |  | ||||||
|     @property |     @property | ||||||
|     def headers(self) -> t.Dict[str, str]: |     def headers(self) -> dict[str, str]: | ||||||
|         """The headers to be used for the response.""" |         """The headers to be used for the response.""" | ||||||
|         if isinstance(self.exception, SanicException): |         if isinstance(self.exception, SanicException): | ||||||
|             return getattr(self.exception, "headers", {}) |             return getattr(self.exception, "headers", {}) | ||||||
| @@ -192,8 +190,7 @@ class TextRenderer(BaseRenderer): | |||||||
|             lines += [ |             lines += [ | ||||||
|                 f"{self.exception.__class__.__name__}: {self.exception} while " |                 f"{self.exception.__class__.__name__}: {self.exception} while " | ||||||
|                 f"handling path {self.request.path}", |                 f"handling path {self.request.path}", | ||||||
|                 f"Traceback of {self.request.app.name} " |                 f"Traceback of {self.request.app.name} " "(most recent call last):\n", | ||||||
|                 "(most recent call last):\n", |  | ||||||
|             ] |             ] | ||||||
|  |  | ||||||
|             while exc_value: |             while exc_value: | ||||||
| @@ -326,8 +323,8 @@ def exception_response( | |||||||
|     exception: Exception, |     exception: Exception, | ||||||
|     debug: bool, |     debug: bool, | ||||||
|     fallback: str, |     fallback: str, | ||||||
|     base: t.Type[BaseRenderer], |     base: type[BaseRenderer], | ||||||
|     renderer: t.Optional[t.Type[BaseRenderer]] = None, |     renderer: type[BaseRenderer] | None = None, | ||||||
| ) -> HTTPResponse: | ) -> HTTPResponse: | ||||||
|     """Render a response for the default FALLBACK exception handler.""" |     """Render a response for the default FALLBACK exception handler.""" | ||||||
|     if not renderer: |     if not renderer: | ||||||
| @@ -390,9 +387,7 @@ def guess_mime(req: Request, fallback: str) -> str: | |||||||
|     if m: |     if m: | ||||||
|         format = CONFIG_BY_MIME[m.mime] |         format = CONFIG_BY_MIME[m.mime] | ||||||
|         source = formats[format] |         source = formats[format] | ||||||
|         logger.debug( |         logger.debug(f"The client accepts {m.header}, using '{format}' from {source}") | ||||||
|             f"The client accepts {m.header}, using '{format}' from {source}" |  | ||||||
|         ) |  | ||||||
|     else: |     else: | ||||||
|         logger.debug(f"No format found, the client accepts {req.accept!r}") |         logger.debug(f"No format found, the client accepts {req.accept!r}") | ||||||
|     return m.mime |     return m.mime | ||||||
|   | |||||||
| @@ -69,9 +69,7 @@ class SanicException(Exception): | |||||||
|     ) -> None: |     ) -> None: | ||||||
|         self.context = context |         self.context = context | ||||||
|         self.extra = extra |         self.extra = extra | ||||||
|         status_code = status_code or getattr( |         status_code = status_code or getattr(self.__class__, "status_code", None) | ||||||
|             self.__class__, "status_code", None |  | ||||||
|         ) |  | ||||||
|         quiet = quiet or getattr(self.__class__, "quiet", None) |         quiet = quiet or getattr(self.__class__, "quiet", None) | ||||||
|         headers = headers or getattr(self.__class__, "headers", {}) |         headers = headers or getattr(self.__class__, "headers", {}) | ||||||
|         if message is None: |         if message is None: | ||||||
| @@ -621,9 +619,7 @@ class Unauthorized(HTTPException): | |||||||
|  |  | ||||||
|         # if auth-scheme is specified, set "WWW-Authenticate" header |         # if auth-scheme is specified, set "WWW-Authenticate" header | ||||||
|         if scheme is not None: |         if scheme is not None: | ||||||
|             values = [ |             values = [f'{k!s}="{v!s}"' for k, v in challenges.items()] | ||||||
|                 '{!s}="{!s}"'.format(k, v) for k, v in challenges.items() |  | ||||||
|             ] |  | ||||||
|             challenge = ", ".join(values) |             challenge = ", ".join(values) | ||||||
|  |  | ||||||
|             self.headers = { |             self.headers = { | ||||||
|   | |||||||
| @@ -2,7 +2,6 @@ from .content_range import ContentRangeHandler | |||||||
| from .directory import DirectoryHandler | from .directory import DirectoryHandler | ||||||
| from .error import ErrorHandler | from .error import ErrorHandler | ||||||
|  |  | ||||||
|  |  | ||||||
| __all__ = ( | __all__ = ( | ||||||
|     "ContentRangeHandler", |     "ContentRangeHandler", | ||||||
|     "DirectoryHandler", |     "DirectoryHandler", | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| import os | import os | ||||||
|  |  | ||||||
| from typing import TYPE_CHECKING | from typing import TYPE_CHECKING | ||||||
|  |  | ||||||
| from sanic.exceptions import ( | from sanic.exceptions import ( | ||||||
| @@ -11,7 +10,6 @@ from sanic.exceptions import ( | |||||||
| ) | ) | ||||||
| from sanic.models.protocol_types import Range | from sanic.models.protocol_types import Range | ||||||
|  |  | ||||||
|  |  | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     from sanic import Request |     from sanic import Request | ||||||
|  |  | ||||||
| @@ -33,27 +31,19 @@ class ContentRangeHandler(Range): | |||||||
|             raise HeaderNotFound("Range Header Not Found") |             raise HeaderNotFound("Range Header Not Found") | ||||||
|         unit, _, value = tuple(map(str.strip, _range.partition("="))) |         unit, _, value = tuple(map(str.strip, _range.partition("="))) | ||||||
|         if unit != "bytes": |         if unit != "bytes": | ||||||
|             raise InvalidRangeType( |             raise InvalidRangeType(f"{unit} is not a valid Range Type", self) | ||||||
|                 "%s is not a valid Range Type" % (unit,), self |  | ||||||
|             ) |  | ||||||
|         start_b, _, end_b = tuple(map(str.strip, value.partition("-"))) |         start_b, _, end_b = tuple(map(str.strip, value.partition("-"))) | ||||||
|         try: |         try: | ||||||
|             self.start = int(start_b) if start_b else None |             self.start = int(start_b) if start_b else None | ||||||
|         except ValueError: |         except ValueError: | ||||||
|             raise RangeNotSatisfiable( |             raise RangeNotSatisfiable(f"'{start_b}' is invalid for Content Range", self) | ||||||
|                 "'%s' is invalid for Content Range" % (start_b,), self |  | ||||||
|             ) |  | ||||||
|         try: |         try: | ||||||
|             self.end = int(end_b) if end_b else None |             self.end = int(end_b) if end_b else None | ||||||
|         except ValueError: |         except ValueError: | ||||||
|             raise RangeNotSatisfiable( |             raise RangeNotSatisfiable(f"'{end_b}' is invalid for Content Range", self) | ||||||
|                 "'%s' is invalid for Content Range" % (end_b,), self |  | ||||||
|             ) |  | ||||||
|         if self.end is None: |         if self.end is None: | ||||||
|             if self.start is None: |             if self.start is None: | ||||||
|                 raise RangeNotSatisfiable( |                 raise RangeNotSatisfiable("Invalid for Content Range parameters", self) | ||||||
|                     "Invalid for Content Range parameters", self |  | ||||||
|                 ) |  | ||||||
|             else: |             else: | ||||||
|                 # this case represents `Content-Range: bytes 5-` |                 # this case represents `Content-Range: bytes 5-` | ||||||
|                 self.end = self.total - 1 |                 self.end = self.total - 1 | ||||||
| @@ -63,14 +53,9 @@ class ContentRangeHandler(Range): | |||||||
|                 self.start = self.total - self.end |                 self.start = self.total - self.end | ||||||
|                 self.end = self.total - 1 |                 self.end = self.total - 1 | ||||||
|         if self.start >= self.end: |         if self.start >= self.end: | ||||||
|             raise RangeNotSatisfiable( |             raise RangeNotSatisfiable("Invalid for Content Range parameters", self) | ||||||
|                 "Invalid for Content Range parameters", self |  | ||||||
|             ) |  | ||||||
|         self.size = self.end - self.start + 1 |         self.size = self.end - self.start + 1 | ||||||
|         self.headers = { |         self.headers = {"Content-Range": f"bytes {self.start}-{self.end}/{self.total}"} | ||||||
|             "Content-Range": "bytes %s-%s/%s" |  | ||||||
|             % (self.start, self.end, self.total) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|     def __bool__(self): |     def __bool__(self): | ||||||
|         return hasattr(self, "size") and self.size > 0 |         return hasattr(self, "size") and self.size > 0 | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ from datetime import datetime | |||||||
| from operator import itemgetter | from operator import itemgetter | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from stat import S_ISDIR | from stat import S_ISDIR | ||||||
| from typing import Dict, Iterable, Optional, Sequence, Union, cast | from typing import Iterable, Sequence, cast | ||||||
|  |  | ||||||
| from sanic.exceptions import NotFound | from sanic.exceptions import NotFound | ||||||
| from sanic.pages.directory_page import DirectoryPage, FileInfo | from sanic.pages.directory_page import DirectoryPage, FileInfo | ||||||
| @@ -28,7 +28,7 @@ class DirectoryHandler: | |||||||
|         uri: str, |         uri: str, | ||||||
|         directory: Path, |         directory: Path, | ||||||
|         directory_view: bool = False, |         directory_view: bool = False, | ||||||
|         index: Optional[Union[str, Sequence[str]]] = None, |         index: str | Sequence[str] | None = None, | ||||||
|     ) -> None: |     ) -> None: | ||||||
|         if isinstance(index, str): |         if isinstance(index, str): | ||||||
|             index = [index] |             index = [index] | ||||||
| @@ -60,9 +60,7 @@ class DirectoryHandler: | |||||||
|                 return await file(index_file) |                 return await file(index_file) | ||||||
|  |  | ||||||
|         if self.directory_view: |         if self.directory_view: | ||||||
|             return self._index( |             return self._index(self.directory / current, path, request.app.debug) | ||||||
|                 self.directory / current, path, request.app.debug |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|         if self.index: |         if self.index: | ||||||
|             raise NotFound("File not found") |             raise NotFound("File not found") | ||||||
| @@ -72,20 +70,16 @@ class DirectoryHandler: | |||||||
|     def _index(self, location: Path, path: str, debug: bool): |     def _index(self, location: Path, path: str, debug: bool): | ||||||
|         # Remove empty path elements, append slash |         # Remove empty path elements, append slash | ||||||
|         if "//" in path or not path.endswith("/"): |         if "//" in path or not path.endswith("/"): | ||||||
|             return redirect( |             return redirect("/" + "".join([f"{p}/" for p in path.split("/") if p])) | ||||||
|                 "/" + "".join([f"{p}/" for p in path.split("/") if p]) |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|         # Render file browser |         # Render file browser | ||||||
|         page = DirectoryPage(self._iter_files(location), path, debug) |         page = DirectoryPage(self._iter_files(location), path, debug) | ||||||
|         return html(page.render()) |         return html(page.render()) | ||||||
|  |  | ||||||
|     def _prepare_file(self, path: Path) -> Dict[str, Union[int, str]]: |     def _prepare_file(self, path: Path) -> dict[str, int | str]: | ||||||
|         stat = path.stat() |         stat = path.stat() | ||||||
|         modified = ( |         modified = ( | ||||||
|             datetime.fromtimestamp(stat.st_mtime) |             datetime.fromtimestamp(stat.st_mtime).isoformat()[:19].replace("T", " ") | ||||||
|             .isoformat()[:19] |  | ||||||
|             .replace("T", " ") |  | ||||||
|         ) |         ) | ||||||
|         is_dir = S_ISDIR(stat.st_mode) |         is_dir = S_ISDIR(stat.st_mode) | ||||||
|         icon = "📁" if is_dir else "📄" |         icon = "📁" if is_dir else "📄" | ||||||
|   | |||||||
| @@ -1,7 +1,5 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| from typing import Dict, List, Optional, Tuple, Type |  | ||||||
|  |  | ||||||
| from sanic.errorpages import BaseRenderer, TextRenderer, exception_response | from sanic.errorpages import BaseRenderer, TextRenderer, exception_response | ||||||
| from sanic.exceptions import ServerError | from sanic.exceptions import ServerError | ||||||
| from sanic.log import error_logger | from sanic.log import error_logger | ||||||
| @@ -25,20 +23,20 @@ class ErrorHandler: | |||||||
|  |  | ||||||
|     def __init__( |     def __init__( | ||||||
|         self, |         self, | ||||||
|         base: Type[BaseRenderer] = TextRenderer, |         base: type[BaseRenderer] = TextRenderer, | ||||||
|     ): |     ): | ||||||
|         self.cached_handlers: Dict[ |         self.cached_handlers: dict[ | ||||||
|             Tuple[Type[BaseException], Optional[str]], Optional[RouteHandler] |             tuple[type[BaseException], str | None], RouteHandler | None | ||||||
|         ] = {} |         ] = {} | ||||||
|         self.debug = False |         self.debug = False | ||||||
|         self.base = base |         self.base = base | ||||||
|  |  | ||||||
|     def _full_lookup(self, exception, route_name: Optional[str] = None): |     def _full_lookup(self, exception, route_name: str | None = None): | ||||||
|         return self.lookup(exception, route_name) |         return self.lookup(exception, route_name) | ||||||
|  |  | ||||||
|     def _add( |     def _add( | ||||||
|         self, |         self, | ||||||
|         key: Tuple[Type[BaseException], Optional[str]], |         key: tuple[type[BaseException], str | None], | ||||||
|         handler: RouteHandler, |         handler: RouteHandler, | ||||||
|     ) -> None: |     ) -> None: | ||||||
|         if key in self.cached_handlers: |         if key in self.cached_handlers: | ||||||
| @@ -53,7 +51,7 @@ class ErrorHandler: | |||||||
|             raise ServerError(message) |             raise ServerError(message) | ||||||
|         self.cached_handlers[key] = handler |         self.cached_handlers[key] = handler | ||||||
|  |  | ||||||
|     def add(self, exception, handler, route_names: Optional[List[str]] = None): |     def add(self, exception, handler, route_names: list[str] | None = None): | ||||||
|         """Add a new exception handler to an already existing handler object. |         """Add a new exception handler to an already existing handler object. | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
| @@ -72,7 +70,7 @@ class ErrorHandler: | |||||||
|         else: |         else: | ||||||
|             self._add((exception, None), handler) |             self._add((exception, None), handler) | ||||||
|  |  | ||||||
|     def lookup(self, exception, route_name: Optional[str] = None): |     def lookup(self, exception, route_name: str | None = None): | ||||||
|         """Lookup the existing instance of `ErrorHandler` and fetch the registered handler for a specific type of exception. |         """Lookup the existing instance of `ErrorHandler` and fetch the registered handler for a specific type of exception. | ||||||
|  |  | ||||||
|         This method leverages a dict lookup to speedup the retrieval process. |         This method leverages a dict lookup to speedup the retrieval process. | ||||||
| @@ -98,9 +96,7 @@ class ErrorHandler: | |||||||
|                 exception_key = (ancestor, name) |                 exception_key = (ancestor, name) | ||||||
|                 if exception_key in self.cached_handlers: |                 if exception_key in self.cached_handlers: | ||||||
|                     handler = self.cached_handlers[exception_key] |                     handler = self.cached_handlers[exception_key] | ||||||
|                     self.cached_handlers[ |                     self.cached_handlers[(exception_class, route_name)] = handler | ||||||
|                         (exception_class, route_name) |  | ||||||
|                     ] = handler |  | ||||||
|                     return handler |                     return handler | ||||||
|  |  | ||||||
|                 if ancestor is BaseException: |                 if ancestor is BaseException: | ||||||
| @@ -135,13 +131,11 @@ class ErrorHandler: | |||||||
|                 url = repr(request.url) |                 url = repr(request.url) | ||||||
|             except AttributeError:  # no cov |             except AttributeError:  # no cov | ||||||
|                 url = "unknown" |                 url = "unknown" | ||||||
|             response_message = ( |             response_message = f'Exception raised in exception handler "{handler.__name__}" for uri: {url}' | ||||||
|                 "Exception raised in exception handler " '"%s" for uri: %s' |             error_logger.exception(response_message) | ||||||
|             ) |  | ||||||
|             error_logger.exception(response_message, handler.__name__, url) |  | ||||||
|  |  | ||||||
|             if self.debug: |             if self.debug: | ||||||
|                 return text(response_message % (handler.__name__, url), 500) |                 return text(response_message, 500) | ||||||
|             else: |             else: | ||||||
|                 return text("An error occurred while handling an error", 500) |                 return text("An error occurred while handling an error", 500) | ||||||
|         return response |         return response | ||||||
| @@ -200,6 +194,4 @@ class ErrorHandler: | |||||||
|             except AttributeError:  # no cov |             except AttributeError:  # no cov | ||||||
|                 url = "unknown" |                 url = "unknown" | ||||||
|  |  | ||||||
|             error_logger.exception( |             error_logger.exception("Exception occurred while handling uri: %s", url) | ||||||
|                 "Exception occurred while handling uri: %s", url |  | ||||||
|             ) |  | ||||||
|   | |||||||
| @@ -1,14 +1,12 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| import re | import re | ||||||
|  | from typing import Any, Dict, Iterable, Tuple, Union | ||||||
| from typing import Any, Dict, Iterable, List, Optional, Set, Tuple, Union |  | ||||||
| from urllib.parse import unquote | from urllib.parse import unquote | ||||||
|  |  | ||||||
| from sanic.exceptions import InvalidHeader | from sanic.exceptions import InvalidHeader | ||||||
| from sanic.helpers import STATUS_CODES | from sanic.helpers import STATUS_CODES | ||||||
|  |  | ||||||
|  |  | ||||||
| # TODO: | # TODO: | ||||||
| # - the Options object should be a typed object to allow for less casting | # - the Options object should be a typed object to allow for less casting | ||||||
| #   across the application (in request.py for example) | #   across the application (in request.py for example) | ||||||
| @@ -21,9 +19,7 @@ _token, _quoted = r"([\w!#$%&'*+\-.^_`|~]+)", r'"([^"]*)"' | |||||||
| _param = re.compile(rf";\s*{_token}=(?:{_token}|{_quoted})", re.ASCII) | _param = re.compile(rf";\s*{_token}=(?:{_token}|{_quoted})", re.ASCII) | ||||||
| _ipv6 = "(?:[0-9A-Fa-f]{0,4}:){2,7}[0-9A-Fa-f]{0,4}" | _ipv6 = "(?:[0-9A-Fa-f]{0,4}:){2,7}[0-9A-Fa-f]{0,4}" | ||||||
| _ipv6_re = re.compile(_ipv6) | _ipv6_re = re.compile(_ipv6) | ||||||
| _host_re = re.compile( | _host_re = re.compile(r"((?:\[" + _ipv6 + r"\])|[a-zA-Z0-9.\-]{1,253})(?::(\d{1,5}))?") | ||||||
|     r"((?:\[" + _ipv6 + r"\])|[a-zA-Z0-9.\-]{1,253})(?::(\d{1,5}))?" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| # RFC's quoted-pair escapes are mostly ignored by browsers. Chrome, Firefox and | # RFC's quoted-pair escapes are mostly ignored by browsers. Chrome, Firefox and | ||||||
| # curl all have different escaping, that we try to handle as well as possible, | # curl all have different escaping, that we try to handle as well as possible, | ||||||
| @@ -85,8 +81,8 @@ class MediaType: | |||||||
|  |  | ||||||
|     def match( |     def match( | ||||||
|         self, |         self, | ||||||
|         mime_with_params: Union[str, MediaType], |         mime_with_params: str | MediaType, | ||||||
|     ) -> Optional[MediaType]: |     ) -> MediaType | None: | ||||||
|         """Match this media type against another media type. |         """Match this media type against another media type. | ||||||
|  |  | ||||||
|         Check if this media type matches the given mime type/subtype. |         Check if this media type matches the given mime type/subtype. | ||||||
| @@ -124,9 +120,7 @@ class MediaType: | |||||||
|                     or mt.subtype == "*" |                     or mt.subtype == "*" | ||||||
|                 ) |                 ) | ||||||
|                 # Type match |                 # Type match | ||||||
|                 and ( |                 and (self.type == mt.type or self.type == "*" or mt.type == "*") | ||||||
|                     self.type == mt.type or self.type == "*" or mt.type == "*" |  | ||||||
|                 ) |  | ||||||
|             ) |             ) | ||||||
|             else None |             else None | ||||||
|         ) |         ) | ||||||
| @@ -141,7 +135,7 @@ class MediaType: | |||||||
|         return any(part == "*" for part in (self.subtype, self.type)) |         return any(part == "*" for part in (self.subtype, self.type)) | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def _parse(cls, mime_with_params: str) -> Optional[MediaType]: |     def _parse(cls, mime_with_params: str) -> MediaType | None: | ||||||
|         mtype = mime_with_params.strip() |         mtype = mime_with_params.strip() | ||||||
|         if "/" not in mime_with_params: |         if "/" not in mime_with_params: | ||||||
|             return None |             return None | ||||||
| @@ -151,12 +145,10 @@ class MediaType: | |||||||
|         if not type_ or not subtype: |         if not type_ or not subtype: | ||||||
|             raise ValueError(f"Invalid media type: {mtype}") |             raise ValueError(f"Invalid media type: {mtype}") | ||||||
|  |  | ||||||
|         params = dict( |         params = { | ||||||
|             [ |             key.strip(): value.strip() | ||||||
|                 (key.strip(), value.strip()) |  | ||||||
|             for key, value in (param.split("=", 1) for param in raw_params) |             for key, value in (param.split("=", 1) for param in raw_params) | ||||||
|             ] |         } | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         return cls(type_.lstrip(), subtype.rstrip(), **params) |         return cls(type_.lstrip(), subtype.rstrip(), **params) | ||||||
|  |  | ||||||
| @@ -173,7 +165,7 @@ class Matched: | |||||||
|         header (MediaType): The header to match against, if any. |         header (MediaType): The header to match against, if any. | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|     def __init__(self, mime: str, header: Optional[MediaType]): |     def __init__(self, mime: str, header: MediaType | None): | ||||||
|         self.mime = mime |         self.mime = mime | ||||||
|         self.header = header |         self.header = header | ||||||
|  |  | ||||||
| @@ -200,7 +192,7 @@ class Matched: | |||||||
|             ) |             ) | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def _compare(self, other) -> Tuple[bool, Matched]: |     def _compare(self, other) -> tuple[bool, Matched]: | ||||||
|         if isinstance(other, str): |         if isinstance(other, str): | ||||||
|             parsed = Matched.parse(other) |             parsed = Matched.parse(other) | ||||||
|             if self.mime == other: |             if self.mime == other: | ||||||
| @@ -215,7 +207,7 @@ class Matched: | |||||||
|             f"mime types of '{self.mime}' and '{other}'" |             f"mime types of '{self.mime}' and '{other}'" | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     def match(self, other: Union[str, Matched]) -> Optional[Matched]: |     def match(self, other: str | Matched) -> Matched | None: | ||||||
|         """Match this MIME string against another MIME string. |         """Match this MIME string against another MIME string. | ||||||
|  |  | ||||||
|         Check if this MIME string matches the given MIME string. Wildcards are supported both ways on both type and subtype. |         Check if this MIME string matches the given MIME string. Wildcards are supported both ways on both type and subtype. | ||||||
| @@ -296,7 +288,7 @@ class AcceptList(list): | |||||||
|         return ", ".join(str(m) for m in self) |         return ", ".join(str(m) for m in self) | ||||||
|  |  | ||||||
|  |  | ||||||
| def parse_accept(accept: Optional[str]) -> AcceptList: | def parse_accept(accept: str | None) -> AcceptList: | ||||||
|     """Parse an Accept header and order the acceptable media types according to RFC 7231, s. 5.3.2 |     """Parse an Accept header and order the acceptable media types according to RFC 7231, s. 5.3.2 | ||||||
|  |  | ||||||
|     https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2 |     https://datatracker.ietf.org/doc/html/rfc7231#section-5.3.2 | ||||||
| @@ -316,9 +308,7 @@ def parse_accept(accept: Optional[str]) -> AcceptList: | |||||||
|         accept = "*/*"  # No header means that all types are accepted |         accept = "*/*"  # No header means that all types are accepted | ||||||
|     try: |     try: | ||||||
|         a = [ |         a = [ | ||||||
|             mt |             mt for mt in [MediaType._parse(mtype) for mtype in accept.split(",")] if mt | ||||||
|             for mt in [MediaType._parse(mtype) for mtype in accept.split(",")] |  | ||||||
|             if mt |  | ||||||
|         ] |         ] | ||||||
|         if not a: |         if not a: | ||||||
|             raise ValueError |             raise ValueError | ||||||
| @@ -327,7 +317,7 @@ def parse_accept(accept: Optional[str]) -> AcceptList: | |||||||
|         raise InvalidHeader(f"Invalid header value in Accept: {accept}") |         raise InvalidHeader(f"Invalid header value in Accept: {accept}") | ||||||
|  |  | ||||||
|  |  | ||||||
| def parse_content_header(value: str) -> Tuple[str, Options]: | def parse_content_header(value: str) -> tuple[str, Options]: | ||||||
|     """Parse content-type and content-disposition header values. |     """Parse content-type and content-disposition header values. | ||||||
|  |  | ||||||
|     E.g. `form-data; name=upload; filename="file.txt"` to |     E.g. `form-data; name=upload; filename="file.txt"` to | ||||||
| @@ -346,11 +336,10 @@ def parse_content_header(value: str) -> Tuple[str, Options]: | |||||||
|     """ |     """ | ||||||
|     pos = value.find(";") |     pos = value.find(";") | ||||||
|     if pos == -1: |     if pos == -1: | ||||||
|         options: Dict[str, Union[int, str]] = {} |         options: dict[str, int | str] = {} | ||||||
|     else: |     else: | ||||||
|         options = { |         options = { | ||||||
|             m.group(1) |             m.group(1).lower(): (m.group(2) or m.group(3)) | ||||||
|             .lower(): (m.group(2) or m.group(3)) |  | ||||||
|             .replace("%22", '"') |             .replace("%22", '"') | ||||||
|             .replace("%0D%0A", "\n") |             .replace("%0D%0A", "\n") | ||||||
|             for m in _param.finditer(value[pos:]) |             for m in _param.finditer(value[pos:]) | ||||||
| @@ -367,7 +356,7 @@ def parse_content_header(value: str) -> Tuple[str, Options]: | |||||||
| _rparam = re.compile(f"(?:{_token}|{_quoted})={_token}\\s*($|[;,])", re.ASCII) | _rparam = re.compile(f"(?:{_token}|{_quoted})={_token}\\s*($|[;,])", re.ASCII) | ||||||
|  |  | ||||||
|  |  | ||||||
| def parse_forwarded(headers, config) -> Optional[Options]: | def parse_forwarded(headers, config) -> Options | None: | ||||||
|     """Parse RFC 7239 Forwarded headers. |     """Parse RFC 7239 Forwarded headers. | ||||||
|     The value of `by` or `secret` must match `config.FORWARDED_SECRET` |     The value of `by` or `secret` must match `config.FORWARDED_SECRET` | ||||||
|     :return: dict with keys and values, or None if nothing matched |     :return: dict with keys and values, or None if nothing matched | ||||||
| @@ -381,7 +370,7 @@ def parse_forwarded(headers, config) -> Optional[Options]: | |||||||
|         return None |         return None | ||||||
|     # Loop over <separator><key>=<value> elements from right to left |     # Loop over <separator><key>=<value> elements from right to left | ||||||
|     sep = pos = None |     sep = pos = None | ||||||
|     options: List[Tuple[str, str]] = [] |     options: list[tuple[str, str]] = [] | ||||||
|     found = False |     found = False | ||||||
|     for m in _rparam.finditer(header[::-1]): |     for m in _rparam.finditer(header[::-1]): | ||||||
|         # Start of new element? (on parser skips and non-semicolon right sep) |         # Start of new element? (on parser skips and non-semicolon right sep) | ||||||
| @@ -405,7 +394,7 @@ def parse_forwarded(headers, config) -> Optional[Options]: | |||||||
|     return fwd_normalize(reversed(options)) if found else None |     return fwd_normalize(reversed(options)) if found else None | ||||||
|  |  | ||||||
|  |  | ||||||
| def parse_xforwarded(headers, config) -> Optional[Options]: | def parse_xforwarded(headers, config) -> Options | None: | ||||||
|     """Parse traditional proxy headers.""" |     """Parse traditional proxy headers.""" | ||||||
|     real_ip_header = config.REAL_IP_HEADER |     real_ip_header = config.REAL_IP_HEADER | ||||||
|     proxies_count = config.PROXIES_COUNT |     proxies_count = config.PROXIES_COUNT | ||||||
| @@ -416,11 +405,7 @@ def parse_xforwarded(headers, config) -> Optional[Options]: | |||||||
|             # Combine, split and filter multiple headers' entries |             # Combine, split and filter multiple headers' entries | ||||||
|             forwarded_for = headers.getall(config.FORWARDED_FOR_HEADER) |             forwarded_for = headers.getall(config.FORWARDED_FOR_HEADER) | ||||||
|             proxies = [ |             proxies = [ | ||||||
|                 p |                 p for p in (p.strip() for h in forwarded_for for p in h.split(",")) if p | ||||||
|                 for p in ( |  | ||||||
|                     p.strip() for h in forwarded_for for p in h.split(",") |  | ||||||
|                 ) |  | ||||||
|                 if p |  | ||||||
|             ] |             ] | ||||||
|             addr = proxies[-proxies_count] |             addr = proxies[-proxies_count] | ||||||
|         except (KeyError, IndexError): |         except (KeyError, IndexError): | ||||||
| @@ -452,7 +437,7 @@ def fwd_normalize(fwd: OptionsIterable) -> Options: | |||||||
|     Returns: |     Returns: | ||||||
|         Options: A dict of normalized key-value pairs. |         Options: A dict of normalized key-value pairs. | ||||||
|     """ |     """ | ||||||
|     ret: Dict[str, Union[int, str]] = {} |     ret: dict[str, int | str] = {} | ||||||
|     for key, val in fwd: |     for key, val in fwd: | ||||||
|         if val is not None: |         if val is not None: | ||||||
|             try: |             try: | ||||||
| @@ -489,7 +474,7 @@ def fwd_normalize_address(addr: str) -> str: | |||||||
|     return addr.lower() |     return addr.lower() | ||||||
|  |  | ||||||
|  |  | ||||||
| def parse_host(host: str) -> Tuple[Optional[str], Optional[int]]: | def parse_host(host: str) -> tuple[str | None, int | None]: | ||||||
|     """Split host:port into hostname and port. |     """Split host:port into hostname and port. | ||||||
|  |  | ||||||
|     Args: |     Args: | ||||||
| @@ -531,9 +516,9 @@ def format_http1_response(status: int, headers: HeaderBytesIterable) -> bytes: | |||||||
|  |  | ||||||
|  |  | ||||||
| def parse_credentials( | def parse_credentials( | ||||||
|     header: Optional[str], |     header: str | None, | ||||||
|     prefixes: Optional[Union[List, Tuple, Set]] = None, |     prefixes: list | tuple | set | None = None, | ||||||
| ) -> Tuple[Optional[str], Optional[str]]: | ) -> tuple[str | None, str | None]: | ||||||
|     """Parses any header with the aim to retrieve any credentials from it. |     """Parses any header with the aim to retrieve any credentials from it. | ||||||
|  |  | ||||||
|     Args: |     Args: | ||||||
|   | |||||||
| @@ -1,12 +1,10 @@ | |||||||
| """Defines basics of HTTP standard.""" | """Defines basics of HTTP standard.""" | ||||||
|  |  | ||||||
| import sys | import sys | ||||||
|  |  | ||||||
| from importlib import import_module | from importlib import import_module | ||||||
| from inspect import ismodule | from inspect import ismodule | ||||||
| from typing import Dict | from typing import Dict | ||||||
|  |  | ||||||
|  |  | ||||||
| STATUS_CODES: Dict[int, bytes] = { | STATUS_CODES: Dict[int, bytes] = { | ||||||
|     100: b"Continue", |     100: b"Continue", | ||||||
|     101: b"Switching Protocols", |     101: b"Switching Protocols", | ||||||
| @@ -132,7 +130,7 @@ def remove_entity_headers(headers, allowed=("content-location", "expires")): | |||||||
|  |  | ||||||
|     returns the headers without the entity headers |     returns the headers without the entity headers | ||||||
|     """ |     """ | ||||||
|     allowed = set([h.lower() for h in allowed]) |     allowed = {h.lower() for h in allowed} | ||||||
|     headers = { |     headers = { | ||||||
|         header: value |         header: value | ||||||
|         for header, value in headers.items() |         for header, value in headers.items() | ||||||
|   | |||||||
| @@ -2,5 +2,4 @@ from .constants import Stage | |||||||
| from .http1 import Http | from .http1 import Http | ||||||
| from .http3 import Http3 | from .http3 import Http3 | ||||||
|  |  | ||||||
|  |  | ||||||
| __all__ = ("Http", "Stage", "Http3") | __all__ = ("Http", "Stage", "Http3") | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| from typing import TYPE_CHECKING, Optional | from typing import TYPE_CHECKING | ||||||
|  |  | ||||||
|  |  | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     from sanic.request import Request |     from sanic.request import Request | ||||||
| @@ -25,7 +24,6 @@ from sanic.http.stream import Stream | |||||||
| from sanic.log import access_logger, error_logger, logger | from sanic.log import access_logger, error_logger, logger | ||||||
| from sanic.touchup import TouchUpMeta | from sanic.touchup import TouchUpMeta | ||||||
|  |  | ||||||
|  |  | ||||||
| HTTP_CONTINUE = b"HTTP/1.1 100 Continue\r\n\r\n" | HTTP_CONTINUE = b"HTTP/1.1 100 Continue\r\n\r\n" | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -363,26 +361,20 @@ class Http(Stream, metaclass=TouchUpMeta): | |||||||
|             self.response_func = None |             self.response_func = None | ||||||
|             self.stage = Stage.IDLE |             self.stage = Stage.IDLE | ||||||
|  |  | ||||||
|     async def http1_response_chunked( |     async def http1_response_chunked(self, data: bytes, end_stream: bool) -> None: | ||||||
|         self, data: bytes, end_stream: bool |  | ||||||
|     ) -> None: |  | ||||||
|         """Format a part of response body in chunked encoding.""" |         """Format a part of response body in chunked encoding.""" | ||||||
|         # Chunked encoding |         # Chunked encoding | ||||||
|         size = len(data) |         size = len(data) | ||||||
|         if end_stream: |         if end_stream: | ||||||
|             await self._send( |             await self._send( | ||||||
|                 b"%x\r\n%b\r\n0\r\n\r\n" % (size, data) |                 b"%x\r\n%b\r\n0\r\n\r\n" % (size, data) if size else b"0\r\n\r\n" | ||||||
|                 if size |  | ||||||
|                 else b"0\r\n\r\n" |  | ||||||
|             ) |             ) | ||||||
|             self.response_func = None |             self.response_func = None | ||||||
|             self.stage = Stage.IDLE |             self.stage = Stage.IDLE | ||||||
|         elif size: |         elif size: | ||||||
|             await self._send(b"%x\r\n%b\r\n" % (size, data)) |             await self._send(b"%x\r\n%b\r\n" % (size, data)) | ||||||
|  |  | ||||||
|     async def http1_response_normal( |     async def http1_response_normal(self, data: bytes, end_stream: bool) -> None: | ||||||
|         self, data: bytes, end_stream: bool |  | ||||||
|     ) -> None: |  | ||||||
|         """Format / keep track of non-chunked response.""" |         """Format / keep track of non-chunked response.""" | ||||||
|         bytes_left = self.response_bytes_left - len(data) |         bytes_left = self.response_bytes_left - len(data) | ||||||
|         if bytes_left <= 0: |         if bytes_left <= 0: | ||||||
| @@ -420,9 +412,7 @@ class Http(Stream, metaclass=TouchUpMeta): | |||||||
|                 exception, (ServiceUnavailable, RequestCancelled) |                 exception, (ServiceUnavailable, RequestCancelled) | ||||||
|             ) |             ) | ||||||
|             try: |             try: | ||||||
|                 await app.handle_exception( |                 await app.handle_exception(self.request, exception, request_middleware) | ||||||
|                     self.request, exception, request_middleware |  | ||||||
|                 ) |  | ||||||
|             except Exception as e: |             except Exception as e: | ||||||
|                 await app.handle_exception(self.request, e, False) |                 await app.handle_exception(self.request, e, False) | ||||||
|  |  | ||||||
| @@ -481,7 +471,7 @@ class Http(Stream, metaclass=TouchUpMeta): | |||||||
|             if data: |             if data: | ||||||
|                 yield data |                 yield data | ||||||
|  |  | ||||||
|     async def read(self) -> Optional[bytes]:  # no cov |     async def read(self) -> bytes | None:  # no cov | ||||||
|         """Read some bytes of request body.""" |         """Read some bytes of request body.""" | ||||||
|  |  | ||||||
|         # Send a 100-continue if needed |         # Send a 100-continue if needed | ||||||
|   | |||||||
| @@ -1,17 +1,12 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| import asyncio | import asyncio | ||||||
|  |  | ||||||
| from abc import ABC, abstractmethod | from abc import ABC, abstractmethod | ||||||
| from ssl import SSLContext | from ssl import SSLContext | ||||||
| from typing import ( | from typing import ( | ||||||
|     TYPE_CHECKING, |     TYPE_CHECKING, | ||||||
|     Any, |     Any, | ||||||
|     Callable, |     Callable, | ||||||
|     Dict, |  | ||||||
|     List, |  | ||||||
|     Optional, |  | ||||||
|     Tuple, |  | ||||||
|     Union, |     Union, | ||||||
|     cast, |     cast, | ||||||
| ) | ) | ||||||
| @@ -32,7 +27,6 @@ from sanic.log import Colors, logger | |||||||
| from sanic.models.protocol_types import TransportProtocol | from sanic.models.protocol_types import TransportProtocol | ||||||
| from sanic.models.server_types import ConnInfo | from sanic.models.server_types import ConnInfo | ||||||
|  |  | ||||||
|  |  | ||||||
| try: | try: | ||||||
|     from aioquic.h0.connection import H0_ALPN, H0Connection |     from aioquic.h0.connection import H0_ALPN, H0Connection | ||||||
|     from aioquic.h3.connection import H3_ALPN, H3Connection |     from aioquic.h3.connection import H3_ALPN, H3Connection | ||||||
| @@ -71,10 +65,7 @@ class HTTP3Transport(TransportProtocol): | |||||||
|         return self._protocol |         return self._protocol | ||||||
|  |  | ||||||
|     def get_extra_info(self, info: str, default: Any = None) -> Any: |     def get_extra_info(self, info: str, default: Any = None) -> Any: | ||||||
|         if ( |         if info in ("socket", "sockname", "peername") and self._protocol._transport: | ||||||
|             info in ("socket", "sockname", "peername") |  | ||||||
|             and self._protocol._transport |  | ||||||
|         ): |  | ||||||
|             return self._protocol._transport.get_extra_info(info, default) |             return self._protocol._transport.get_extra_info(info, default) | ||||||
|         elif info == "network_paths": |         elif info == "network_paths": | ||||||
|             return self._protocol._quic._network_paths |             return self._protocol._quic._network_paths | ||||||
| @@ -109,19 +100,18 @@ class HTTPReceiver(Receiver, Stream): | |||||||
|         self.request_body = None |         self.request_body = None | ||||||
|         self.stage = Stage.IDLE |         self.stage = Stage.IDLE | ||||||
|         self.headers_sent = False |         self.headers_sent = False | ||||||
|         self.response: Optional[BaseHTTPResponse] = None |         self.response: BaseHTTPResponse | None = None | ||||||
|         self.request_max_size = self.protocol.request_max_size |         self.request_max_size = self.protocol.request_max_size | ||||||
|         self.request_bytes = 0 |         self.request_bytes = 0 | ||||||
|  |  | ||||||
|     async def run(self, exception: Optional[Exception] = None): |     async def run(self, exception: Exception | None = None): | ||||||
|         """Handle the request and response cycle.""" |         """Handle the request and response cycle.""" | ||||||
|         self.stage = Stage.HANDLER |         self.stage = Stage.HANDLER | ||||||
|         self.head_only = self.request.method.upper() == "HEAD" |         self.head_only = self.request.method.upper() == "HEAD" | ||||||
|  |  | ||||||
|         if exception: |         if exception: | ||||||
|             logger.info(  # no cov |             logger.info(  # no cov | ||||||
|                 f"{Colors.BLUE}[exception]: " |                 f"{Colors.BLUE}[exception]: " f"{Colors.RED}{exception}{Colors.END}", | ||||||
|                 f"{Colors.RED}{exception}{Colors.END}", |  | ||||||
|                 exc_info=True, |                 exc_info=True, | ||||||
|                 extra={"verbosity": 1}, |                 extra={"verbosity": 1}, | ||||||
|             ) |             ) | ||||||
| @@ -146,17 +136,13 @@ class HTTPReceiver(Receiver, Stream): | |||||||
|  |  | ||||||
|         await app.handle_exception(self.request, exception) |         await app.handle_exception(self.request, exception) | ||||||
|  |  | ||||||
|     def _prepare_headers( |     def _prepare_headers(self, response: BaseHTTPResponse) -> list[tuple[bytes, bytes]]: | ||||||
|         self, response: BaseHTTPResponse |  | ||||||
|     ) -> List[Tuple[bytes, bytes]]: |  | ||||||
|         size = len(response.body) if response.body else 0 |         size = len(response.body) if response.body else 0 | ||||||
|         headers = response.headers |         headers = response.headers | ||||||
|         status = response.status |         status = response.status | ||||||
|  |  | ||||||
|         if not has_message_body(status) and ( |         if not has_message_body(status) and ( | ||||||
|             size |             size or "content-length" in headers or "transfer-encoding" in headers | ||||||
|             or "content-length" in headers |  | ||||||
|             or "transfer-encoding" in headers |  | ||||||
|         ): |         ): | ||||||
|             headers.pop("content-length", None) |             headers.pop("content-length", None) | ||||||
|             headers.pop("transfer-encoding", None) |             headers.pop("transfer-encoding", None) | ||||||
| @@ -249,11 +235,7 @@ class HTTPReceiver(Receiver, Stream): | |||||||
|         ): |         ): | ||||||
|             size = len(data) |             size = len(data) | ||||||
|             if end_stream: |             if end_stream: | ||||||
|                 data = ( |                 data = b"%x\r\n%b\r\n0\r\n\r\n" % (size, data) if size else b"0\r\n\r\n" | ||||||
|                     b"%x\r\n%b\r\n0\r\n\r\n" % (size, data) |  | ||||||
|                     if size |  | ||||||
|                     else b"0\r\n\r\n" |  | ||||||
|                 ) |  | ||||||
|             elif size: |             elif size: | ||||||
|                 data = b"%x\r\n%b\r\n" % (size, data) |                 data = b"%x\r\n%b\r\n" % (size, data) | ||||||
|  |  | ||||||
| @@ -304,7 +286,7 @@ class Http3: | |||||||
|     ) -> None: |     ) -> None: | ||||||
|         self.protocol = protocol |         self.protocol = protocol | ||||||
|         self.transmit = transmit |         self.transmit = transmit | ||||||
|         self.receivers: Dict[int, Receiver] = {} |         self.receivers: dict[int, Receiver] = {} | ||||||
|  |  | ||||||
|     def http_event_received(self, event: H3Event) -> None: |     def http_event_received(self, event: H3Event) -> None: | ||||||
|         logger.debug(  # no cov |         logger.debug(  # no cov | ||||||
| @@ -330,11 +312,8 @@ class Http3: | |||||||
|                 extra={"verbosity": 2}, |                 extra={"verbosity": 2}, | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|     def get_or_make_receiver(self, event: H3Event) -> Tuple[Receiver, bool]: |     def get_or_make_receiver(self, event: H3Event) -> tuple[Receiver, bool]: | ||||||
|         if ( |         if isinstance(event, HeadersReceived) and event.stream_id not in self.receivers: | ||||||
|             isinstance(event, HeadersReceived) |  | ||||||
|             and event.stream_id not in self.receivers |  | ||||||
|         ): |  | ||||||
|             request = self._make_request(event) |             request = self._make_request(event) | ||||||
|             receiver = HTTPReceiver(self.transmit, self.protocol, request) |             receiver = HTTPReceiver(self.transmit, self.protocol, request) | ||||||
|             request.stream = receiver |             request.stream = receiver | ||||||
| @@ -357,9 +336,7 @@ class Http3: | |||||||
|                 ) |                 ) | ||||||
|             ) |             ) | ||||||
|         except UnicodeDecodeError: |         except UnicodeDecodeError: | ||||||
|             raise BadRequest( |             raise BadRequest("Header names may only contain US-ASCII characters.") | ||||||
|                 "Header names may only contain US-ASCII characters." |  | ||||||
|             ) |  | ||||||
|         method = headers[":method"] |         method = headers[":method"] | ||||||
|         path = headers[":path"] |         path = headers[":path"] | ||||||
|         scheme = headers.pop(":scheme", "") |         scheme = headers.pop(":scheme", "") | ||||||
| @@ -396,18 +373,16 @@ class SessionTicketStore: | |||||||
|     """ |     """ | ||||||
|  |  | ||||||
|     def __init__(self) -> None: |     def __init__(self) -> None: | ||||||
|         self.tickets: Dict[bytes, SessionTicket] = {} |         self.tickets: dict[bytes, SessionTicket] = {} | ||||||
|  |  | ||||||
|     def add(self, ticket: SessionTicket) -> None: |     def add(self, ticket: SessionTicket) -> None: | ||||||
|         self.tickets[ticket.ticket] = ticket |         self.tickets[ticket.ticket] = ticket | ||||||
|  |  | ||||||
|     def pop(self, label: bytes) -> Optional[SessionTicket]: |     def pop(self, label: bytes) -> SessionTicket | None: | ||||||
|         return self.tickets.pop(label, None) |         return self.tickets.pop(label, None) | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_config( | def get_config(app: Sanic, ssl: SanicSSLContext | CertSelector | SSLContext): | ||||||
|     app: Sanic, ssl: Union[SanicSSLContext, CertSelector, SSLContext] |  | ||||||
| ): |  | ||||||
|     # TODO: |     # TODO: | ||||||
|     # - proper selection needed if service with multiple certs insted of |     # - proper selection needed if service with multiple certs insted of | ||||||
|     #   just taking the first |     #   just taking the first | ||||||
| @@ -430,8 +405,6 @@ def get_config( | |||||||
|     ) |     ) | ||||||
|     password = app.config.TLS_CERT_PASSWORD or None |     password = app.config.TLS_CERT_PASSWORD or None | ||||||
|  |  | ||||||
|     config.load_cert_chain( |     config.load_cert_chain(ssl.sanic["cert"], ssl.sanic["key"], password=password) | ||||||
|         ssl.sanic["cert"], ssl.sanic["key"], password=password |  | ||||||
|     ) |  | ||||||
|  |  | ||||||
|     return config |     return config | ||||||
|   | |||||||
| @@ -1,10 +1,9 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| from typing import TYPE_CHECKING, Optional, Tuple, Union | from typing import TYPE_CHECKING | ||||||
|  |  | ||||||
| from sanic.http.constants import Stage | from sanic.http.constants import Stage | ||||||
|  |  | ||||||
|  |  | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     from sanic.response import BaseHTTPResponse |     from sanic.response import BaseHTTPResponse | ||||||
|     from sanic.server.protocols.http_protocol import HttpProtocol |     from sanic.server.protocols.http_protocol import HttpProtocol | ||||||
| @@ -12,16 +11,14 @@ if TYPE_CHECKING: | |||||||
|  |  | ||||||
| class Stream: | class Stream: | ||||||
|     stage: Stage |     stage: Stage | ||||||
|     response: Optional[BaseHTTPResponse] |     response: BaseHTTPResponse | None | ||||||
|     protocol: HttpProtocol |     protocol: HttpProtocol | ||||||
|     url: Optional[str] |     url: str | None | ||||||
|     request_body: Optional[bytes] |     request_body: bytes | None | ||||||
|     request_max_size: Union[int, float] |     request_max_size: int | float | ||||||
|  |  | ||||||
|     __touchup__: Tuple[str, ...] = tuple() |     __touchup__: tuple[str, ...] = () | ||||||
|     __slots__ = ("request_max_size",) |     __slots__ = ("request_max_size",) | ||||||
|  |  | ||||||
|     def respond( |     def respond(self, response: BaseHTTPResponse) -> BaseHTTPResponse:  # no cov | ||||||
|         self, response: BaseHTTPResponse |  | ||||||
|     ) -> BaseHTTPResponse:  # no cov |  | ||||||
|         raise NotImplementedError("Not implemented") |         raise NotImplementedError("Not implemented") | ||||||
|   | |||||||
| @@ -1,5 +1,4 @@ | |||||||
| from .context import process_to_context | from .context import process_to_context | ||||||
| from .creators import get_ssl_context | from .creators import get_ssl_context | ||||||
|  |  | ||||||
|  |  | ||||||
| __all__ = ("get_ssl_context", "process_to_context") | __all__ = ("get_ssl_context", "process_to_context") | ||||||
|   | |||||||
| @@ -2,12 +2,10 @@ from __future__ import annotations | |||||||
|  |  | ||||||
| import os | import os | ||||||
| import ssl | import ssl | ||||||
|  | from typing import Any, Iterable | ||||||
| from typing import Any, Dict, Iterable, Optional, Union |  | ||||||
|  |  | ||||||
| from sanic.log import logger | from sanic.log import logger | ||||||
|  |  | ||||||
|  |  | ||||||
| # Only allow secure ciphers, notably leaving out AES-CBC mode | # Only allow secure ciphers, notably leaving out AES-CBC mode | ||||||
| # OpenSSL chooses ECDSA or RSA depending on the cert in use | # OpenSSL chooses ECDSA or RSA depending on the cert in use | ||||||
| CIPHERS_TLS12 = [ | CIPHERS_TLS12 = [ | ||||||
| @@ -19,11 +17,14 @@ CIPHERS_TLS12 = [ | |||||||
|     "ECDHE-RSA-AES128-GCM-SHA256", |     "ECDHE-RSA-AES128-GCM-SHA256", | ||||||
| ] | ] | ||||||
|  |  | ||||||
|  | TlsDef = None | ssl.SSLContext | dict[str, Any] | str | ||||||
|  | TlsDefs = TlsDef | list[TlsDef] | tuple[TlsDef, ...] | ||||||
|  |  | ||||||
|  |  | ||||||
| def create_context( | def create_context( | ||||||
|     certfile: Optional[str] = None, |     certfile: str | None = None, | ||||||
|     keyfile: Optional[str] = None, |     keyfile: str | None = None, | ||||||
|     password: Optional[str] = None, |     password: str | None = None, | ||||||
|     purpose: ssl.Purpose = ssl.Purpose.CLIENT_AUTH, |     purpose: ssl.Purpose = ssl.Purpose.CLIENT_AUTH, | ||||||
| ) -> ssl.SSLContext: | ) -> ssl.SSLContext: | ||||||
|     """Create a context with secure crypto and HTTP/1.1 in protocols.""" |     """Create a context with secure crypto and HTTP/1.1 in protocols.""" | ||||||
| @@ -38,9 +39,7 @@ def create_context( | |||||||
|     return context |     return context | ||||||
|  |  | ||||||
|  |  | ||||||
| def shorthand_to_ctx( | def shorthand_to_ctx(ctxdef: TlsDef) -> ssl.SSLContext | None: | ||||||
|     ctxdef: Union[None, ssl.SSLContext, dict, str] |  | ||||||
| ) -> Optional[ssl.SSLContext]: |  | ||||||
|     """Convert an ssl argument shorthand to an SSLContext object.""" |     """Convert an ssl argument shorthand to an SSLContext object.""" | ||||||
|     if ctxdef is None or isinstance(ctxdef, ssl.SSLContext): |     if ctxdef is None or isinstance(ctxdef, ssl.SSLContext): | ||||||
|         return ctxdef |         return ctxdef | ||||||
| @@ -54,9 +53,7 @@ def shorthand_to_ctx( | |||||||
|     ) |     ) | ||||||
|  |  | ||||||
|  |  | ||||||
| def process_to_context( | def process_to_context(ssldef: TlsDefs) -> ssl.SSLContext | None: | ||||||
|     ssldef: Union[None, ssl.SSLContext, dict, str, list, tuple] |  | ||||||
| ) -> Optional[ssl.SSLContext]: |  | ||||||
|     """Process app.run ssl argument from easy formats to full SSLContext.""" |     """Process app.run ssl argument from easy formats to full SSLContext.""" | ||||||
|     return ( |     return ( | ||||||
|         CertSelector(map(shorthand_to_ctx, ssldef)) |         CertSelector(map(shorthand_to_ctx, ssldef)) | ||||||
| @@ -71,13 +68,9 @@ def load_cert_dir(p: str) -> ssl.SSLContext: | |||||||
|     keyfile = os.path.join(p, "privkey.pem") |     keyfile = os.path.join(p, "privkey.pem") | ||||||
|     certfile = os.path.join(p, "fullchain.pem") |     certfile = os.path.join(p, "fullchain.pem") | ||||||
|     if not os.access(keyfile, os.R_OK): |     if not os.access(keyfile, os.R_OK): | ||||||
|         raise ValueError( |         raise ValueError(f"Certificate not found or permission denied {keyfile}") | ||||||
|             f"Certificate not found or permission denied {keyfile}" |  | ||||||
|         ) |  | ||||||
|     if not os.access(certfile, os.R_OK): |     if not os.access(certfile, os.R_OK): | ||||||
|         raise ValueError( |         raise ValueError(f"Certificate not found or permission denied {certfile}") | ||||||
|             f"Certificate not found or permission denied {certfile}" |  | ||||||
|         ) |  | ||||||
|     return CertSimple(certfile, keyfile) |     return CertSimple(certfile, keyfile) | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -89,9 +82,7 @@ def find_cert(self: CertSelector, server_name: str): | |||||||
|     if not server_name: |     if not server_name: | ||||||
|         if self.sanic_fallback: |         if self.sanic_fallback: | ||||||
|             return self.sanic_fallback |             return self.sanic_fallback | ||||||
|         raise ValueError( |         raise ValueError("The client provided no SNI to match for certificate.") | ||||||
|             "The client provided no SNI to match for certificate." |  | ||||||
|         ) |  | ||||||
|     for ctx in self.sanic_select: |     for ctx in self.sanic_select: | ||||||
|         if match_hostname(ctx, server_name): |         if match_hostname(ctx, server_name): | ||||||
|             return ctx |             return ctx | ||||||
| @@ -100,9 +91,7 @@ def find_cert(self: CertSelector, server_name: str): | |||||||
|     raise ValueError(f"No certificate found matching hostname {server_name!r}") |     raise ValueError(f"No certificate found matching hostname {server_name!r}") | ||||||
|  |  | ||||||
|  |  | ||||||
| def match_hostname( | def match_hostname(ctx: ssl.SSLContext | CertSelector, hostname: str) -> bool: | ||||||
|     ctx: Union[ssl.SSLContext, CertSelector], hostname: str |  | ||||||
| ) -> bool: |  | ||||||
|     """Match names from CertSelector against a received hostname.""" |     """Match names from CertSelector against a received hostname.""" | ||||||
|     # Local certs are considered trusted, so this can be less pedantic |     # Local certs are considered trusted, so this can be less pedantic | ||||||
|     # and thus faster than the deprecated ssl.match_hostname function is. |     # and thus faster than the deprecated ssl.match_hostname function is. | ||||||
| @@ -119,7 +108,7 @@ def match_hostname( | |||||||
|  |  | ||||||
| def selector_sni_callback( | def selector_sni_callback( | ||||||
|     sslobj: ssl.SSLObject, server_name: str, ctx: CertSelector |     sslobj: ssl.SSLObject, server_name: str, ctx: CertSelector | ||||||
| ) -> Optional[int]: | ) -> int | None: | ||||||
|     """Select a certificate matching the SNI.""" |     """Select a certificate matching the SNI.""" | ||||||
|     # Call server_name_callback to store the SNI on sslobj |     # Call server_name_callback to store the SNI on sslobj | ||||||
|     server_name_callback(sslobj, server_name, ctx) |     server_name_callback(sslobj, server_name, ctx) | ||||||
| @@ -142,7 +131,7 @@ def server_name_callback( | |||||||
|  |  | ||||||
|  |  | ||||||
| class SanicSSLContext(ssl.SSLContext): | class SanicSSLContext(ssl.SSLContext): | ||||||
|     sanic: Dict[str, os.PathLike] |     sanic: dict[str, os.PathLike] | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def create_from_ssl_context(cls, context: ssl.SSLContext): |     def create_from_ssl_context(cls, context: ssl.SSLContext): | ||||||
| @@ -153,7 +142,7 @@ class SanicSSLContext(ssl.SSLContext): | |||||||
| class CertSimple(SanicSSLContext): | class CertSimple(SanicSSLContext): | ||||||
|     """A wrapper for creating SSLContext with a sanic attribute.""" |     """A wrapper for creating SSLContext with a sanic attribute.""" | ||||||
|  |  | ||||||
|     sanic: Dict[str, Any] |     sanic: dict[str, Any] | ||||||
|  |  | ||||||
|     def __new__(cls, cert, key, **kw): |     def __new__(cls, cert, key, **kw): | ||||||
|         # try common aliases, rename to cert/key |         # try common aliases, rename to cert/key | ||||||
| @@ -166,9 +155,7 @@ class CertSimple(SanicSSLContext): | |||||||
|         if "names" not in kw: |         if "names" not in kw: | ||||||
|             cert = ssl._ssl._test_decode_cert(certfile)  # type: ignore |             cert = ssl._ssl._test_decode_cert(certfile)  # type: ignore | ||||||
|             kw["names"] = [ |             kw["names"] = [ | ||||||
|                 name |                 name for t, name in cert["subjectAltName"] if t in ["DNS", "IP Address"] | ||||||
|                 for t, name in cert["subjectAltName"] |  | ||||||
|                 if t in ["DNS", "IP Address"] |  | ||||||
|             ] |             ] | ||||||
|             subject = {k: v for item in cert["subject"] for k, v in item} |             subject = {k: v for item in cert["subject"] for k, v in item} | ||||||
|         self = create_context(certfile, keyfile, password) |         self = create_context(certfile, keyfile, password) | ||||||
| @@ -190,7 +177,7 @@ class CertSelector(ssl.SSLContext): | |||||||
|     def __new__(cls, ctxs): |     def __new__(cls, ctxs): | ||||||
|         return super().__new__(cls) |         return super().__new__(cls) | ||||||
|  |  | ||||||
|     def __init__(self, ctxs: Iterable[Optional[ssl.SSLContext]]): |     def __init__(self, ctxs: Iterable[ssl.SSLContext | None]): | ||||||
|         super().__init__() |         super().__init__() | ||||||
|         self.sni_callback = selector_sni_callback  # type: ignore |         self.sni_callback = selector_sni_callback  # type: ignore | ||||||
|         self.sanic_select = [] |         self.sanic_select = [] | ||||||
| @@ -205,7 +192,5 @@ class CertSelector(ssl.SSLContext): | |||||||
|             if i == 0: |             if i == 0: | ||||||
|                 self.sanic_fallback = ctx |                 self.sanic_fallback = ctx | ||||||
|         if not all_names: |         if not all_names: | ||||||
|             raise ValueError( |             raise ValueError("No certificates with SubjectAlternativeNames found.") | ||||||
|                 "No certificates with SubjectAlternativeNames found." |  | ||||||
|             ) |  | ||||||
|         logger.info(f"Certificate vhosts: {', '.join(all_names)}") |         logger.info(f"Certificate vhosts: {', '.join(all_names)}") | ||||||
|   | |||||||
| @@ -3,13 +3,12 @@ from __future__ import annotations | |||||||
| import ssl | import ssl | ||||||
| import subprocess | import subprocess | ||||||
| import sys | import sys | ||||||
|  |  | ||||||
| from abc import ABC, abstractmethod | from abc import ABC, abstractmethod | ||||||
| from contextlib import suppress | from contextlib import suppress | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from tempfile import mkdtemp | from tempfile import mkdtemp | ||||||
| from types import ModuleType | from types import ModuleType | ||||||
| from typing import TYPE_CHECKING, Optional, Tuple, Type, Union, cast | from typing import TYPE_CHECKING, cast | ||||||
|  |  | ||||||
| from sanic.application.constants import Mode | from sanic.application.constants import Mode | ||||||
| from sanic.application.spinner import loading | from sanic.application.spinner import loading | ||||||
| @@ -22,7 +21,6 @@ from sanic.exceptions import SanicException | |||||||
| from sanic.helpers import Default | from sanic.helpers import Default | ||||||
| from sanic.http.tls.context import CertSimple, SanicSSLContext | from sanic.http.tls.context import CertSimple, SanicSSLContext | ||||||
|  |  | ||||||
|  |  | ||||||
| try: | try: | ||||||
|     import trustme |     import trustme | ||||||
|  |  | ||||||
| @@ -47,7 +45,7 @@ CIPHERS_TLS12 = [ | |||||||
| ] | ] | ||||||
|  |  | ||||||
|  |  | ||||||
| def _make_path(maybe_path: Union[Path, str], tmpdir: Optional[Path]) -> Path: | def _make_path(maybe_path: Path | str, tmpdir: Path | None) -> Path: | ||||||
|     if isinstance(maybe_path, Path): |     if isinstance(maybe_path, Path): | ||||||
|         return maybe_path |         return maybe_path | ||||||
|     else: |     else: | ||||||
| @@ -60,9 +58,7 @@ def _make_path(maybe_path: Union[Path, str], tmpdir: Optional[Path]) -> Path: | |||||||
|     return path |     return path | ||||||
|  |  | ||||||
|  |  | ||||||
| def get_ssl_context( | def get_ssl_context(app: Sanic, ssl: ssl.SSLContext | None) -> ssl.SSLContext: | ||||||
|     app: Sanic, ssl: Optional[ssl.SSLContext] |  | ||||||
| ) -> ssl.SSLContext: |  | ||||||
|     if ssl: |     if ssl: | ||||||
|         return ssl |         return ssl | ||||||
|  |  | ||||||
| @@ -96,16 +92,8 @@ class CertCreator(ABC): | |||||||
|         if isinstance(self.key, Default) or isinstance(self.cert, Default): |         if isinstance(self.key, Default) or isinstance(self.cert, Default): | ||||||
|             self.tmpdir = Path(mkdtemp()) |             self.tmpdir = Path(mkdtemp()) | ||||||
|  |  | ||||||
|         key = ( |         key = DEFAULT_LOCAL_TLS_KEY if isinstance(self.key, Default) else self.key | ||||||
|             DEFAULT_LOCAL_TLS_KEY |         cert = DEFAULT_LOCAL_TLS_CERT if isinstance(self.cert, Default) else self.cert | ||||||
|             if isinstance(self.key, Default) |  | ||||||
|             else self.key |  | ||||||
|         ) |  | ||||||
|         cert = ( |  | ||||||
|             DEFAULT_LOCAL_TLS_CERT |  | ||||||
|             if isinstance(self.cert, Default) |  | ||||||
|             else self.cert |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         self.key_path = _make_path(key, self.tmpdir) |         self.key_path = _make_path(key, self.tmpdir) | ||||||
|         self.cert_path = _make_path(cert, self.tmpdir) |         self.cert_path = _make_path(cert, self.tmpdir) | ||||||
| @@ -126,11 +114,9 @@ class CertCreator(ABC): | |||||||
|         local_tls_key, |         local_tls_key, | ||||||
|         local_tls_cert, |         local_tls_cert, | ||||||
|     ) -> CertCreator: |     ) -> CertCreator: | ||||||
|         creator: Optional[CertCreator] = None |         creator: CertCreator | None = None | ||||||
|  |  | ||||||
|         cert_creator_options: Tuple[ |         cert_creator_options: tuple[tuple[type[CertCreator], LocalCertCreator], ...] = ( | ||||||
|             Tuple[Type[CertCreator], LocalCertCreator], ... |  | ||||||
|         ] = ( |  | ||||||
|             (MkcertCreator, LocalCertCreator.MKCERT), |             (MkcertCreator, LocalCertCreator.MKCERT), | ||||||
|             (TrustmeCreator, LocalCertCreator.TRUSTME), |             (TrustmeCreator, LocalCertCreator.TRUSTME), | ||||||
|         ) |         ) | ||||||
| @@ -160,8 +146,8 @@ class CertCreator(ABC): | |||||||
|     @staticmethod |     @staticmethod | ||||||
|     def _try_select( |     def _try_select( | ||||||
|         app: Sanic, |         app: Sanic, | ||||||
|         creator: Optional[CertCreator], |         creator: CertCreator | None, | ||||||
|         creator_class: Type[CertCreator], |         creator_class: type[CertCreator], | ||||||
|         creator_requirement: LocalCertCreator, |         creator_requirement: LocalCertCreator, | ||||||
|         creator_requested: LocalCertCreator, |         creator_requested: LocalCertCreator, | ||||||
|         local_tls_key, |         local_tls_key, | ||||||
|   | |||||||
							
								
								
									
										18
									
								
								sanic/log.py
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								sanic/log.py
									
									
									
									
									
								
							| @@ -1,13 +1,11 @@ | |||||||
| import logging | import logging | ||||||
| import sys | import sys | ||||||
|  |  | ||||||
| from enum import Enum | from enum import Enum | ||||||
| from typing import TYPE_CHECKING, Any, Dict | from typing import TYPE_CHECKING, Any, Dict | ||||||
| from warnings import warn | from warnings import warn | ||||||
|  |  | ||||||
| from sanic.helpers import is_atty | from sanic.helpers import is_atty | ||||||
|  |  | ||||||
|  |  | ||||||
| # Python 3.11 changed the way Enum formatting works for mixed-in types. | # Python 3.11 changed the way Enum formatting works for mixed-in types. | ||||||
| if sys.version_info < (3, 11, 0): | if sys.version_info < (3, 11, 0): | ||||||
|  |  | ||||||
| @@ -19,10 +17,10 @@ else: | |||||||
|         from enum import StrEnum |         from enum import StrEnum | ||||||
|  |  | ||||||
|  |  | ||||||
| LOGGING_CONFIG_DEFAULTS: Dict[str, Any] = dict(  # no cov | LOGGING_CONFIG_DEFAULTS: Dict[str, Any] = {  # no cov | ||||||
|     version=1, |     "version": 1, | ||||||
|     disable_existing_loggers=False, |     "disable_existing_loggers": False, | ||||||
|     loggers={ |     "loggers": { | ||||||
|         "sanic.root": {"level": "INFO", "handlers": ["console"]}, |         "sanic.root": {"level": "INFO", "handlers": ["console"]}, | ||||||
|         "sanic.error": { |         "sanic.error": { | ||||||
|             "level": "INFO", |             "level": "INFO", | ||||||
| @@ -43,7 +41,7 @@ LOGGING_CONFIG_DEFAULTS: Dict[str, Any] = dict(  # no cov | |||||||
|             "qualname": "sanic.server", |             "qualname": "sanic.server", | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|     handlers={ |     "handlers": { | ||||||
|         "console": { |         "console": { | ||||||
|             "class": "logging.StreamHandler", |             "class": "logging.StreamHandler", | ||||||
|             "formatter": "generic", |             "formatter": "generic", | ||||||
| @@ -60,7 +58,7 @@ LOGGING_CONFIG_DEFAULTS: Dict[str, Any] = dict(  # no cov | |||||||
|             "stream": sys.stdout, |             "stream": sys.stdout, | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
|     formatters={ |     "formatters": { | ||||||
|         "generic": { |         "generic": { | ||||||
|             "format": "%(asctime)s [%(process)s] [%(levelname)s] %(message)s", |             "format": "%(asctime)s [%(process)s] [%(levelname)s] %(message)s", | ||||||
|             "datefmt": "[%Y-%m-%d %H:%M:%S %z]", |             "datefmt": "[%Y-%m-%d %H:%M:%S %z]", | ||||||
| @@ -68,12 +66,12 @@ LOGGING_CONFIG_DEFAULTS: Dict[str, Any] = dict(  # no cov | |||||||
|         }, |         }, | ||||||
|         "access": { |         "access": { | ||||||
|             "format": "%(asctime)s - (%(name)s)[%(levelname)s][%(host)s]: " |             "format": "%(asctime)s - (%(name)s)[%(levelname)s][%(host)s]: " | ||||||
|             + "%(request)s %(message)s %(status)s %(byte)s", |             "%(request)s %(message)s %(status)s %(byte)s", | ||||||
|             "datefmt": "[%Y-%m-%d %H:%M:%S %z]", |             "datefmt": "[%Y-%m-%d %H:%M:%S %z]", | ||||||
|             "class": "logging.Formatter", |             "class": "logging.Formatter", | ||||||
|         }, |         }, | ||||||
|     }, |     }, | ||||||
| ) | } | ||||||
| """ | """ | ||||||
| Defult logging configuration | Defult logging configuration | ||||||
| """ | """ | ||||||
|   | |||||||
| @@ -3,7 +3,7 @@ from __future__ import annotations | |||||||
| from collections import deque | from collections import deque | ||||||
| from enum import IntEnum, auto | from enum import IntEnum, auto | ||||||
| from itertools import count | from itertools import count | ||||||
| from typing import Deque, Sequence, Union | from typing import Sequence | ||||||
|  |  | ||||||
| from sanic.models.handler_types import MiddlewareType | from sanic.models.handler_types import MiddlewareType | ||||||
|  |  | ||||||
| @@ -69,9 +69,9 @@ class Middleware: | |||||||
|     @classmethod |     @classmethod | ||||||
|     def convert( |     def convert( | ||||||
|         cls, |         cls, | ||||||
|         *middleware_collections: Sequence[Union[Middleware, MiddlewareType]], |         *middleware_collections: Sequence[Middleware | MiddlewareType], | ||||||
|         location: MiddlewareLocation, |         location: MiddlewareLocation, | ||||||
|     ) -> Deque[Middleware]: |     ) -> deque[Middleware]: | ||||||
|         """Convert middleware collections to a deque of Middleware objects. |         """Convert middleware collections to a deque of Middleware objects. | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
|   | |||||||
| @@ -14,7 +14,7 @@ class ExceptionMixin(metaclass=SanicMeta): | |||||||
|     def exception( |     def exception( | ||||||
|         self, |         self, | ||||||
|         *exceptions: Union[Type[Exception], List[Type[Exception]]], |         *exceptions: Union[Type[Exception], List[Type[Exception]]], | ||||||
|         apply: bool = True |         apply: bool = True, | ||||||
|     ) -> Callable: |     ) -> Callable: | ||||||
|         """Decorator used to register an exception handler for the current application or blueprint instance. |         """Decorator used to register an exception handler for the current application or blueprint instance. | ||||||
|  |  | ||||||
| @@ -79,9 +79,7 @@ class ExceptionMixin(metaclass=SanicMeta): | |||||||
|  |  | ||||||
|         return decorator |         return decorator | ||||||
|  |  | ||||||
|     def all_exceptions( |     def all_exceptions(self, handler: Callable[..., Any]) -> Callable[..., Any]: | ||||||
|         self, handler: Callable[..., Any] |  | ||||||
|     ) -> Callable[..., Any]: |  | ||||||
|         """Enables the process of creating a global exception handler as a convenience. |         """Enables the process of creating a global exception handler as a convenience. | ||||||
|  |  | ||||||
|         This following two examples are equivalent: |         This following two examples are equivalent: | ||||||
|   | |||||||
| @@ -120,16 +120,12 @@ class ListenerMixin(metaclass=SanicMeta): | |||||||
|  |  | ||||||
|         if callable(listener_or_event): |         if callable(listener_or_event): | ||||||
|             if event_or_none is None: |             if event_or_none is None: | ||||||
|                 raise BadRequest( |                 raise BadRequest("Invalid event registration: Missing event name.") | ||||||
|                     "Invalid event registration: Missing event name." |  | ||||||
|                 ) |  | ||||||
|             return register_listener(listener_or_event, event_or_none) |             return register_listener(listener_or_event, event_or_none) | ||||||
|         else: |         else: | ||||||
|             return partial(register_listener, event=listener_or_event) |             return partial(register_listener, event=listener_or_event) | ||||||
|  |  | ||||||
|     def main_process_start( |     def main_process_start(self, listener: ListenerType[Sanic]) -> ListenerType[Sanic]: | ||||||
|         self, listener: ListenerType[Sanic] |  | ||||||
|     ) -> ListenerType[Sanic]: |  | ||||||
|         """Decorator for registering a listener for the main_process_start event. |         """Decorator for registering a listener for the main_process_start event. | ||||||
|  |  | ||||||
|         This event is fired only on the main process and **NOT** on any |         This event is fired only on the main process and **NOT** on any | ||||||
| @@ -151,9 +147,7 @@ class ListenerMixin(metaclass=SanicMeta): | |||||||
|         """  # noqa: E501 |         """  # noqa: E501 | ||||||
|         return self.listener(listener, "main_process_start") |         return self.listener(listener, "main_process_start") | ||||||
|  |  | ||||||
|     def main_process_ready( |     def main_process_ready(self, listener: ListenerType[Sanic]) -> ListenerType[Sanic]: | ||||||
|         self, listener: ListenerType[Sanic] |  | ||||||
|     ) -> ListenerType[Sanic]: |  | ||||||
|         """Decorator for registering a listener for the main_process_ready event. |         """Decorator for registering a listener for the main_process_ready event. | ||||||
|  |  | ||||||
|         This event is fired only on the main process and **NOT** on any |         This event is fired only on the main process and **NOT** on any | ||||||
| @@ -176,9 +170,7 @@ class ListenerMixin(metaclass=SanicMeta): | |||||||
|         """  # noqa: E501 |         """  # noqa: E501 | ||||||
|         return self.listener(listener, "main_process_ready") |         return self.listener(listener, "main_process_ready") | ||||||
|  |  | ||||||
|     def main_process_stop( |     def main_process_stop(self, listener: ListenerType[Sanic]) -> ListenerType[Sanic]: | ||||||
|         self, listener: ListenerType[Sanic] |  | ||||||
|     ) -> ListenerType[Sanic]: |  | ||||||
|         """Decorator for registering a listener for the main_process_stop event. |         """Decorator for registering a listener for the main_process_stop event. | ||||||
|  |  | ||||||
|         This event is fired only on the main process and **NOT** on any |         This event is fired only on the main process and **NOT** on any | ||||||
| @@ -222,9 +214,7 @@ class ListenerMixin(metaclass=SanicMeta): | |||||||
|         """  # noqa: E501 |         """  # noqa: E501 | ||||||
|         return self.listener(listener, "reload_process_start") |         return self.listener(listener, "reload_process_start") | ||||||
|  |  | ||||||
|     def reload_process_stop( |     def reload_process_stop(self, listener: ListenerType[Sanic]) -> ListenerType[Sanic]: | ||||||
|         self, listener: ListenerType[Sanic] |  | ||||||
|     ) -> ListenerType[Sanic]: |  | ||||||
|         """Decorator for registering a listener for the reload_process_stop event. |         """Decorator for registering a listener for the reload_process_stop event. | ||||||
|  |  | ||||||
|         This event is fired only on the reload process and **NOT** on any |         This event is fired only on the reload process and **NOT** on any | ||||||
| @@ -293,9 +283,7 @@ class ListenerMixin(metaclass=SanicMeta): | |||||||
|         """  # noqa: E501 |         """  # noqa: E501 | ||||||
|         return self.listener(listener, "after_reload_trigger") |         return self.listener(listener, "after_reload_trigger") | ||||||
|  |  | ||||||
|     def before_server_start( |     def before_server_start(self, listener: ListenerType[Sanic]) -> ListenerType[Sanic]: | ||||||
|         self, listener: ListenerType[Sanic] |  | ||||||
|     ) -> ListenerType[Sanic]: |  | ||||||
|         """Decorator for registering a listener for the before_server_start event. |         """Decorator for registering a listener for the before_server_start event. | ||||||
|  |  | ||||||
|         This event is fired on all worker processes. You should typically |         This event is fired on all worker processes. You should typically | ||||||
| @@ -319,9 +307,7 @@ class ListenerMixin(metaclass=SanicMeta): | |||||||
|         """  # noqa: E501 |         """  # noqa: E501 | ||||||
|         return self.listener(listener, "before_server_start") |         return self.listener(listener, "before_server_start") | ||||||
|  |  | ||||||
|     def after_server_start( |     def after_server_start(self, listener: ListenerType[Sanic]) -> ListenerType[Sanic]: | ||||||
|         self, listener: ListenerType[Sanic] |  | ||||||
|     ) -> ListenerType[Sanic]: |  | ||||||
|         """Decorator for registering a listener for the after_server_start event. |         """Decorator for registering a listener for the after_server_start event. | ||||||
|  |  | ||||||
|         This event is fired on all worker processes. You should typically |         This event is fired on all worker processes. You should typically | ||||||
| @@ -349,9 +335,7 @@ class ListenerMixin(metaclass=SanicMeta): | |||||||
|         """  # noqa: E501 |         """  # noqa: E501 | ||||||
|         return self.listener(listener, "after_server_start") |         return self.listener(listener, "after_server_start") | ||||||
|  |  | ||||||
|     def before_server_stop( |     def before_server_stop(self, listener: ListenerType[Sanic]) -> ListenerType[Sanic]: | ||||||
|         self, listener: ListenerType[Sanic] |  | ||||||
|     ) -> ListenerType[Sanic]: |  | ||||||
|         """Decorator for registering a listener for the before_server_stop event. |         """Decorator for registering a listener for the before_server_stop event. | ||||||
|  |  | ||||||
|         This event is fired on all worker processes. This event is fired |         This event is fired on all worker processes. This event is fired | ||||||
| @@ -376,9 +360,7 @@ class ListenerMixin(metaclass=SanicMeta): | |||||||
|         """  # noqa: E501 |         """  # noqa: E501 | ||||||
|         return self.listener(listener, "before_server_stop") |         return self.listener(listener, "before_server_stop") | ||||||
|  |  | ||||||
|     def after_server_stop( |     def after_server_stop(self, listener: ListenerType[Sanic]) -> ListenerType[Sanic]: | ||||||
|         self, listener: ListenerType[Sanic] |  | ||||||
|     ) -> ListenerType[Sanic]: |  | ||||||
|         """Decorator for registering a listener for the after_server_stop event. |         """Decorator for registering a listener for the after_server_stop event. | ||||||
|  |  | ||||||
|         This event is fired on all worker processes. This event is fired |         This event is fired on all worker processes. This event is fired | ||||||
|   | |||||||
| @@ -25,7 +25,7 @@ class MiddlewareMixin(metaclass=SanicMeta): | |||||||
|         attach_to: str = "request", |         attach_to: str = "request", | ||||||
|         apply: bool = True, |         apply: bool = True, | ||||||
|         *, |         *, | ||||||
|         priority: int = 0 |         priority: int = 0, | ||||||
|     ) -> MiddlewareType: |     ) -> MiddlewareType: | ||||||
|         ... |         ... | ||||||
|  |  | ||||||
| @@ -36,7 +36,7 @@ class MiddlewareMixin(metaclass=SanicMeta): | |||||||
|         attach_to: str = "request", |         attach_to: str = "request", | ||||||
|         apply: bool = True, |         apply: bool = True, | ||||||
|         *, |         *, | ||||||
|         priority: int = 0 |         priority: int = 0, | ||||||
|     ) -> Callable[[MiddlewareType], MiddlewareType]: |     ) -> Callable[[MiddlewareType], MiddlewareType]: | ||||||
|         ... |         ... | ||||||
|  |  | ||||||
| @@ -46,7 +46,7 @@ class MiddlewareMixin(metaclass=SanicMeta): | |||||||
|         attach_to: str = "request", |         attach_to: str = "request", | ||||||
|         apply: bool = True, |         apply: bool = True, | ||||||
|         *, |         *, | ||||||
|         priority: int = 0 |         priority: int = 0, | ||||||
|     ) -> Union[MiddlewareType, Callable[[MiddlewareType], MiddlewareType]]: |     ) -> Union[MiddlewareType, Callable[[MiddlewareType], MiddlewareType]]: | ||||||
|         """Decorator for registering middleware. |         """Decorator for registering middleware. | ||||||
|  |  | ||||||
| @@ -99,13 +99,9 @@ class MiddlewareMixin(metaclass=SanicMeta): | |||||||
|  |  | ||||||
|         # Detect which way this was called, @middleware or @middleware('AT') |         # Detect which way this was called, @middleware or @middleware('AT') | ||||||
|         if callable(middleware_or_request): |         if callable(middleware_or_request): | ||||||
|             return register_middleware( |             return register_middleware(middleware_or_request, attach_to=attach_to) | ||||||
|                 middleware_or_request, attach_to=attach_to |  | ||||||
|             ) |  | ||||||
|         else: |         else: | ||||||
|             return partial( |             return partial(register_middleware, attach_to=middleware_or_request) | ||||||
|                 register_middleware, attach_to=middleware_or_request |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|     def on_request(self, middleware=None, *, priority=0) -> MiddlewareType: |     def on_request(self, middleware=None, *, priority=0) -> MiddlewareType: | ||||||
|         """Register a middleware to be called before a request is handled. |         """Register a middleware to be called before a request is handled. | ||||||
| @@ -157,9 +153,7 @@ class MiddlewareMixin(metaclass=SanicMeta): | |||||||
|         if callable(middleware): |         if callable(middleware): | ||||||
|             return self.middleware(middleware, "response", priority=priority) |             return self.middleware(middleware, "response", priority=priority) | ||||||
|         else: |         else: | ||||||
|             return partial( |             return partial(self.middleware, attach_to="response", priority=priority) | ||||||
|                 self.middleware, attach_to="response", priority=priority |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|     def finalize_middleware(self) -> None: |     def finalize_middleware(self) -> None: | ||||||
|         """Finalize the middleware configuration for the Sanic application. |         """Finalize the middleware configuration for the Sanic application. | ||||||
|   | |||||||
| @@ -25,10 +25,7 @@ from sanic.models.futures import FutureRoute, FutureStatic | |||||||
| from sanic.models.handler_types import RouteHandler | from sanic.models.handler_types import RouteHandler | ||||||
| from sanic.types import HashableDict | from sanic.types import HashableDict | ||||||
|  |  | ||||||
|  | RouteWrapper = Callable[[RouteHandler], Union[RouteHandler, Tuple[Route, RouteHandler]]] | ||||||
| RouteWrapper = Callable[ |  | ||||||
|     [RouteHandler], Union[RouteHandler, Tuple[Route, RouteHandler]] |  | ||||||
| ] |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class RouteMixin(BaseMixin, metaclass=SanicMeta): | class RouteMixin(BaseMixin, metaclass=SanicMeta): | ||||||
| @@ -815,7 +812,5 @@ class RouteMixin(BaseMixin, metaclass=SanicMeta): | |||||||
|         } |         } | ||||||
|         if raw: |         if raw: | ||||||
|             unexpected_arguments = ", ".join(raw.keys()) |             unexpected_arguments = ", ".join(raw.keys()) | ||||||
|             raise TypeError( |             raise TypeError(f"Unexpected keyword arguments: {unexpected_arguments}") | ||||||
|                 f"Unexpected keyword arguments: {unexpected_arguments}" |  | ||||||
|             ) |  | ||||||
|         return HashableDict(ctx_kwargs) |         return HashableDict(ctx_kwargs) | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
| from enum import Enum | from enum import Enum | ||||||
| from typing import Any, Callable, Coroutine, Dict, Optional, Set, Union | from typing import Any, Callable, Coroutine | ||||||
|  |  | ||||||
| from sanic.base.meta import SanicMeta | from sanic.base.meta import SanicMeta | ||||||
| from sanic.models.futures import FutureSignal | from sanic.models.futures import FutureSignal | ||||||
| @@ -12,17 +12,17 @@ from sanic.types import HashableDict | |||||||
|  |  | ||||||
| class SignalMixin(metaclass=SanicMeta): | class SignalMixin(metaclass=SanicMeta): | ||||||
|     def __init__(self, *args, **kwargs) -> None: |     def __init__(self, *args, **kwargs) -> None: | ||||||
|         self._future_signals: Set[FutureSignal] = set() |         self._future_signals: set[FutureSignal] = set() | ||||||
|  |  | ||||||
|     def _apply_signal(self, signal: FutureSignal) -> Signal: |     def _apply_signal(self, signal: FutureSignal) -> Signal: | ||||||
|         raise NotImplementedError  # noqa |         raise NotImplementedError  # noqa | ||||||
|  |  | ||||||
|     def signal( |     def signal( | ||||||
|         self, |         self, | ||||||
|         event: Union[str, Enum], |         event: str | Enum, | ||||||
|         *, |         *, | ||||||
|         apply: bool = True, |         apply: bool = True, | ||||||
|         condition: Optional[Dict[str, Any]] = None, |         condition: dict[str, Any] | None = None, | ||||||
|         exclusive: bool = True, |         exclusive: bool = True, | ||||||
|     ) -> Callable[[SignalHandler], SignalHandler]: |     ) -> Callable[[SignalHandler], SignalHandler]: | ||||||
|         """ |         """ | ||||||
| @@ -64,9 +64,9 @@ class SignalMixin(metaclass=SanicMeta): | |||||||
|  |  | ||||||
|     def add_signal( |     def add_signal( | ||||||
|         self, |         self, | ||||||
|         handler: Optional[Callable[..., Any]], |         handler: Callable[..., Any] | None, | ||||||
|         event: str, |         event: str, | ||||||
|         condition: Optional[Dict[str, Any]] = None, |         condition: dict[str, Any] | None = None, | ||||||
|         exclusive: bool = True, |         exclusive: bool = True, | ||||||
|     ) -> Callable[..., Any]: |     ) -> Callable[..., Any]: | ||||||
|         """Registers a signal handler for a specific event. |         """Registers a signal handler for a specific event. | ||||||
| @@ -92,9 +92,7 @@ class SignalMixin(metaclass=SanicMeta): | |||||||
|                 ... |                 ... | ||||||
|  |  | ||||||
|             handler = noop |             handler = noop | ||||||
|         self.signal(event=event, condition=condition, exclusive=exclusive)( |         self.signal(event=event, condition=condition, exclusive=exclusive)(handler) | ||||||
|             handler |  | ||||||
|         ) |  | ||||||
|         return handler |         return handler | ||||||
|  |  | ||||||
|     def event(self, event: str): |     def event(self, event: str): | ||||||
|   | |||||||
| @@ -2,8 +2,6 @@ from __future__ import annotations | |||||||
|  |  | ||||||
| import os | import os | ||||||
| import platform | import platform | ||||||
| import sys |  | ||||||
|  |  | ||||||
| from asyncio import ( | from asyncio import ( | ||||||
|     AbstractEventLoop, |     AbstractEventLoop, | ||||||
|     CancelledError, |     CancelledError, | ||||||
| @@ -32,13 +30,7 @@ from typing import ( | |||||||
|     Any, |     Any, | ||||||
|     Callable, |     Callable, | ||||||
|     ClassVar, |     ClassVar, | ||||||
|     Dict, |  | ||||||
|     List, |  | ||||||
|     Mapping, |     Mapping, | ||||||
|     Optional, |  | ||||||
|     Set, |  | ||||||
|     Tuple, |  | ||||||
|     Type, |  | ||||||
|     Union, |     Union, | ||||||
|     cast, |     cast, | ||||||
| ) | ) | ||||||
| @@ -71,7 +63,6 @@ from sanic.worker.multiplexer import WorkerMultiplexer | |||||||
| from sanic.worker.reloader import Reloader | from sanic.worker.reloader import Reloader | ||||||
| from sanic.worker.serve import worker_serve | from sanic.worker.serve import worker_serve | ||||||
|  |  | ||||||
|  |  | ||||||
| if TYPE_CHECKING: | if TYPE_CHECKING: | ||||||
|     from sanic import Sanic |     from sanic import Sanic | ||||||
|     from sanic.application.state import ApplicationState |     from sanic.application.state import ApplicationState | ||||||
| @@ -79,20 +70,17 @@ if TYPE_CHECKING: | |||||||
|  |  | ||||||
| SANIC_PACKAGES = ("sanic-routing", "sanic-testing", "sanic-ext") | SANIC_PACKAGES = ("sanic-routing", "sanic-testing", "sanic-ext") | ||||||
|  |  | ||||||
| if sys.version_info < (3, 8):  # no cov |  | ||||||
|     HTTPVersion = Union[HTTP, int] |  | ||||||
| else:  # no cov |  | ||||||
| from typing import Literal | from typing import Literal | ||||||
|  |  | ||||||
| HTTPVersion = Union[HTTP, Literal[1], Literal[3]] | HTTPVersion = Union[HTTP, Literal[1], Literal[3]] | ||||||
|  |  | ||||||
|  |  | ||||||
| class StartupMixin(metaclass=SanicMeta): | class StartupMixin(metaclass=SanicMeta): | ||||||
|     _app_registry: ClassVar[Dict[str, Sanic]] |     _app_registry: ClassVar[dict[str, Sanic]] | ||||||
|  |  | ||||||
|     asgi: bool |     asgi: bool | ||||||
|     config: Config |     config: Config | ||||||
|     listeners: Dict[str, List[ListenerType[Any]]] |     listeners: dict[str, list[ListenerType[Any]]] | ||||||
|     state: ApplicationState |     state: ApplicationState | ||||||
|     websocket_enabled: bool |     websocket_enabled: bool | ||||||
|     multiplexer: WorkerMultiplexer |     multiplexer: WorkerMultiplexer | ||||||
| @@ -112,8 +100,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|         """ |         """ | ||||||
|         if not self.asgi: |         if not self.asgi: | ||||||
|             if self.config.USE_UVLOOP is True or ( |             if self.config.USE_UVLOOP is True or ( | ||||||
|                 isinstance(self.config.USE_UVLOOP, Default) |                 isinstance(self.config.USE_UVLOOP, Default) and not OS_IS_WINDOWS | ||||||
|                 and not OS_IS_WINDOWS |  | ||||||
|             ): |             ): | ||||||
|                 try_use_uvloop() |                 try_use_uvloop() | ||||||
|             elif OS_IS_WINDOWS: |             elif OS_IS_WINDOWS: | ||||||
| @@ -159,28 +146,28 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|  |  | ||||||
|     def run( |     def run( | ||||||
|         self, |         self, | ||||||
|         host: Optional[str] = None, |         host: str | None = None, | ||||||
|         port: Optional[int] = None, |         port: int | None = None, | ||||||
|         *, |         *, | ||||||
|         dev: bool = False, |         dev: bool = False, | ||||||
|         debug: bool = False, |         debug: bool = False, | ||||||
|         auto_reload: Optional[bool] = None, |         auto_reload: bool | None = None, | ||||||
|         version: HTTPVersion = HTTP.VERSION_1, |         version: HTTPVersion = HTTP.VERSION_1, | ||||||
|         ssl: Union[None, SSLContext, dict, str, list, tuple] = None, |         ssl: None | SSLContext | dict | str | list | tuple = None, | ||||||
|         sock: Optional[socket] = None, |         sock: socket | None = None, | ||||||
|         workers: int = 1, |         workers: int = 1, | ||||||
|         protocol: Optional[Type[Protocol]] = None, |         protocol: type[Protocol] | None = None, | ||||||
|         backlog: int = 100, |         backlog: int = 100, | ||||||
|         register_sys_signals: bool = True, |         register_sys_signals: bool = True, | ||||||
|         access_log: Optional[bool] = None, |         access_log: bool | None = None, | ||||||
|         unix: Optional[str] = None, |         unix: str | None = None, | ||||||
|         loop: Optional[AbstractEventLoop] = None, |         loop: AbstractEventLoop | None = None, | ||||||
|         reload_dir: Optional[Union[List[str], str]] = None, |         reload_dir: list[str] | str | None = None, | ||||||
|         noisy_exceptions: Optional[bool] = None, |         noisy_exceptions: bool | None = None, | ||||||
|         motd: bool = True, |         motd: bool = True, | ||||||
|         fast: bool = False, |         fast: bool = False, | ||||||
|         verbosity: int = 0, |         verbosity: int = 0, | ||||||
|         motd_display: Optional[Dict[str, str]] = None, |         motd_display: dict[str, str] | None = None, | ||||||
|         auto_tls: bool = False, |         auto_tls: bool = False, | ||||||
|         single_process: bool = False, |         single_process: bool = False, | ||||||
|     ) -> None: |     ) -> None: | ||||||
| @@ -289,28 +276,28 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|  |  | ||||||
|     def prepare( |     def prepare( | ||||||
|         self, |         self, | ||||||
|         host: Optional[str] = None, |         host: str | None = None, | ||||||
|         port: Optional[int] = None, |         port: int | None = None, | ||||||
|         *, |         *, | ||||||
|         dev: bool = False, |         dev: bool = False, | ||||||
|         debug: bool = False, |         debug: bool = False, | ||||||
|         auto_reload: Optional[bool] = None, |         auto_reload: bool | None = None, | ||||||
|         version: HTTPVersion = HTTP.VERSION_1, |         version: HTTPVersion = HTTP.VERSION_1, | ||||||
|         ssl: Union[None, SSLContext, dict, str, list, tuple] = None, |         ssl: None | SSLContext | dict | str | list | tuple = None, | ||||||
|         sock: Optional[socket] = None, |         sock: socket | None = None, | ||||||
|         workers: int = 1, |         workers: int = 1, | ||||||
|         protocol: Optional[Type[Protocol]] = None, |         protocol: type[Protocol] | None = None, | ||||||
|         backlog: int = 100, |         backlog: int = 100, | ||||||
|         register_sys_signals: bool = True, |         register_sys_signals: bool = True, | ||||||
|         access_log: Optional[bool] = None, |         access_log: bool | None = None, | ||||||
|         unix: Optional[str] = None, |         unix: str | None = None, | ||||||
|         loop: Optional[AbstractEventLoop] = None, |         loop: AbstractEventLoop | None = None, | ||||||
|         reload_dir: Optional[Union[List[str], str]] = None, |         reload_dir: list[str] | str | None = None, | ||||||
|         noisy_exceptions: Optional[bool] = None, |         noisy_exceptions: bool | None = None, | ||||||
|         motd: bool = True, |         motd: bool = True, | ||||||
|         fast: bool = False, |         fast: bool = False, | ||||||
|         verbosity: int = 0, |         verbosity: int = 0, | ||||||
|         motd_display: Optional[Dict[str, str]] = None, |         motd_display: dict[str, str] | None = None, | ||||||
|         coffee: bool = False, |         coffee: bool = False, | ||||||
|         auto_tls: bool = False, |         auto_tls: bool = False, | ||||||
|         single_process: bool = False, |         single_process: bool = False, | ||||||
| @@ -385,8 +372,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|  |  | ||||||
|         if single_process and (fast or (workers > 1) or auto_reload): |         if single_process and (fast or (workers > 1) or auto_reload): | ||||||
|             raise RuntimeError( |             raise RuntimeError( | ||||||
|                 "Single process cannot be run with multiple workers " |                 "Single process cannot be run with multiple workers " "or auto-reload" | ||||||
|                 "or auto-reload" |  | ||||||
|             ) |             ) | ||||||
|  |  | ||||||
|         if register_sys_signals is False and not single_process: |         if register_sys_signals is False and not single_process: | ||||||
| @@ -405,9 +391,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|             for directory in reload_dir: |             for directory in reload_dir: | ||||||
|                 direc = Path(directory) |                 direc = Path(directory) | ||||||
|                 if not direc.is_dir(): |                 if not direc.is_dir(): | ||||||
|                     logger.warning( |                     logger.warning(f"Directory {directory} could not be located") | ||||||
|                         f"Directory {directory} could not be located" |  | ||||||
|                     ) |  | ||||||
|                 self.state.reload_dirs.add(Path(directory)) |                 self.state.reload_dirs.add(Path(directory)) | ||||||
|  |  | ||||||
|         if loop is not None: |         if loop is not None: | ||||||
| @@ -422,9 +406,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|             host, port = self.get_address(host, port, version, auto_tls) |             host, port = self.get_address(host, port, version, auto_tls) | ||||||
|  |  | ||||||
|         if protocol is None: |         if protocol is None: | ||||||
|             protocol = ( |             protocol = WebSocketProtocol if self.websocket_enabled else HttpProtocol | ||||||
|                 WebSocketProtocol if self.websocket_enabled else HttpProtocol |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|         # Set explicitly passed configuration values |         # Set explicitly passed configuration values | ||||||
|         for attribute, value in { |         for attribute, value in { | ||||||
| @@ -460,9 +442,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|             register_sys_signals=register_sys_signals, |             register_sys_signals=register_sys_signals, | ||||||
|             auto_tls=auto_tls, |             auto_tls=auto_tls, | ||||||
|         ) |         ) | ||||||
|         self.state.server_info.append( |         self.state.server_info.append(ApplicationServerInfo(settings=server_settings)) | ||||||
|             ApplicationServerInfo(settings=server_settings) |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         # if self.config.USE_UVLOOP is True or ( |         # if self.config.USE_UVLOOP is True or ( | ||||||
|         #     self.config.USE_UVLOOP is _default and not OS_IS_WINDOWS |         #     self.config.USE_UVLOOP is _default and not OS_IS_WINDOWS | ||||||
| @@ -471,20 +451,20 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|  |  | ||||||
|     async def create_server( |     async def create_server( | ||||||
|         self, |         self, | ||||||
|         host: Optional[str] = None, |         host: str | None = None, | ||||||
|         port: Optional[int] = None, |         port: int | None = None, | ||||||
|         *, |         *, | ||||||
|         debug: bool = False, |         debug: bool = False, | ||||||
|         ssl: Union[None, SSLContext, dict, str, list, tuple] = None, |         ssl: None | SSLContext | dict | str | list | tuple = None, | ||||||
|         sock: Optional[socket] = None, |         sock: socket | None = None, | ||||||
|         protocol: Optional[Type[Protocol]] = None, |         protocol: type[Protocol] | None = None, | ||||||
|         backlog: int = 100, |         backlog: int = 100, | ||||||
|         access_log: Optional[bool] = None, |         access_log: bool | None = None, | ||||||
|         unix: Optional[str] = None, |         unix: str | None = None, | ||||||
|         return_asyncio_server: bool = True, |         return_asyncio_server: bool = True, | ||||||
|         asyncio_server_kwargs: Optional[Dict[str, Any]] = None, |         asyncio_server_kwargs: dict[str, Any] | None = None, | ||||||
|         noisy_exceptions: Optional[bool] = None, |         noisy_exceptions: bool | None = None, | ||||||
|     ) -> Optional[AsyncioServer]: |     ) -> AsyncioServer | None: | ||||||
|         """ |         """ | ||||||
|         Low level API for creating a Sanic Server instance. |         Low level API for creating a Sanic Server instance. | ||||||
|  |  | ||||||
| @@ -558,9 +538,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|             host, port = host, port = self.get_address(host, port) |             host, port = host, port = self.get_address(host, port) | ||||||
|  |  | ||||||
|         if protocol is None: |         if protocol is None: | ||||||
|             protocol = ( |             protocol = WebSocketProtocol if self.websocket_enabled else HttpProtocol | ||||||
|                 WebSocketProtocol if self.websocket_enabled else HttpProtocol |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|         # Set explicitly passed configuration values |         # Set explicitly passed configuration values | ||||||
|         for attribute, value in { |         for attribute, value in { | ||||||
| @@ -637,21 +615,21 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|  |  | ||||||
|     def _helper( |     def _helper( | ||||||
|         self, |         self, | ||||||
|         host: Optional[str] = None, |         host: str | None = None, | ||||||
|         port: Optional[int] = None, |         port: int | None = None, | ||||||
|         debug: bool = False, |         debug: bool = False, | ||||||
|         version: HTTPVersion = HTTP.VERSION_1, |         version: HTTPVersion = HTTP.VERSION_1, | ||||||
|         ssl: Union[None, SSLContext, dict, str, list, tuple] = None, |         ssl: None | SSLContext | dict | str | list | tuple = None, | ||||||
|         sock: Optional[socket] = None, |         sock: socket | None = None, | ||||||
|         unix: Optional[str] = None, |         unix: str | None = None, | ||||||
|         workers: int = 1, |         workers: int = 1, | ||||||
|         loop: Optional[AbstractEventLoop] = None, |         loop: AbstractEventLoop | None = None, | ||||||
|         protocol: Type[Protocol] = HttpProtocol, |         protocol: type[Protocol] = HttpProtocol, | ||||||
|         backlog: int = 100, |         backlog: int = 100, | ||||||
|         register_sys_signals: bool = True, |         register_sys_signals: bool = True, | ||||||
|         run_async: bool = False, |         run_async: bool = False, | ||||||
|         auto_tls: bool = False, |         auto_tls: bool = False, | ||||||
|     ) -> Dict[str, Any]: |     ) -> dict[str, Any]: | ||||||
|         """Helper function used by `run` and `create_server`.""" |         """Helper function used by `run` and `create_server`.""" | ||||||
|         if self.config.PROXIES_COUNT and self.config.PROXIES_COUNT < 0: |         if self.config.PROXIES_COUNT and self.config.PROXIES_COUNT < 0: | ||||||
|             raise ValueError( |             raise ValueError( | ||||||
| @@ -726,7 +704,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|  |  | ||||||
|     def motd( |     def motd( | ||||||
|         self, |         self, | ||||||
|         server_settings: Optional[Dict[str, Any]] = None, |         server_settings: dict[str, Any] | None = None, | ||||||
|     ) -> None: |     ) -> None: | ||||||
|         """Outputs the message of the day (MOTD). |         """Outputs the message of the day (MOTD). | ||||||
|  |  | ||||||
| @@ -755,8 +733,8 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|             MOTD.output(logo, serve_location, display, extra) |             MOTD.output(logo, serve_location, display, extra) | ||||||
|  |  | ||||||
|     def get_motd_data( |     def get_motd_data( | ||||||
|         self, server_settings: Optional[Dict[str, Any]] = None |         self, server_settings: dict[str, Any] | None = None | ||||||
|     ) -> Tuple[Dict[str, Any], Dict[str, Any]]: |     ) -> tuple[dict[str, Any], dict[str, Any]]: | ||||||
|         """Retrieves the message of the day (MOTD) data. |         """Retrieves the message of the day (MOTD) data. | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
| @@ -802,10 +780,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|                 reload_display += ", ".join( |                 reload_display += ", ".join( | ||||||
|                     [ |                     [ | ||||||
|                         "", |                         "", | ||||||
|                         *( |                         *(str(path.absolute()) for path in self.state.reload_dirs), | ||||||
|                             str(path.absolute()) |  | ||||||
|                             for path in self.state.reload_dirs |  | ||||||
|                         ), |  | ||||||
|                     ] |                     ] | ||||||
|                 ) |                 ) | ||||||
|             display["auto-reload"] = reload_display |             display["auto-reload"] = reload_display | ||||||
| @@ -844,9 +819,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|             return f"http://<{location}>" |             return f"http://<{location}>" | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def get_server_location( |     def get_server_location(server_settings: dict[str, Any] | None = None) -> str: | ||||||
|         server_settings: Optional[Dict[str, Any]] = None |  | ||||||
|     ) -> str: |  | ||||||
|         """Using the server settings, retrieve the server location. |         """Using the server settings, retrieve the server location. | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
| @@ -880,11 +853,11 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def get_address( |     def get_address( | ||||||
|         host: Optional[str], |         host: str | None, | ||||||
|         port: Optional[int], |         port: int | None, | ||||||
|         version: HTTPVersion = HTTP.VERSION_1, |         version: HTTPVersion = HTTP.VERSION_1, | ||||||
|         auto_tls: bool = False, |         auto_tls: bool = False, | ||||||
|     ) -> Tuple[str, int]: |     ) -> tuple[str, int]: | ||||||
|         """Retrieve the host address and port, with default values based on the given parameters. |         """Retrieve the host address and port, with default values based on the given parameters. | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
| @@ -913,9 +886,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|     @classmethod |     @classmethod | ||||||
|     def _get_startup_method(cls) -> str: |     def _get_startup_method(cls) -> str: | ||||||
|         return ( |         return ( | ||||||
|             cls.start_method |             cls.start_method if not isinstance(cls.start_method, Default) else "spawn" | ||||||
|             if not isinstance(cls.start_method, Default) |  | ||||||
|             else "spawn" |  | ||||||
|         ) |         ) | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
| @@ -942,10 +913,10 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|     @classmethod |     @classmethod | ||||||
|     def serve( |     def serve( | ||||||
|         cls, |         cls, | ||||||
|         primary: Optional[Sanic] = None, |         primary: Sanic | None = None, | ||||||
|         *, |         *, | ||||||
|         app_loader: Optional[AppLoader] = None, |         app_loader: AppLoader | None = None, | ||||||
|         factory: Optional[Callable[[], Sanic]] = None, |         factory: Callable[[], Sanic] | None = None, | ||||||
|     ) -> None: |     ) -> None: | ||||||
|         """Serve one or more Sanic applications. |         """Serve one or more Sanic applications. | ||||||
|  |  | ||||||
| @@ -996,9 +967,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|                     try: |                     try: | ||||||
|                         primary = apps[0] |                         primary = apps[0] | ||||||
|                     except IndexError: |                     except IndexError: | ||||||
|                         raise RuntimeError( |                         raise RuntimeError("Did not find any applications.") from None | ||||||
|                             "Did not find any applications." |  | ||||||
|                         ) from None |  | ||||||
|  |  | ||||||
|             # This exists primarily for unit testing |             # This exists primarily for unit testing | ||||||
|             if not primary.state.server_info:  # no cov |             if not primary.state.server_info:  # no cov | ||||||
| @@ -1040,7 +1009,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|             primary_server_info.settings["run_multiple"] = True |             primary_server_info.settings["run_multiple"] = True | ||||||
|             monitor_sub, monitor_pub = Pipe(True) |             monitor_sub, monitor_pub = Pipe(True) | ||||||
|             worker_state: Mapping[str, Any] = sync_manager.dict() |             worker_state: Mapping[str, Any] = sync_manager.dict() | ||||||
|             kwargs: Dict[str, Any] = { |             kwargs: dict[str, Any] = { | ||||||
|                 **primary_server_info.settings, |                 **primary_server_info.settings, | ||||||
|                 "monitor_publisher": monitor_pub, |                 "monitor_publisher": monitor_pub, | ||||||
|                 "worker_state": worker_state, |                 "worker_state": worker_state, | ||||||
| @@ -1092,7 +1061,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|                 worker_state, |                 worker_state, | ||||||
|             ) |             ) | ||||||
|             if cls.should_auto_reload(): |             if cls.should_auto_reload(): | ||||||
|                 reload_dirs: Set[Path] = primary.state.reload_dirs.union( |                 reload_dirs: set[Path] = primary.state.reload_dirs.union( | ||||||
|                     *(app.state.reload_dirs for app in apps) |                     *(app.state.reload_dirs for app in apps) | ||||||
|                 ) |                 ) | ||||||
|                 reloader = Reloader(monitor_pub, 0, reload_dirs, app_loader) |                 reloader = Reloader(monitor_pub, 0, reload_dirs, app_loader) | ||||||
| @@ -1101,9 +1070,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|             inspector = None |             inspector = None | ||||||
|             if primary.config.INSPECTOR: |             if primary.config.INSPECTOR: | ||||||
|                 display, extra = primary.get_motd_data() |                 display, extra = primary.get_motd_data() | ||||||
|                 packages = [ |                 packages = [pkg.strip() for pkg in display["packages"].split(",")] | ||||||
|                     pkg.strip() for pkg in display["packages"].split(",") |  | ||||||
|                 ] |  | ||||||
|                 module = import_module("sanic") |                 module = import_module("sanic") | ||||||
|                 sanic_version = f"sanic=={module.__version__}"  # type: ignore |                 sanic_version = f"sanic=={module.__version__}"  # type: ignore | ||||||
|                 app_info = { |                 app_info = { | ||||||
| @@ -1134,9 +1101,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|             exit_code = 1 |             exit_code = 1 | ||||||
|         except BaseException: |         except BaseException: | ||||||
|             kwargs = primary_server_info.settings |             kwargs = primary_server_info.settings | ||||||
|             error_logger.exception( |             error_logger.exception("Experienced exception while trying to serve") | ||||||
|                 "Experienced exception while trying to serve" |  | ||||||
|             ) |  | ||||||
|             raise |             raise | ||||||
|         finally: |         finally: | ||||||
|             logger.info("Server Stopped") |             logger.info("Server Stopped") | ||||||
| @@ -1164,7 +1129,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|             os._exit(exit_code) |             os._exit(exit_code) | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def serve_single(cls, primary: Optional[Sanic] = None) -> None: |     def serve_single(cls, primary: Sanic | None = None) -> None: | ||||||
|         """Serve a single process of a Sanic application. |         """Serve a single process of a Sanic application. | ||||||
|  |  | ||||||
|         Similar to `serve`, but only serves a single process. When used, |         Similar to `serve`, but only serves a single process. When used, | ||||||
| @@ -1242,9 +1207,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|         try: |         try: | ||||||
|             worker_serve(monitor_publisher=None, **kwargs) |             worker_serve(monitor_publisher=None, **kwargs) | ||||||
|         except BaseException: |         except BaseException: | ||||||
|             error_logger.exception( |             error_logger.exception("Experienced exception while trying to serve") | ||||||
|                 "Experienced exception while trying to serve" |  | ||||||
|             ) |  | ||||||
|             raise |             raise | ||||||
|         finally: |         finally: | ||||||
|             logger.info("Server Stopped") |             logger.info("Server Stopped") | ||||||
| @@ -1263,7 +1226,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|         self, |         self, | ||||||
|         primary: Sanic, |         primary: Sanic, | ||||||
|         _, |         _, | ||||||
|         apps: List[Sanic], |         apps: list[Sanic], | ||||||
|     ) -> None: |     ) -> None: | ||||||
|         for app in apps: |         for app in apps: | ||||||
|             if ( |             if ( | ||||||
| @@ -1308,7 +1271,7 @@ class StartupMixin(metaclass=SanicMeta): | |||||||
|                     if not server_info.settings["loop"]: |                     if not server_info.settings["loop"]: | ||||||
|                         server_info.settings["loop"] = get_running_loop() |                         server_info.settings["loop"] = get_running_loop() | ||||||
|  |  | ||||||
|                     serve_args: Dict[str, Any] = { |                     serve_args: dict[str, Any] = { | ||||||
|                         **server_info.settings, |                         **server_info.settings, | ||||||
|                         "run_async": True, |                         "run_async": True, | ||||||
|                         "reuse_port": bool(primary.state.workers - 1), |                         "reuse_port": bool(primary.state.workers - 1), | ||||||
|   | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user