From edf821c31c34c915c5f88e06f201a9119d0e6b7a Mon Sep 17 00:00:00 2001 From: Adam Johnson Date: Sun, 22 Oct 2023 23:34:07 +0100 Subject: [PATCH] Fix ASGI incompatiblity on Python 3.12 --- docs/changelog.rst | 4 ++++ pyproject.toml | 1 + src/django_htmx/middleware.py | 18 +++++++----------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index f90ebb5..f6b31a5 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,6 +2,10 @@ Changelog ========= +* Fix ASGI incompatibility on Python 3.12. + + Thanks to Grigory Vydrin for the report in `Issue #381 `__. + 1.17.0 (2023-10-11) ------------------- diff --git a/pyproject.toml b/pyproject.toml index ffb8f12..b16b69c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,6 +10,7 @@ version = "1.17.0" description = "Extensions for using Django with htmx." readme = {file = "README.rst", content-type = "text/x-rst"} keywords = [ + "asgiref>=3.6", "Django", ] license = {text = "MIT"} diff --git a/src/django_htmx/middleware.py b/src/django_htmx/middleware.py index 8b43ade..a355d18 100644 --- a/src/django_htmx/middleware.py +++ b/src/django_htmx/middleware.py @@ -1,6 +1,5 @@ from __future__ import annotations -import asyncio import json from typing import Any from typing import Awaitable @@ -9,6 +8,8 @@ from urllib.parse import urlsplit from urllib.parse import urlunsplit +from asgiref.sync import iscoroutinefunction +from asgiref.sync import markcoroutinefunction from django.http import HttpRequest from django.http.response import HttpResponseBase from django.utils.functional import cached_property @@ -26,29 +27,24 @@ def __init__( ), ) -> None: self.get_response = get_response + self.async_mode = iscoroutinefunction(self.get_response) - if asyncio.iscoroutinefunction(self.get_response): + if self.async_mode: # Mark the class as async-capable, but do the actual switch # inside __call__ to avoid swapping out dunder methods - self._is_coroutine = ( - asyncio.coroutines._is_coroutine # type: ignore [attr-defined] - ) - else: - self._is_coroutine = None + markcoroutinefunction(self) def __call__( self, request: HttpRequest ) -> HttpResponseBase | Awaitable[HttpResponseBase]: - if self._is_coroutine: + if self.async_mode: return self.__acall__(request) request.htmx = HtmxDetails(request) # type: ignore [attr-defined] return self.get_response(request) async def __acall__(self, request: HttpRequest) -> HttpResponseBase: request.htmx = HtmxDetails(request) # type: ignore [attr-defined] - result = self.get_response(request) - assert not isinstance(result, HttpResponseBase) # type narrow - return await result + return await self.get_response(request) # type: ignore [no-any-return, misc] class HtmxDetails: