Coverage for /pythoncovmergedfiles/medio/medio/src/aiohttp/aiohttp/abc.py: 88%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

136 statements  

1import asyncio 

2import logging 

3import socket 

4from abc import ABC, abstractmethod 

5from collections.abc import Awaitable, Callable, Generator, Iterable, Sequence, Sized 

6from http.cookies import BaseCookie, Morsel, SimpleCookie 

7from types import MappingProxyType 

8from typing import TYPE_CHECKING, Any, TypedDict 

9 

10from multidict import CIMultiDict 

11from yarl import URL 

12 

13from ._cookie_helpers import parse_set_cookie_headers 

14from .typedefs import LooseCookies 

15 

16if TYPE_CHECKING: 

17 from .web_app import Application 

18 from .web_exceptions import HTTPException 

19 from .web_request import BaseRequest, Request 

20 from .web_response import StreamResponse 

21else: 

22 BaseRequest = Request = Application = StreamResponse = Any 

23 HTTPException = Any 

24 

25 

26class AbstractRouter(ABC): 

27 def __init__(self) -> None: 

28 self._frozen = False 

29 

30 def post_init(self, app: Application) -> None: 

31 """Post init stage. 

32 

33 Not an abstract method for sake of backward compatibility, 

34 but if the router wants to be aware of the application 

35 it can override this. 

36 """ 

37 

38 @property 

39 def frozen(self) -> bool: 

40 return self._frozen 

41 

42 def freeze(self) -> None: 

43 """Freeze router.""" 

44 self._frozen = True 

45 

46 @abstractmethod 

47 async def resolve(self, request: Request) -> "AbstractMatchInfo": 

48 """Return MATCH_INFO for given request""" 

49 

50 

51class AbstractMatchInfo(ABC): 

52 

53 __slots__ = () 

54 

55 @property # pragma: no branch 

56 @abstractmethod 

57 def handler(self) -> Callable[[Request], Awaitable[StreamResponse]]: 

58 """Execute matched request handler""" 

59 

60 @property 

61 @abstractmethod 

62 def expect_handler( 

63 self, 

64 ) -> Callable[[Request], Awaitable[StreamResponse | None]]: 

65 """Expect handler for 100-continue processing""" 

66 

67 @property # pragma: no branch 

68 @abstractmethod 

69 def http_exception(self) -> HTTPException | None: 

70 """HTTPException instance raised on router's resolving, or None""" 

71 

72 @abstractmethod # pragma: no branch 

73 def get_info(self) -> dict[str, Any]: 

74 """Return a dict with additional info useful for introspection""" 

75 

76 @property # pragma: no branch 

77 @abstractmethod 

78 def apps(self) -> tuple[Application, ...]: 

79 """Stack of nested applications. 

80 

81 Top level application is left-most element. 

82 

83 """ 

84 

85 @abstractmethod 

86 def add_app(self, app: Application) -> None: 

87 """Add application to the nested apps stack.""" 

88 

89 @abstractmethod 

90 def freeze(self) -> None: 

91 """Freeze the match info. 

92 

93 The method is called after route resolution. 

94 

95 After the call .add_app() is forbidden. 

96 

97 """ 

98 

99 

100class AbstractView(ABC): 

101 """Abstract class based view.""" 

102 

103 def __init__(self, request: Request) -> None: 

104 self._request = request 

105 

106 @property 

107 def request(self) -> Request: 

108 """Request instance.""" 

109 return self._request 

110 

111 @abstractmethod 

112 def __await__(self) -> Generator[None, None, StreamResponse]: 

113 """Execute the view handler.""" 

114 

115 

116class ResolveResult(TypedDict): 

117 """Resolve result. 

118 

119 This is the result returned from an AbstractResolver's 

120 resolve method. 

121 

122 :param hostname: The hostname that was provided. 

123 :param host: The IP address that was resolved. 

124 :param port: The port that was resolved. 

125 :param family: The address family that was resolved. 

126 :param proto: The protocol that was resolved. 

127 :param flags: The flags that were resolved. 

128 """ 

129 

130 hostname: str 

131 host: str 

132 port: int 

133 family: int 

134 proto: int 

135 flags: int 

136 

137 

138class AbstractResolver(ABC): 

139 """Abstract DNS resolver.""" 

140 

141 @abstractmethod 

142 async def resolve( 

143 self, host: str, port: int = 0, family: socket.AddressFamily = socket.AF_INET 

144 ) -> list[ResolveResult]: 

145 """Return IP address for given hostname""" 

146 

147 @abstractmethod 

148 async def close(self) -> None: 

149 """Release resolver""" 

150 

151 

152ClearCookiePredicate = Callable[[Morsel[str]], bool] 

153 

154 

155class AbstractCookieJar(Sized, Iterable[Morsel[str]]): 

156 """Abstract Cookie Jar.""" 

157 

158 @property 

159 @abstractmethod 

160 def unsafe(self) -> bool: 

161 """Return True if cookies can be used with IP addresses.""" 

162 

163 @property 

164 @abstractmethod 

165 def quote_cookie(self) -> bool: 

166 """Return True if cookies should be quoted.""" 

167 

168 @property 

169 def treat_as_secure_origin(self) -> frozenset[URL]: 

170 """Return origins considered secure even over cleartext connections.""" 

171 return frozenset() 

172 

173 @property 

174 @abstractmethod 

175 def cookies(self) -> MappingProxyType[tuple[str, str], SimpleCookie]: 

176 """Return the cookies stored in this jar.""" 

177 

178 @property 

179 @abstractmethod 

180 def host_only_cookies(self) -> frozenset[tuple[str, str, str]]: 

181 """Return the host-only cookies stored in this jar.""" 

182 

183 @abstractmethod 

184 def clear(self, predicate: ClearCookiePredicate | None = None) -> None: 

185 """Clear all cookies if no predicate is passed.""" 

186 

187 @abstractmethod 

188 def clear_domain(self, domain: str) -> None: 

189 """Clear all cookies for domain and all subdomains.""" 

190 

191 @abstractmethod 

192 def update_cookies(self, cookies: LooseCookies, response_url: URL = URL()) -> None: 

193 """Update cookies.""" 

194 

195 def update_cookies_from_headers( 

196 self, headers: Sequence[str], response_url: URL 

197 ) -> None: 

198 """Update cookies from raw Set-Cookie headers.""" 

199 if headers and (cookies_to_update := parse_set_cookie_headers(headers)): 

200 self.update_cookies(cookies_to_update, response_url) 

201 

202 @abstractmethod 

203 def filter_cookies(self, request_url: URL) -> BaseCookie[str]: 

204 """Return the jar's cookies filtered by their attributes.""" 

205 

206 

207class AbstractStreamWriter(ABC): 

208 """Abstract stream writer.""" 

209 

210 buffer_size: int = 0 

211 output_size: int = 0 

212 length: int | None = 0 

213 # Called with each accepted body chunk's byte count (before any 

214 # transport-level transformation such as compression or chunked 

215 # framing) and the writer's total output_size after the chunk. 

216 # Assigned by the client request machinery for upload progress 

217 # tracking; write()/write_eof() implementations should invoke it 

218 # for every body chunk they accept. 

219 on_body_write: Callable[[int, int], None] | None = None 

220 

221 @property 

222 def transport(self) -> asyncio.WriteTransport | None: 

223 """The transport this writer writes to, if any. 

224 

225 Used by upload progress tracking to observe the unsent buffer; 

226 writers without one report progress only on completion. 

227 """ 

228 return None 

229 

230 @abstractmethod 

231 async def write( 

232 self, chunk: "bytes | bytearray | memoryview[int] | memoryview[bytes]" 

233 ) -> None: 

234 """Write chunk into stream.""" 

235 

236 @abstractmethod 

237 async def write_eof(self, chunk: bytes = b"") -> None: 

238 """Write last chunk.""" 

239 

240 @abstractmethod 

241 async def drain(self) -> None: 

242 """Flush the write buffer.""" 

243 

244 @abstractmethod 

245 def enable_compression( 

246 self, encoding: str = "deflate", strategy: int | None = None 

247 ) -> None: 

248 """Enable HTTP body compression""" 

249 

250 @abstractmethod 

251 def enable_chunking(self) -> None: 

252 """Enable HTTP chunked mode""" 

253 

254 @abstractmethod 

255 async def write_headers(self, status_line: str, headers: CIMultiDict[str]) -> None: 

256 """Write HTTP headers""" 

257 

258 def send_headers(self) -> None: 

259 """Force sending buffered headers if not already sent. 

260 

261 Required only if write_headers() buffers headers instead of sending immediately. 

262 For backwards compatibility, this method does nothing by default. 

263 """ 

264 

265 

266class AbstractAccessLogger(ABC): 

267 """Abstract writer to access log.""" 

268 

269 __slots__ = ("logger", "log_format") 

270 

271 def __init__(self, logger: logging.Logger, log_format: str) -> None: 

272 self.logger = logger 

273 self.log_format = log_format 

274 

275 @abstractmethod 

276 def log(self, request: BaseRequest, response: StreamResponse, time: float) -> None: 

277 """Emit log to logger.""" 

278 

279 @property 

280 def enabled(self) -> bool: 

281 """Check if logger is enabled.""" 

282 return True 

283 

284 

285class AbstractAsyncAccessLogger(ABC): 

286 """Abstract asynchronous writer to access log.""" 

287 

288 __slots__ = () 

289 

290 @abstractmethod 

291 async def log( 

292 self, request: BaseRequest, response: StreamResponse, request_start: float 

293 ) -> None: 

294 """Emit log to logger.""" 

295 

296 @property 

297 def enabled(self) -> bool: 

298 """Check if logger is enabled.""" 

299 return True