diff --git a/srcpkgs/python3-quart/patches/blinker-1.6.patch b/srcpkgs/python3-quart/patches/blinker-1.6.patch new file mode 100644 index 00000000000..f5b56b3c8ba --- /dev/null +++ b/srcpkgs/python3-quart/patches/blinker-1.6.patch @@ -0,0 +1,360 @@ +From c9b555ff284bb54a45adab44c8e6d78ab8713da0 Mon Sep 17 00:00:00 2001 +From: pgjones +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 diff --git a/srcpkgs/python3-quart/template b/srcpkgs/python3-quart/template index 49f066b717c..311b0e17fbb 100644 --- a/srcpkgs/python3-quart/template +++ b/srcpkgs/python3-quart/template @@ -1,7 +1,7 @@ # Template file for 'python3-quart' pkgname=python3-quart version=0.18.4 -revision=1 +revision=2 build_style=python3-pep517 hostmakedepends="python3-poetry-core" depends="python3-aiofiles python3-hypercorn python3-click python3-MarkupSafe