parent
35ccd5d9f9
commit
decc1999e9
|
@ -1,360 +0,0 @@
|
||||||
From c9b555ff284bb54a45adab44c8e6d78ab8713da0 Mon Sep 17 00:00:00 2001
|
|
||||||
From: pgjones <philip.graham.jones@googlemail.com>
|
|
||||||
Date: Sun, 9 Apr 2023 16:57:26 +0100
|
|
||||||
Subject: [PATCH] Upgrade to blinker 1.6
|
|
||||||
|
|
||||||
This allows the custom signal implementation to be removed and for
|
|
||||||
sync signal receivers to be supported in the same manner as sync route
|
|
||||||
handlers.
|
|
||||||
---
|
|
||||||
pyproject.toml | 2 +-
|
|
||||||
src/quart/app.py | 38 +++++++++++++++++++++-----------
|
|
||||||
src/quart/asgi.py | 4 ++--
|
|
||||||
src/quart/ctx.py | 4 ++--
|
|
||||||
src/quart/helpers.py | 5 +++--
|
|
||||||
src/quart/signals.py | 48 ++---------------------------------------
|
|
||||||
src/quart/templating.py | 16 ++++++++++----
|
|
||||||
tests/test_signals.py | 37 -------------------------------
|
|
||||||
8 files changed, 48 insertions(+), 106 deletions(-)
|
|
||||||
delete mode 100644 tests/test_signals.py
|
|
||||||
|
|
||||||
diff --git a/pyproject.toml b/pyproject.toml
|
|
||||||
index da35a647..9d4876a0 100644
|
|
||||||
--- a/pyproject.toml
|
|
||||||
+++ b/pyproject.toml
|
|
||||||
@@ -27,7 +27,7 @@
|
|
||||||
[tool.poetry.dependencies]
|
|
||||||
python = ">=3.7"
|
|
||||||
aiofiles = "*"
|
|
||||||
-blinker = "<1.6"
|
|
||||||
+blinker = ">=1.6"
|
|
||||||
click = ">=8.0.0"
|
|
||||||
hypercorn = ">=0.11.2"
|
|
||||||
importlib_metadata = { version = "*", python = "<3.10" }
|
|
||||||
diff --git a/src/quart/app.py b/src/quart/app.py
|
|
||||||
index 8ee111a7..e3d3cc0f 100644
|
|
||||||
--- a/src/quart/app.py
|
|
||||||
+++ b/src/quart/app.py
|
|
||||||
@@ -1113,7 +1113,9 @@
|
|
||||||
By default this switches the error response to a 500 internal
|
|
||||||
server error.
|
|
||||||
"""
|
|
||||||
- await got_request_exception.send(self, exception=error)
|
|
||||||
+ await got_request_exception.send_async(
|
|
||||||
+ self, _sync_wrapper=self.ensure_async, exception=error
|
|
||||||
+ )
|
|
||||||
|
|
||||||
self.log_exception(sys.exc_info())
|
|
||||||
|
|
||||||
@@ -1138,7 +1140,9 @@
|
|
||||||
|
|
||||||
By default this logs the exception and then re-raises it.
|
|
||||||
"""
|
|
||||||
- await got_websocket_exception.send(self, exception=error)
|
|
||||||
+ await got_websocket_exception.send_async(
|
|
||||||
+ self, _sync_wrapper=self.ensure_async, exception=error
|
|
||||||
+ )
|
|
||||||
|
|
||||||
self.log_exception(sys.exc_info())
|
|
||||||
|
|
||||||
@@ -1245,7 +1249,7 @@
|
|
||||||
for function in self.teardown_request_funcs[name]:
|
|
||||||
await self.ensure_async(function)(exc)
|
|
||||||
|
|
||||||
- await request_tearing_down.send(self, exc=exc)
|
|
||||||
+ await request_tearing_down.send_async(self, _sync_wrapper=self.ensure_async, exc=exc)
|
|
||||||
|
|
||||||
async def do_teardown_websocket(
|
|
||||||
self, exc: Optional[BaseException], websocket_context: Optional[WebsocketContext] = None
|
|
||||||
@@ -1263,13 +1267,13 @@
|
|
||||||
for function in self.teardown_websocket_funcs[name]:
|
|
||||||
await self.ensure_async(function)(exc)
|
|
||||||
|
|
||||||
- await websocket_tearing_down.send(self, exc=exc)
|
|
||||||
+ await websocket_tearing_down.send_async(self, _sync_wrapper=self.ensure_async, exc=exc)
|
|
||||||
|
|
||||||
async def do_teardown_appcontext(self, exc: Optional[BaseException]) -> None:
|
|
||||||
"""Teardown the app (context), calling the teardown functions."""
|
|
||||||
for function in self.teardown_appcontext_funcs:
|
|
||||||
await self.ensure_async(function)(exc)
|
|
||||||
- await appcontext_tearing_down.send(self, exc=exc)
|
|
||||||
+ await appcontext_tearing_down.send_async(self, _sync_wrapper=self.ensure_async, exc=exc)
|
|
||||||
|
|
||||||
def app_context(self) -> AppContext:
|
|
||||||
"""Create and return an app context.
|
|
||||||
@@ -1558,7 +1562,9 @@
|
|
||||||
self.background_tasks.add(task)
|
|
||||||
|
|
||||||
async def handle_background_exception(self, error: Exception) -> None:
|
|
||||||
- await got_background_exception.send(self, exception=error)
|
|
||||||
+ await got_background_exception.send_async(
|
|
||||||
+ self, _sync_wrapper=self.ensure_async, exception=error
|
|
||||||
+ )
|
|
||||||
|
|
||||||
self.log_exception(sys.exc_info())
|
|
||||||
|
|
||||||
@@ -1666,7 +1672,7 @@
|
|
||||||
omits this argument.
|
|
||||||
"""
|
|
||||||
await self.try_trigger_before_first_request_functions()
|
|
||||||
- await request_started.send(self)
|
|
||||||
+ await request_started.send_async(self, _sync_wrapper=self.ensure_async)
|
|
||||||
try:
|
|
||||||
result = await self.preprocess_request(request_context)
|
|
||||||
if result is None:
|
|
||||||
@@ -1733,7 +1739,9 @@
|
|
||||||
response = await self.make_response(result)
|
|
||||||
try:
|
|
||||||
response = await self.process_response(response, request_context)
|
|
||||||
- await request_finished.send(self, response=response)
|
|
||||||
+ await request_finished.send_async(
|
|
||||||
+ self, _sync_wrapper=self.ensure_async, response=response
|
|
||||||
+ )
|
|
||||||
except Exception:
|
|
||||||
if not from_error_handler:
|
|
||||||
raise
|
|
||||||
@@ -1787,7 +1795,7 @@
|
|
||||||
the Flask convention.
|
|
||||||
"""
|
|
||||||
await self.try_trigger_before_first_request_functions()
|
|
||||||
- await websocket_started.send(self)
|
|
||||||
+ await websocket_started.send_async(self, _sync_wrapper=self.ensure_async)
|
|
||||||
try:
|
|
||||||
result = await self.preprocess_websocket(websocket_context)
|
|
||||||
if result is None:
|
|
||||||
@@ -1857,7 +1865,9 @@
|
|
||||||
response = None
|
|
||||||
try:
|
|
||||||
response = await self.postprocess_websocket(response, websocket_context)
|
|
||||||
- await websocket_finished.send(self, response=response)
|
|
||||||
+ await websocket_finished.send_async(
|
|
||||||
+ self, _sync_wrapper=self.ensure_async, response=response
|
|
||||||
+ )
|
|
||||||
except Exception:
|
|
||||||
if not from_error_handler:
|
|
||||||
raise
|
|
||||||
@@ -1937,7 +1947,9 @@
|
|
||||||
for gen in self.while_serving_gens:
|
|
||||||
await gen.__anext__()
|
|
||||||
except Exception as error:
|
|
||||||
- await got_serving_exception.send(self, exception=error)
|
|
||||||
+ await got_serving_exception.send_async(
|
|
||||||
+ self, _sync_wrapper=self.ensure_async, exception=error
|
|
||||||
+ )
|
|
||||||
self.log_exception(sys.exc_info())
|
|
||||||
raise
|
|
||||||
|
|
||||||
@@ -1954,7 +1966,9 @@
|
|
||||||
else:
|
|
||||||
raise RuntimeError("While serving generator didn't terminate")
|
|
||||||
except Exception as error:
|
|
||||||
- await got_serving_exception.send(self, exception=error)
|
|
||||||
+ await got_serving_exception.send_async(
|
|
||||||
+ self, _sync_wrapper=self.ensure_async, exception=error
|
|
||||||
+ )
|
|
||||||
self.log_exception(sys.exc_info())
|
|
||||||
raise
|
|
||||||
|
|
||||||
diff --git a/src/quart/asgi.py b/src/quart/asgi.py
|
|
||||||
index ba266dc3..2725af8d 100644
|
|
||||||
--- a/src/quart/asgi.py
|
|
||||||
+++ b/src/quart/asgi.py
|
|
||||||
@@ -169,7 +169,7 @@ async def handle_messages(self, receive: ASGIReceiveCallable) -> None:
|
|
||||||
event = await receive()
|
|
||||||
if event["type"] == "websocket.receive":
|
|
||||||
message = event.get("bytes") or event["text"]
|
|
||||||
- await websocket_received.send(message)
|
|
||||||
+ await websocket_received.send_async(message)
|
|
||||||
await self.queue.put(message)
|
|
||||||
elif event["type"] == "websocket.disconnect":
|
|
||||||
return
|
|
||||||
@@ -261,7 +261,7 @@ async def send_data(self, send: ASGISendCallable, data: AnyStr) -> None:
|
|
||||||
await send({"type": "websocket.send", "bytes": None, "text": data})
|
|
||||||
else:
|
|
||||||
await send({"type": "websocket.send", "bytes": data, "text": None})
|
|
||||||
- await websocket_sent.send(data)
|
|
||||||
+ await websocket_sent.send_async(data)
|
|
||||||
|
|
||||||
async def accept_connection(
|
|
||||||
self, send: ASGISendCallable, headers: Headers, subprotocol: Optional[str]
|
|
||||||
diff --git a/src/quart/ctx.py b/src/quart/ctx.py
|
|
||||||
index 806aff74..c92ba92e 100644
|
|
||||||
--- a/src/quart/ctx.py
|
|
||||||
+++ b/src/quart/ctx.py
|
|
||||||
@@ -240,7 +240,7 @@ def copy(self) -> "AppContext":
|
|
||||||
|
|
||||||
async def push(self) -> None:
|
|
||||||
self._cv_tokens.append(_cv_app.set(self))
|
|
||||||
- await appcontext_pushed.send(self.app)
|
|
||||||
+ await appcontext_pushed.send_async(self.app, _sync_wrapper=self.app.ensure_async)
|
|
||||||
|
|
||||||
async def pop(self, exc: Optional[BaseException] = _sentinel) -> None: # type: ignore
|
|
||||||
try:
|
|
||||||
@@ -255,7 +255,7 @@ async def pop(self, exc: Optional[BaseException] = _sentinel) -> None: # type:
|
|
||||||
if ctx is not self:
|
|
||||||
raise AssertionError(f"Popped wrong app context. ({ctx!r} instead of {self!r})")
|
|
||||||
|
|
||||||
- await appcontext_popped.send(self.app)
|
|
||||||
+ await appcontext_popped.send_async(self.app, _sync_wrapper=self.app.ensure_async)
|
|
||||||
|
|
||||||
async def __aenter__(self) -> "AppContext":
|
|
||||||
await self.push()
|
|
||||||
diff --git a/src/quart/helpers.py b/src/quart/helpers.py
|
|
||||||
index c848a72f..0491acfe 100644
|
|
||||||
--- a/src/quart/helpers.py
|
|
||||||
+++ b/src/quart/helpers.py
|
|
||||||
@@ -113,8 +113,9 @@ async def login():
|
|
||||||
flashes = session.get("_flashes", [])
|
|
||||||
flashes.append((category, message))
|
|
||||||
session["_flashes"] = flashes
|
|
||||||
- await message_flashed.send(
|
|
||||||
- current_app._get_current_object(), message=message, category=category # type: ignore
|
|
||||||
+ app = current_app._get_current_object() # type: ignore
|
|
||||||
+ await message_flashed.send_async(
|
|
||||||
+ app, _sync_wrapper=app.ensure_async, message=message, category=category
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
diff --git a/src/quart/signals.py b/src/quart/signals.py
|
|
||||||
index a23b902d..de5e9fcc 100644
|
|
||||||
--- a/src/quart/signals.py
|
|
||||||
+++ b/src/quart/signals.py
|
|
||||||
@@ -1,54 +1,10 @@
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
-from functools import wraps
|
|
||||||
-from typing import Any, Callable, List, Optional, Tuple
|
|
||||||
-
|
|
||||||
-from blinker import NamedSignal, Namespace # type: ignore[import]
|
|
||||||
-
|
|
||||||
-from .utils import is_coroutine_function
|
|
||||||
+from blinker import Namespace
|
|
||||||
|
|
||||||
signals_available = True
|
|
||||||
|
|
||||||
-
|
|
||||||
-class AsyncNamedSignal(NamedSignal): # type: ignore
|
|
||||||
- def __init__(self, name: str, doc: Optional[str] = None) -> None:
|
|
||||||
- super().__init__(name, doc)
|
|
||||||
-
|
|
||||||
- async def send(self, *sender: Any, **kwargs: Any) -> List[Tuple[Callable, Any]]:
|
|
||||||
- coroutines = super().send(*sender, **kwargs)
|
|
||||||
- result: List[Tuple[Callable, Any]] = []
|
|
||||||
- for handler, coroutine in coroutines:
|
|
||||||
- result.append((handler, await coroutine))
|
|
||||||
- return result
|
|
||||||
-
|
|
||||||
- def connect(self, receiver: Callable, *args: Any, **kwargs: Any) -> Callable:
|
|
||||||
- if is_coroutine_function(receiver):
|
|
||||||
- handler = receiver
|
|
||||||
- else:
|
|
||||||
-
|
|
||||||
- @wraps(receiver)
|
|
||||||
- async def handler(*a: Any, **k: Any) -> Any:
|
|
||||||
- return receiver(*a, **k)
|
|
||||||
-
|
|
||||||
- if handler is not receiver and kwargs.get("weak", True):
|
|
||||||
- # Blinker will take a weakref to handler, which goes out
|
|
||||||
- # of scope with this method as it is a wrapper around the
|
|
||||||
- # receiver. Whereas we'd want it to go out of scope when
|
|
||||||
- # receiver does. Therefore we can place it on the receiver
|
|
||||||
- # function. (Ideally I'll think of a better way).
|
|
||||||
- receiver._quart_wrapper_func = handler # type: ignore
|
|
||||||
- return super().connect(handler, *args, **kwargs)
|
|
||||||
-
|
|
||||||
-
|
|
||||||
-class AsyncNamespace(Namespace): # type: ignore
|
|
||||||
- def signal(self, name: str, doc: Optional[str] = None) -> AsyncNamedSignal:
|
|
||||||
- try:
|
|
||||||
- return self[name]
|
|
||||||
- except KeyError:
|
|
||||||
- return self.setdefault(name, AsyncNamedSignal(name, doc))
|
|
||||||
-
|
|
||||||
-
|
|
||||||
-_signals = AsyncNamespace()
|
|
||||||
+_signals = Namespace()
|
|
||||||
|
|
||||||
#: Called before a template is rendered, connection functions
|
|
||||||
# should have a signature of Callable[[Quart, Template, dict], None]
|
|
||||||
diff --git a/src/quart/templating.py b/src/quart/templating.py
|
|
||||||
index 416c2eef..5c07d132 100644
|
|
||||||
--- a/src/quart/templating.py
|
|
||||||
+++ b/src/quart/templating.py
|
|
||||||
@@ -116,9 +116,13 @@ async def render_template_string(source: str, **context: Any) -> str:
|
|
||||||
|
|
||||||
|
|
||||||
async def _render(template: Template, context: dict, app: "Quart") -> str:
|
|
||||||
- await before_render_template.send(app, template=template, context=context)
|
|
||||||
+ await before_render_template.send_async(
|
|
||||||
+ app, _sync_wrapper=app.ensure_async, template=template, context=context
|
|
||||||
+ )
|
|
||||||
rendered_template = await template.render_async(context)
|
|
||||||
- await template_rendered.send(app, template=template, context=context)
|
|
||||||
+ await template_rendered.send_async(
|
|
||||||
+ app, _sync_wrapper=app.ensure_async, template=template, context=context
|
|
||||||
+ )
|
|
||||||
return rendered_template
|
|
||||||
|
|
||||||
|
|
||||||
@@ -166,12 +170,16 @@ async def stream_template_string(source: str, **context: Any) -> AsyncIterator[s
|
|
||||||
|
|
||||||
|
|
||||||
async def _stream(app: "Quart", template: Template, context: Dict[str, Any]) -> AsyncIterator[str]:
|
|
||||||
- await before_render_template.send(app, template=template, context=context)
|
|
||||||
+ await before_render_template.send_async(
|
|
||||||
+ app, _sync_wrapper=app.ensure_async, template=template, context=context
|
|
||||||
+ )
|
|
||||||
|
|
||||||
async def generate() -> AsyncIterator[str]:
|
|
||||||
async for chunk in template.generate_async(context):
|
|
||||||
yield chunk
|
|
||||||
- await template_rendered.send(app, template=template, context=context)
|
|
||||||
+ await template_rendered.send_async(
|
|
||||||
+ app, _sync_wrapper=app.ensure_async, template=template, context=context
|
|
||||||
+ )
|
|
||||||
|
|
||||||
# If a request context is active, keep it while generating.
|
|
||||||
if has_request_context():
|
|
||||||
diff --git a/tests/test_signals.py b/tests/test_signals.py
|
|
||||||
deleted file mode 100644
|
|
||||||
index 671d5942..00000000
|
|
||||||
--- a/tests/test_signals.py
|
|
||||||
+++ /dev/null
|
|
||||||
@@ -1,37 +0,0 @@
|
|
||||||
-from __future__ import annotations
|
|
||||||
-
|
|
||||||
-from typing import Any
|
|
||||||
-
|
|
||||||
-import pytest
|
|
||||||
-
|
|
||||||
-from quart.signals import AsyncNamedSignal
|
|
||||||
-
|
|
||||||
-
|
|
||||||
-@pytest.mark.parametrize("weak", [True, False])
|
|
||||||
-async def test_sync_signal(weak: bool) -> None:
|
|
||||||
- signal = AsyncNamedSignal("name")
|
|
||||||
- fired = False
|
|
||||||
-
|
|
||||||
- def sync_fired(*_: Any) -> None:
|
|
||||||
- nonlocal fired
|
|
||||||
- fired = True
|
|
||||||
-
|
|
||||||
- signal.connect(sync_fired, weak=weak)
|
|
||||||
-
|
|
||||||
- await signal.send()
|
|
||||||
- assert fired
|
|
||||||
-
|
|
||||||
-
|
|
||||||
-@pytest.mark.parametrize("weak", [True, False])
|
|
||||||
-async def test_async_signal(weak: bool) -> None:
|
|
||||||
- signal = AsyncNamedSignal("name")
|
|
||||||
- fired = False
|
|
||||||
-
|
|
||||||
- async def async_fired(*_: Any) -> None:
|
|
||||||
- nonlocal fired
|
|
||||||
- fired = True
|
|
||||||
-
|
|
||||||
- signal.connect(async_fired, weak=weak)
|
|
||||||
-
|
|
||||||
- await signal.send()
|
|
||||||
- assert fired
|
|
|
@ -1,19 +1,20 @@
|
||||||
# Template file for 'python3-quart'
|
# Template file for 'python3-quart'
|
||||||
pkgname=python3-quart
|
pkgname=python3-quart
|
||||||
version=0.18.4
|
version=0.19.3
|
||||||
revision=3
|
revision=1
|
||||||
build_style=python3-pep517
|
build_style=python3-pep517
|
||||||
hostmakedepends="python3-poetry-core"
|
hostmakedepends="python3-poetry-core"
|
||||||
depends="python3-aiofiles python3-hypercorn python3-click python3-MarkupSafe
|
depends="python3-aiofiles python3-blinker python3-click python3-Flask
|
||||||
python3-blinker python3-itsdangerous python3-Jinja2 python3-Werkzeug"
|
python3-hypercorn python3-itsdangerous python3-Jinja2 python3-MarkupSafe
|
||||||
checkdepends="python3-pytest-asyncio python3-hypothesis python3-mock python3-dotenv $depends"
|
python3-Werkzeug"
|
||||||
|
checkdepends="python3-pytest-asyncio python3-hypothesis python3-dotenv $depends"
|
||||||
short_desc="Python asyncio ASGI web framework with Flask API"
|
short_desc="Python asyncio ASGI web framework with Flask API"
|
||||||
maintainer="Andrew J. Hesford <ajh@sideband.org>"
|
maintainer="Andrew J. Hesford <ajh@sideband.org>"
|
||||||
license="MIT"
|
license="MIT"
|
||||||
homepage="https://quart.palletsprojects.com/en/latest/"
|
homepage="https://quart.palletsprojects.com/en/latest/"
|
||||||
changelog="https://raw.githubusercontent.com/pallets/quart/main/CHANGES.rst"
|
changelog="https://raw.githubusercontent.com/pallets/quart/main/CHANGES.rst"
|
||||||
distfiles="https://github.com/pallets/quart/archive/refs/tags/${version}.tar.gz"
|
distfiles="https://github.com/pallets/quart/archive/refs/tags/${version}.tar.gz"
|
||||||
checksum=0fc5fe5c32ec94a3a12262afa56c1be143599c2a4fb02bdf7109456b61c87ac2
|
checksum=5f1a43e2296960c755fec3ab6144b6786614bf4602c2f52084f75d5ce0d8ad25
|
||||||
|
|
||||||
pre_check() {
|
pre_check() {
|
||||||
vsed -e '/addopts/d' -i pyproject.toml
|
vsed -e '/addopts/d' -i pyproject.toml
|
||||||
|
|
Loading…
Reference in New Issue