Format: text/x-python
⬇ Download File (1.6 KB)
   1  import inspect
   2  
   3  from .sync import iscoroutinefunction
   4  
   5  
   6  def is_double_callable(application):
   7      """
   8      Tests to see if an application is a legacy-style (double-callable) application.
   9      """
  10      # Look for a hint on the object first
  11      if getattr(application, "_asgi_single_callable", False):
  12          return False
  13      if getattr(application, "_asgi_double_callable", False):
  14          return True
  15      # Uninstanted classes are double-callable
  16      if inspect.isclass(application):
  17          return True
  18      # Instanted classes depend on their __call__
  19      if hasattr(application, "__call__"):
  20          # We only check to see if its __call__ is a coroutine function -
  21          # if it's not, it still might be a coroutine function itself.
  22          if iscoroutinefunction(application.__call__):
  23              return False
  24      # Non-classes we just check directly
  25      return not iscoroutinefunction(application)
  26  
  27  
  28  def double_to_single_callable(application):
  29      """
  30      Transforms a double-callable ASGI application into a single-callable one.
  31      """
  32  
  33      async def new_application(scope, receive, send):
  34          instance = application(scope)
  35          return await instance(receive, send)
  36  
  37      return new_application
  38  
  39  
  40  def guarantee_single_callable(application):
  41      """
  42      Takes either a single- or double-callable application and always returns it
  43      in single-callable style. Use this to add backwards compatibility for ASGI
  44      2.0 applications to your server/test harness/etc.
  45      """
  46      if is_double_callable(application):
  47          application = double_to_single_callable(application)
  48      return application
  49