abc.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import asyncio
  2. import logging
  3. from abc import ABC, abstractmethod
  4. from collections.abc import Sized
  5. from http.cookies import BaseCookie, Morsel
  6. from typing import (
  7. TYPE_CHECKING,
  8. Any,
  9. Awaitable,
  10. Callable,
  11. Dict,
  12. Generator,
  13. Iterable,
  14. List,
  15. Optional,
  16. Tuple,
  17. )
  18. from multidict import CIMultiDict
  19. from yarl import URL
  20. from .helpers import get_running_loop
  21. from .typedefs import LooseCookies
  22. if TYPE_CHECKING: # pragma: no cover
  23. from .web_app import Application
  24. from .web_exceptions import HTTPException
  25. from .web_request import BaseRequest, Request
  26. from .web_response import StreamResponse
  27. else:
  28. BaseRequest = Request = Application = StreamResponse = None
  29. HTTPException = None
  30. class AbstractRouter(ABC):
  31. def __init__(self) -> None:
  32. self._frozen = False
  33. def post_init(self, app: Application) -> None:
  34. """Post init stage.
  35. Not an abstract method for sake of backward compatibility,
  36. but if the router wants to be aware of the application
  37. it can override this.
  38. """
  39. @property
  40. def frozen(self) -> bool:
  41. return self._frozen
  42. def freeze(self) -> None:
  43. """Freeze router."""
  44. self._frozen = True
  45. @abstractmethod
  46. async def resolve(self, request: Request) -> "AbstractMatchInfo":
  47. """Return MATCH_INFO for given request"""
  48. class AbstractMatchInfo(ABC):
  49. @property # pragma: no branch
  50. @abstractmethod
  51. def handler(self) -> Callable[[Request], Awaitable[StreamResponse]]:
  52. """Execute matched request handler"""
  53. @property
  54. @abstractmethod
  55. def expect_handler(self) -> Callable[[Request], Awaitable[None]]:
  56. """Expect handler for 100-continue processing"""
  57. @property # pragma: no branch
  58. @abstractmethod
  59. def http_exception(self) -> Optional[HTTPException]:
  60. """HTTPException instance raised on router's resolving, or None"""
  61. @abstractmethod # pragma: no branch
  62. def get_info(self) -> Dict[str, Any]:
  63. """Return a dict with additional info useful for introspection"""
  64. @property # pragma: no branch
  65. @abstractmethod
  66. def apps(self) -> Tuple[Application, ...]:
  67. """Stack of nested applications.
  68. Top level application is left-most element.
  69. """
  70. @abstractmethod
  71. def add_app(self, app: Application) -> None:
  72. """Add application to the nested apps stack."""
  73. @abstractmethod
  74. def freeze(self) -> None:
  75. """Freeze the match info.
  76. The method is called after route resolution.
  77. After the call .add_app() is forbidden.
  78. """
  79. class AbstractView(ABC):
  80. """Abstract class based view."""
  81. def __init__(self, request: Request) -> None:
  82. self._request = request
  83. @property
  84. def request(self) -> Request:
  85. """Request instance."""
  86. return self._request
  87. @abstractmethod
  88. def __await__(self) -> Generator[Any, None, StreamResponse]:
  89. """Execute the view handler."""
  90. class AbstractResolver(ABC):
  91. """Abstract DNS resolver."""
  92. @abstractmethod
  93. async def resolve(self, host: str, port: int, family: int) -> List[Dict[str, Any]]:
  94. """Return IP address for given hostname"""
  95. @abstractmethod
  96. async def close(self) -> None:
  97. """Release resolver"""
  98. if TYPE_CHECKING: # pragma: no cover
  99. IterableBase = Iterable[Morsel[str]]
  100. else:
  101. IterableBase = Iterable
  102. class AbstractCookieJar(Sized, IterableBase):
  103. """Abstract Cookie Jar."""
  104. def __init__(self, *, loop: Optional[asyncio.AbstractEventLoop] = None) -> None:
  105. self._loop = get_running_loop(loop)
  106. @abstractmethod
  107. def clear(self) -> None:
  108. """Clear all cookies."""
  109. @abstractmethod
  110. def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None:
  111. """Update cookies."""
  112. @abstractmethod
  113. def filter_cookies(self, request_url: URL) -> "BaseCookie[str]":
  114. """Return the jar's cookies filtered by their attributes."""
  115. class AbstractStreamWriter(ABC):
  116. """Abstract stream writer."""
  117. buffer_size = 0
  118. output_size = 0
  119. length = 0 # type: Optional[int]
  120. @abstractmethod
  121. async def write(self, chunk: bytes) -> None:
  122. """Write chunk into stream."""
  123. @abstractmethod
  124. async def write_eof(self, chunk: bytes = b"") -> None:
  125. """Write last chunk."""
  126. @abstractmethod
  127. async def drain(self) -> None:
  128. """Flush the write buffer."""
  129. @abstractmethod
  130. def enable_compression(self, encoding: str = "deflate") -> None:
  131. """Enable HTTP body compression"""
  132. @abstractmethod
  133. def enable_chunking(self) -> None:
  134. """Enable HTTP chunked mode"""
  135. @abstractmethod
  136. async def write_headers(
  137. self, status_line: str, headers: "CIMultiDict[str]"
  138. ) -> None:
  139. """Write HTTP headers"""
  140. class AbstractAccessLogger(ABC):
  141. """Abstract writer to access log."""
  142. def __init__(self, logger: logging.Logger, log_format: str) -> None:
  143. self.logger = logger
  144. self.log_format = log_format
  145. @abstractmethod
  146. def log(self, request: BaseRequest, response: StreamResponse, time: float) -> None:
  147. """Emit log to logger."""