compatibility.py 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. import asyncio
  2. import inspect
  3. import sys
  4. def is_double_callable(application):
  5. """
  6. Tests to see if an application is a legacy-style (double-callable) application.
  7. """
  8. # Look for a hint on the object first
  9. if getattr(application, "_asgi_single_callable", False):
  10. return False
  11. if getattr(application, "_asgi_double_callable", False):
  12. return True
  13. # Uninstanted classes are double-callable
  14. if inspect.isclass(application):
  15. return True
  16. # Instanted classes depend on their __call__
  17. if hasattr(application, "__call__"):
  18. # We only check to see if its __call__ is a coroutine function -
  19. # if it's not, it still might be a coroutine function itself.
  20. if asyncio.iscoroutinefunction(application.__call__):
  21. return False
  22. # Non-classes we just check directly
  23. return not asyncio.iscoroutinefunction(application)
  24. def double_to_single_callable(application):
  25. """
  26. Transforms a double-callable ASGI application into a single-callable one.
  27. """
  28. async def new_application(scope, receive, send):
  29. instance = application(scope)
  30. return await instance(receive, send)
  31. return new_application
  32. def guarantee_single_callable(application):
  33. """
  34. Takes either a single- or double-callable application and always returns it
  35. in single-callable style. Use this to add backwards compatibility for ASGI
  36. 2.0 applications to your server/test harness/etc.
  37. """
  38. if is_double_callable(application):
  39. application = double_to_single_callable(application)
  40. return application
  41. if sys.version_info >= (3, 7):
  42. # these were introduced in 3.7
  43. get_running_loop = asyncio.get_running_loop
  44. run_future = asyncio.run
  45. create_task = asyncio.create_task
  46. else:
  47. # marked as deprecated in 3.10, did not exist before 3.7
  48. get_running_loop = asyncio.get_event_loop
  49. run_future = asyncio.ensure_future
  50. # does nothing, this is fine for <3.7
  51. create_task = lambda task: task