#33495: "Synchronous middleware ... adapted" is not logged for synchronous middleware ----------------------------------------+----------------------------- Reporter: Aaron Chong | Owner: Aaron Chong Type: Bug | Status: assigned Component: Core (Other) | Version: 4.0 Severity: Normal | Keywords: async Triage Stage: Unreviewed | Has patch: 0 Needs documentation: 0 | Needs tests: 0 Patch needs improvement: 0 | Easy pickings: 0 UI/UX: 0 | ----------------------------------------+----------------------------- This is a bug in log messages that contradicts the guide in a warning in the docs; the feature works.
The guide at https://docs.djangoproject.com/en/4.0/topics/async/#async- views, **emphasis** mine: Warning [[BR]] You will only get the benefits of a fully-asynchronous request stack if you have no synchronous middleware loaded into your site. **If there is a piece of synchronous middleware, then Django must use a thread per request to safely emulate a synchronous environment for it.** [[BR]] Middleware can be built to support both sync and async contexts. Some of Django’s middleware is built like this, but not all. **To see what middleware Django has to adapt, you can turn on debug logging for the django.request logger and look for log messages about “Synchronous middleware … adapted”.** The test for "Synchronous middleware ... adapted" is instead testing an async middleware over a sync method: https://github.com/django/django/blob/7ca7f4495ba746279b734695a8dd137bf7ee0bab/tests/middleware_exceptions/tests.py#L222-L234 {{{#!python @override_settings(MIDDLEWARE=[ 'middleware_exceptions.middleware.async_payment_middleware', ]) def test_async_middleware(self): with self.assertLogs('django.request', 'DEBUG') as cm: response = self.client.get('/middleware_exceptions/view/') self.assertEqual(response.status_code, 402) self.assertEqual( cm.records[0].getMessage(), "Synchronous middleware " "middleware_exceptions.middleware.async_payment_middleware " "adapted.", ) }}} About the existing implementation: 1. `BaseHandler.load_middleware` passes the middleware name as `name` to `BaseHandler.adapt_method_mode`: https://github.com/django/django/blob/98ad327864aed8df245fd19ea9d2743279e11643/django/core/handlers/base.py#L53-L57 {{{#!python # Adapt handler, if needed. adapted_handler = self.adapt_method_mode( middleware_is_async, handler, handler_is_async, debug=settings.DEBUG, name='middleware %s' % middleware_path, ) }}} 2. `BaseHandler.adapt_method_mode` adapts the `method` and treats `name` as the method name rather than the middleware name; when the middleware name is used, it implies a method has been adapted for the middleware, not that the middleware was adapted: {{{#!python if debug and not name: name = name or 'method %s()' % method.__qualname__ if is_async: if not method_is_async: if debug: logger.debug('Synchronous %s adapted.', name) return sync_to_async(method, thread_sensitive=True) elif method_is_async: if debug: logger.debug('Asynchronous %s adapted.', name) return async_to_sync(method) }}} Proposed fix: Handle middleware `name` and method name separately within `BaseHandler.adapt_method_mode`: {{{#!diff def adapt_method_mode( self, is_async, method, method_is_async=None, debug=False, name=None, ): """ Adapt a method to be in the correct "mode": - If is_async is False: - Synchronous methods are left alone - Asynchronous methods are wrapped with async_to_sync - If is_async is True: - Synchronous methods are wrapped with sync_to_async() - Asynchronous methods are left alone """ + method_name = None if method_is_async is None: method_is_async = asyncio.iscoroutinefunction(method) if debug and not name: - name = name or 'method %s()' % method.__qualname__ + method_name = 'method %s()' % method.__qualname__ if is_async: if not method_is_async: if debug: - logger.debug('Synchronous %s adapted.', name) + if name: + logger.debug('Asynchronous %s adapted.', name) + else: + logger.debug('Synchronous %s adapted.', method_name) return sync_to_async(method, thread_sensitive=True) elif method_is_async: if debug: - logger.debug('Asynchronous %s adapted.', name) + if name: + logger.debug('Synchronous %s adapted.', name) + else: + logger.debug('Asynchronous %s adapted.', method_name) return async_to_sync(method) }}} -- Ticket URL: <https://code.djangoproject.com/ticket/33495> Django <https://code.djangoproject.com/> The Web framework for perfectionists with deadlines. -- You received this message because you are subscribed to the Google Groups "Django updates" group. To unsubscribe from this group and stop receiving emails from it, send an email to django-updates+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-updates/047.f639cd9e1ded034c68c22f9a66f17514%40djangoproject.com.