Opened 49 minutes ago
#37382 new Bug
@non_atomic_requests no longer works on async views
| Reported by: | Jacob Walls | Owned by: | |
|---|---|---|---|
| Component: | Database layer (models, ORM) | Version: | dev |
| Severity: | Release blocker | Keywords: | |
| Cc: | Sina Chaichi | Triage Stage: | Unreviewed |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
After bdbda29c3e126754c3ae04ceb5c5d35d49aae01c (#29303), @non_atomic_requests wraps all views in a sync callable regardless if the view was a coroutine. This test passes on 6.1:
-
tests/handlers/tests.py
diff --git a/tests/handlers/tests.py b/tests/handlers/tests.py index 240c398988..c641aeb82a 100644
a b class TransactionsPerRequestTests(TransactionTestCase): 155 155 self.assertEqual(wrapped_once._non_atomic_requests, {"default"}) 156 156 self.assertEqual(wrapped_twice._non_atomic_requests, {"default", "other"}) 157 157 158 async def test_no_auto_transaction_async(self): 159 old_atomic_requests = connection.settings_dict["ATOMIC_REQUESTS"] 160 try: 161 connection.settings_dict["ATOMIC_REQUESTS"] = True 162 response = await self.async_client.get("/not_in_transaction_async/") 163 finally: 164 connection.settings_dict["ATOMIC_REQUESTS"] = old_atomic_requests 165 self.assertContains(response, "False") 166 158 167 159 168 @override_settings(ROOT_URLCONF="handlers.urls") 160 169 class SignalsTests(SimpleTestCase): -
tests/handlers/urls.py
diff --git a/tests/handlers/urls.py b/tests/handlers/urls.py index a0efece602..8c7b65e018 100644
a b urlpatterns = [ 11 11 path("async_streaming/", views.async_streaming), 12 12 path("in_transaction/", views.in_transaction), 13 13 path("not_in_transaction/", views.not_in_transaction), 14 path("not_in_transaction_async/", views.not_in_transaction_async), 14 15 path("not_in_transaction_using_none/", views.not_in_transaction_using_none), 15 16 path("not_in_transaction_using_text/", views.not_in_transaction_using_text), 16 17 path("bad_request/", views.bad_request), -
tests/handlers/views.py
diff --git a/tests/handlers/views.py b/tests/handlers/views.py index 95d663323d..fe72c600ae 100644
a b def not_in_transaction(request): 33 33 return HttpResponse(str(connection.in_atomic_block)) 34 34 35 35 36 @transaction.non_atomic_requests 37 async def not_in_transaction_async(request): 38 return HttpResponse(str(connection.in_atomic_block)) 39 40 36 41 @transaction.non_atomic_requests(using=None) 37 42 def not_in_transaction_using_none(request): 38 43 return HttpResponse(str(connection.in_atomic_block))
Exception ignored while finalizing coroutine <coroutine object not_in_transaction_async at 0x10a5812a0>: Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/_py_warnings.py", line 856, in _warn_unawaited_coroutine _wm.warn( RuntimeWarning: coroutine 'not_in_transaction_async' was never awaited ====================================================================== ERROR: test_no_auto_transaction_async (handlers.tests.TransactionsPerRequestTests.test_no_auto_transaction_async) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/unittest/case.py", line 58, in testPartExecutor yield File "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/unittest/case.py", line 669, in run self._callTestMethod(testMethod) File "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/unittest/case.py", line 615, in _callTestMethod result = method() ^^^^^^^^^^^^^^^ File "/Users/jwalls/my314/lib/python3.14/site-packages/asgiref/sync.py", line 332, in __call__ return call_result.result() ^^^^^^^^^^^^^^^ File "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/concurrent/futures/_base.py", line 447, in result return self.__get_result() ^^^ File "/Library/Frameworks/Python.framework/Versions/3.14/lib/python3.14/concurrent/futures/_base.py", line 396, in __get_result raise self._exception ^^^^^^^ File "/Users/jwalls/my314/lib/python3.14/site-packages/asgiref/sync.py", line 372, in main_wrap result = await awaitable ^^^^^^^ File "/Users/jwalls/django/tests/handlers/tests.py", line 162, in test_no_auto_transaction_async response = await self.async_client.get("/not_in_transaction_async/") ^^^^^^^^^^^ File "/Users/jwalls/django/django/test/client.py", line 1495, in get response = await super().get( ^^^^^^^^^^^^^^^ File "/Users/jwalls/django/django/test/client.py", line 1458, in request self.check_exception(response) ^^^^^^^^^^^^^^^ File "/Users/jwalls/django/django/test/client.py", line 810, in check_exception raise exc_value ^^^^^^^ File "/Users/jwalls/my314/lib/python3.14/site-packages/asgiref/sync.py", line 577, in thread_handler raise exc_info[1] ^^^^^^^ File "/Users/jwalls/django/django/core/handlers/exception.py", line 43, in inner response = await get_response(request) ^^^^^^^ File "/Users/jwalls/django/django/core/handlers/base.py", line 267, in _get_response_async self.check_response(response, callback) ^^^^^^^^^^^^^^^ File "/Users/jwalls/django/django/core/handlers/base.py", line 339, in check_response raise ValueError( ^^^^^^^^^^^ ValueError: The view handlers.views.not_in_transaction_async didn't return an HttpResponse object. It returned an unawaited coroutine instead. You may need to add an 'await' into your view. ---------------------------------------------------------------------- Ran 42 tests in 1.277s FAILED (errors=1)
My understanding is that using this decorator is required for async views to avoid the safety check here:
def make_view_atomic(self, view): non_atomic_requests = getattr(view, "_non_atomic_requests", set()) for alias, settings_dict in connections.settings.items(): if settings_dict["ATOMIC_REQUESTS"] and alias not in non_atomic_requests: if iscoroutinefunction(view): raise RuntimeError( "You cannot use ATOMIC_REQUESTS with async views." ) view = transaction.atomic(using=alias)(view) return view
Note:
See TracTickets
for help on using tickets.