_asgi_compatibility.py 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. # Includes work from:
  2. # Copyright (c) Django Software Foundation and individual contributors.
  3. # All rights reserved.
  4. #
  5. # Redistribution and use in source and binary forms, with or without modification,
  6. # are permitted provided that the following conditions are met:
  7. #
  8. # 1. Redistributions of source code must retain the above copyright notice,
  9. # this list of conditions and the following disclaimer.
  10. #
  11. # 2. Redistributions in binary form must reproduce the above copyright
  12. # notice, this list of conditions and the following disclaimer in the
  13. # documentation and/or other materials provided with the distribution.
  14. #
  15. # 3. Neither the name of Django nor the names of its contributors may be used
  16. # to endorse or promote products derived from this software without
  17. # specific prior written permission.
  18. #
  19. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  20. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  21. # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  22. # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
  23. # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  24. # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  25. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  26. # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  27. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  28. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  29. from __future__ import annotations
  30. import asyncio
  31. import inspect
  32. from typing import TYPE_CHECKING, cast
  33. if TYPE_CHECKING:
  34. from asgiref.typing import (
  35. ASGI2Application,
  36. ASGI3Application,
  37. ASGIApplication,
  38. ASGIReceiveCallable,
  39. ASGISendCallable,
  40. Scope,
  41. )
  42. # Vendored from https://github.com/django/asgiref/blob/main/asgiref/compatibility.py
  43. if hasattr(inspect, "markcoroutinefunction"):
  44. iscoroutinefunction = inspect.iscoroutinefunction
  45. else:
  46. iscoroutinefunction = asyncio.iscoroutinefunction # type: ignore[assignment]
  47. def is_double_callable(application: ASGIApplication) -> bool:
  48. if getattr(application, "_asgi_single_callable", False):
  49. return False
  50. if getattr(application, "_asgi_double_callable", False):
  51. return True
  52. if inspect.isclass(application):
  53. return True
  54. if callable(application) and iscoroutinefunction(application.__call__):
  55. return False
  56. return not iscoroutinefunction(application)
  57. def double_to_single_callable(application: ASGI2Application) -> ASGI3Application:
  58. async def new_application(
  59. scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable
  60. ) -> None:
  61. instance = application(scope)
  62. return await instance(receive, send)
  63. return new_application
  64. def guarantee_single_callable(application: ASGIApplication) -> ASGI3Application:
  65. if is_double_callable(application):
  66. application = double_to_single_callable(cast("ASGI2Application", application))
  67. return cast("ASGI3Application", application)