You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

208 lines
5.3 KiB

4 years ago
  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 # noqa
  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 # noqa
  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_request import BaseRequest, Request
  24. from .web_response import StreamResponse
  25. from .web_app import Application
  26. from .web_exceptions import HTTPException
  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,
  94. port: int, family: int) -> List[Dict[str, Any]]:
  95. """Return IP address for given hostname"""
  96. @abstractmethod
  97. async def close(self) -> None:
  98. """Release resolver"""
  99. if TYPE_CHECKING: # pragma: no cover
  100. IterableBase = Iterable[Morsel[str]]
  101. else:
  102. IterableBase = Iterable
  103. class AbstractCookieJar(Sized, IterableBase):
  104. """Abstract Cookie Jar."""
  105. def __init__(self, *,
  106. loop: Optional[asyncio.AbstractEventLoop]=None) -> None:
  107. self._loop = get_running_loop(loop)
  108. @abstractmethod
  109. def clear(self) -> None:
  110. """Clear all cookies."""
  111. @abstractmethod
  112. def update_cookies(self,
  113. cookies: LooseCookies,
  114. response_url: URL=URL()) -> None:
  115. """Update cookies."""
  116. @abstractmethod
  117. def filter_cookies(self, request_url: URL) -> 'BaseCookie[str]':
  118. """Return the jar's cookies filtered by their attributes."""
  119. class AbstractStreamWriter(ABC):
  120. """Abstract stream writer."""
  121. buffer_size = 0
  122. output_size = 0
  123. length = 0 # type: Optional[int]
  124. @abstractmethod
  125. async def write(self, chunk: bytes) -> None:
  126. """Write chunk into stream."""
  127. @abstractmethod
  128. async def write_eof(self, chunk: bytes=b'') -> None:
  129. """Write last chunk."""
  130. @abstractmethod
  131. async def drain(self) -> None:
  132. """Flush the write buffer."""
  133. @abstractmethod
  134. def enable_compression(self, encoding: str='deflate') -> None:
  135. """Enable HTTP body compression"""
  136. @abstractmethod
  137. def enable_chunking(self) -> None:
  138. """Enable HTTP chunked mode"""
  139. @abstractmethod
  140. async def write_headers(self, status_line: str,
  141. headers: 'CIMultiDict[str]') -> None:
  142. """Write HTTP headers"""
  143. class AbstractAccessLogger(ABC):
  144. """Abstract writer to access log."""
  145. def __init__(self, logger: logging.Logger, log_format: str) -> None:
  146. self.logger = logger
  147. self.log_format = log_format
  148. @abstractmethod
  149. def log(self,
  150. request: BaseRequest,
  151. response: StreamResponse,
  152. time: float) -> None:
  153. """Emit log to logger."""