omlish 0.0.0.dev468__py3-none-any.whl → 0.0.0.dev470__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,9 +1,11 @@
1
1
  import abc
2
+ import contextlib
2
3
  import typing as ta
3
4
 
4
5
  from ... import lang
5
6
  from ..headers import CanHttpHeaders
6
7
  from .asyncs import AsyncHttpClient
8
+ from .base import HttpClientContext
7
9
  from .base import HttpRequest
8
10
  from .base import HttpResponse
9
11
  from .sync import HttpClient
@@ -32,12 +34,13 @@ class _DefaultRequester(lang.Abstract, ta.Generic[C, R]):
32
34
 
33
35
  timeout_s: float | None = None,
34
36
 
37
+ context: HttpClientContext | None = None,
35
38
  check: bool = False,
36
39
  client: C | None = None, # noqa
37
40
 
38
41
  **kwargs: ta.Any,
39
42
  ) -> R:
40
- req = HttpRequest(
43
+ request = HttpRequest( # noqa
41
44
  url,
42
45
  method=method,
43
46
 
@@ -50,7 +53,8 @@ class _DefaultRequester(lang.Abstract, ta.Generic[C, R]):
50
53
  )
51
54
 
52
55
  return self._do(
53
- req,
56
+ request,
57
+ context=context,
54
58
  check=check,
55
59
  client=client,
56
60
  )
@@ -58,8 +62,9 @@ class _DefaultRequester(lang.Abstract, ta.Generic[C, R]):
58
62
  @abc.abstractmethod
59
63
  def _do(
60
64
  self,
61
- req: HttpRequest,
65
+ request: HttpRequest, # noqa
62
66
  *,
67
+ context: HttpClientContext | None = None,
63
68
  check: bool = False,
64
69
  client: C | None = None, # noqa
65
70
  ) -> R:
@@ -77,30 +82,74 @@ def client() -> HttpClient:
77
82
  return _default_client()
78
83
 
79
84
 
85
+ @contextlib.contextmanager
86
+ def manage_client(client: HttpClient | None) -> ta.Generator[HttpClient]: # noqa
87
+ if client is not None:
88
+ yield client
89
+
90
+ else:
91
+ with _default_client() as client: # noqa
92
+ yield client
93
+
94
+
80
95
  #
81
96
 
82
97
 
83
- class _SyncDefaultRequester(_DefaultRequester[HttpClient, HttpResponse]):
98
+ class _BaseSyncDefaultRequester(_DefaultRequester[HttpClient, R], lang.Abstract, ta.Generic[R]):
84
99
  def _do(
85
100
  self,
86
- req: HttpRequest,
101
+ request: HttpRequest, # noqa
87
102
  *,
103
+ context: HttpClientContext | None = None,
88
104
  check: bool = False,
89
105
  client: HttpClient | None = None, # noqa
90
- ) -> HttpResponse:
91
- def do(cli: HttpClient) -> HttpResponse: # noqa
92
- return cli.request(
93
- req,
106
+ ) -> R:
107
+ if context is None:
108
+ context = HttpClientContext()
94
109
 
110
+ if client is not None:
111
+ return self._do_(
112
+ client,
113
+ context,
114
+ request,
95
115
  check=check,
96
116
  )
97
117
 
98
- if client is not None:
99
- return do(client)
100
-
101
118
  else:
102
- with _default_client() as cli:
103
- return do(cli)
119
+ with _default_client() as client: # noqa
120
+ return self._do_(
121
+ client,
122
+ context,
123
+ request,
124
+ check=check,
125
+ )
126
+
127
+ @abc.abstractmethod
128
+ def _do_(
129
+ self,
130
+ client: HttpClient, # noqa
131
+ context: HttpClientContext,
132
+ request: HttpRequest, # noqa
133
+ *,
134
+ check: bool = False, # noqa
135
+ ) -> R:
136
+ raise NotImplementedError
137
+
138
+
139
+ class _SyncDefaultRequester(_BaseSyncDefaultRequester[HttpResponse]):
140
+ def _do_(
141
+ self,
142
+ client: HttpClient, # noqa
143
+ context: HttpClientContext,
144
+ request: HttpRequest, # noqa
145
+ *,
146
+ check: bool = False, # noqa
147
+ ) -> HttpResponse:
148
+ return client.request(
149
+ request,
150
+ context=context,
151
+ check=check,
152
+ )
104
153
 
105
154
 
106
155
  request = _SyncDefaultRequester()
@@ -117,30 +166,74 @@ def async_client() -> AsyncHttpClient:
117
166
  return _default_async_client()
118
167
 
119
168
 
169
+ @contextlib.asynccontextmanager
170
+ async def manage_async_client(client: AsyncHttpClient | None) -> ta.AsyncGenerator[AsyncHttpClient]: # noqa
171
+ if client is not None:
172
+ yield client
173
+
174
+ else:
175
+ async with _default_async_client() as client: # noqa
176
+ yield client
177
+
178
+
120
179
  #
121
180
 
122
181
 
123
- class _AsyncDefaultRequester(_DefaultRequester[AsyncHttpClient, ta.Awaitable[HttpResponse]]):
182
+ class _BaseAsyncDefaultRequester(_DefaultRequester[AsyncHttpClient, ta.Awaitable[R]], lang.Abstract, ta.Generic[R]):
124
183
  async def _do(
125
184
  self,
126
- req: HttpRequest,
185
+ request: HttpRequest, # noqa
127
186
  *,
187
+ context: HttpClientContext | None = None,
128
188
  check: bool = False,
129
189
  client: AsyncHttpClient | None = None, # noqa
130
- ) -> HttpResponse:
131
- async def do(cli: AsyncHttpClient) -> HttpResponse: # noqa
132
- return await cli.request(
133
- req,
190
+ ) -> R:
191
+ if context is None:
192
+ context = HttpClientContext()
134
193
 
194
+ if client is not None:
195
+ return await self._do_(
196
+ client,
197
+ context,
198
+ request,
135
199
  check=check,
136
200
  )
137
201
 
138
- if client is not None:
139
- return await do(client)
140
-
141
202
  else:
142
- async with _default_async_client() as cli:
143
- return await do(cli)
203
+ async with _default_async_client() as client: # noqa
204
+ return await self._do_(
205
+ client,
206
+ context,
207
+ request,
208
+ check=check,
209
+ )
210
+
211
+ @abc.abstractmethod
212
+ def _do_(
213
+ self,
214
+ client: AsyncHttpClient, # noqa
215
+ context: HttpClientContext,
216
+ request: HttpRequest, # noqa
217
+ *,
218
+ check: bool = False, # noqa
219
+ ) -> ta.Awaitable[R]:
220
+ raise NotImplementedError
221
+
222
+
223
+ class _AsyncDefaultRequester(_BaseAsyncDefaultRequester[HttpResponse]):
224
+ async def _do_(
225
+ self,
226
+ client: AsyncHttpClient, # noqa
227
+ context: HttpClientContext,
228
+ request: HttpRequest, # noqa
229
+ *,
230
+ check: bool = False,
231
+ ) -> HttpResponse: # noqa
232
+ return await client.request(
233
+ request,
234
+ context=context,
235
+ check=check,
236
+ )
144
237
 
145
238
 
146
239
  async_request = _AsyncDefaultRequester()
@@ -0,0 +1,50 @@
1
+ # ruff: noqa: UP043 UP045
2
+ # @omlish-lite
3
+ import dataclasses as dc
4
+ import typing as ta
5
+
6
+ from .asyncs import AsyncHttpClient
7
+ from .asyncs import AsyncStreamHttpResponse
8
+ from .base import HttpClientContext
9
+ from .base import HttpRequest
10
+ from .sync import HttpClient
11
+ from .sync import StreamHttpResponse
12
+
13
+
14
+ ##
15
+
16
+
17
+ class ExecutorAsyncHttpClient(AsyncHttpClient):
18
+ def __init__(
19
+ self,
20
+ run_in_executor: ta.Callable[..., ta.Awaitable],
21
+ client: HttpClient,
22
+ ) -> None:
23
+ super().__init__()
24
+
25
+ self._run_in_executor = run_in_executor
26
+ self._client = client
27
+
28
+ @dc.dataclass(frozen=True)
29
+ class _StreamAdapter:
30
+ owner: 'ExecutorAsyncHttpClient'
31
+ resp: StreamHttpResponse
32
+
33
+ async def read1(self, /, n: int = -1) -> bytes:
34
+ return await self.owner._run_in_executor(self.resp.stream.read1, n) # noqa
35
+
36
+ async def close(self) -> None:
37
+ return await self.owner._run_in_executor(self.resp.close) # noqa
38
+
39
+ async def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> AsyncStreamHttpResponse:
40
+ resp: StreamHttpResponse = await self._run_in_executor(lambda: self._client.stream_request(req, context=ctx))
41
+ return AsyncStreamHttpResponse(
42
+ status=resp.status,
43
+ headers=resp.headers,
44
+ request=req,
45
+ underlying=resp,
46
+ **(dict( # type: ignore
47
+ stream=(adapter := self._StreamAdapter(self, resp)),
48
+ _closer=adapter.close,
49
+ ) if resp.has_data else {}),
50
+ )
@@ -4,14 +4,15 @@ TODO:
4
4
  """
5
5
  import contextlib
6
6
  import functools
7
- import io
8
7
  import typing as ta
9
8
 
10
9
  from ... import dataclasses as dc
11
10
  from ... import lang
11
+ from ...io.buffers import ReadableListBuffer
12
12
  from ..headers import HttpHeaders
13
13
  from .asyncs import AsyncHttpClient
14
14
  from .asyncs import AsyncStreamHttpResponse
15
+ from .base import HttpClientContext
15
16
  from .base import HttpClientError
16
17
  from .base import HttpRequest
17
18
  from .sync import HttpClient
@@ -31,18 +32,29 @@ class HttpxHttpClient(HttpClient):
31
32
  @dc.dataclass(frozen=True)
32
33
  class _StreamAdapter:
33
34
  it: ta.Iterator[bytes]
35
+ buf: ReadableListBuffer = dc.field(default_factory=ReadableListBuffer)
34
36
 
35
- def read(self, /, n: int = -1) -> bytes:
36
- # FIXME: lol n
37
+ def read1(self, /, n: int = -1) -> bytes:
37
38
  if n < 0:
38
- return b''.join(self.it)
39
- else:
39
+ if (b := self.buf.read(n)) is not None:
40
+ return b
40
41
  try:
41
42
  return next(self.it)
42
43
  except StopIteration:
43
44
  return b''
44
45
 
45
- def _stream_request(self, req: HttpRequest) -> StreamHttpResponse:
46
+ else:
47
+ while len(self.buf) < n:
48
+ try:
49
+ b = next(self.it)
50
+ except StopIteration:
51
+ b = b''
52
+ if not b:
53
+ return self.buf.read() or b''
54
+ self.buf.feed(b)
55
+ return self.buf.read(n) or b''
56
+
57
+ def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> StreamHttpResponse:
46
58
  try:
47
59
  resp_cm = httpx.stream(
48
60
  method=req.method_or_default,
@@ -84,21 +96,29 @@ class HttpxAsyncHttpClient(AsyncHttpClient):
84
96
  @dc.dataclass(frozen=True)
85
97
  class _StreamAdapter:
86
98
  it: ta.AsyncIterator[bytes]
99
+ buf: ReadableListBuffer = dc.field(default_factory=ReadableListBuffer)
87
100
 
88
- async def read(self, /, n: int = -1) -> bytes:
89
- # FIXME: lol n
101
+ async def read1(self, /, n: int = -1) -> bytes:
90
102
  if n < 0:
91
- buf = io.BytesIO()
92
- async for chunk in self.it:
93
- buf.write(chunk)
94
- return buf.getvalue()
95
- else:
103
+ if (b := self.buf.read(n)) is not None:
104
+ return b
96
105
  try:
97
106
  return await anext(self.it)
98
- except StopIteration:
107
+ except StopAsyncIteration:
99
108
  return b''
100
109
 
101
- async def _stream_request(self, req: HttpRequest) -> AsyncStreamHttpResponse:
110
+ else:
111
+ while len(self.buf) < n:
112
+ try:
113
+ b = await anext(self.it)
114
+ except StopAsyncIteration:
115
+ b = b''
116
+ if not b:
117
+ return self.buf.read() or b''
118
+ self.buf.feed(b)
119
+ return self.buf.read(n) or b''
120
+
121
+ async def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> AsyncStreamHttpResponse:
102
122
  es = contextlib.AsyncExitStack()
103
123
 
104
124
  try:
@@ -0,0 +1,181 @@
1
+ # ruff: noqa: UP007 UP043 UP045
2
+ # @omlish-lite
3
+ """
4
+ TODO:
5
+ - redirect
6
+ - referrer header?
7
+ - non-forwarded headers, host check, etc lol
8
+ - 'check' kw becomes StatusCheckingMiddleware?
9
+ """
10
+ import dataclasses as dc
11
+ import typing as ta
12
+ import urllib.parse
13
+
14
+ from ...lite.abstract import Abstract
15
+ from ...lite.check import check
16
+ from ..urls import parsed_url_replace
17
+ from .asyncs import AsyncHttpClient
18
+ from .asyncs import AsyncStreamHttpResponse
19
+ from .base import BaseHttpClient
20
+ from .base import BaseHttpResponse
21
+ from .base import HttpClientContext
22
+ from .base import HttpClientError
23
+ from .base import HttpRequest
24
+ from .sync import HttpClient
25
+ from .sync import StreamHttpResponse
26
+ from .sync import close_http_client_response
27
+
28
+
29
+ BaseHttpClientT = ta.TypeVar('BaseHttpClientT', bound=BaseHttpClient)
30
+
31
+
32
+ ##
33
+
34
+
35
+ class HttpClientMiddleware(Abstract):
36
+ def process_request(
37
+ self,
38
+ ctx: HttpClientContext,
39
+ req: HttpRequest,
40
+ ) -> HttpRequest:
41
+ return req
42
+
43
+ def process_response(
44
+ self,
45
+ ctx: HttpClientContext,
46
+ req: HttpRequest,
47
+ resp: BaseHttpResponse,
48
+ ) -> ta.Union[BaseHttpResponse, HttpRequest]:
49
+ return resp
50
+
51
+
52
+ class AbstractMiddlewareHttpClient(Abstract, ta.Generic[BaseHttpClientT]):
53
+ def __init__(
54
+ self,
55
+ client: BaseHttpClientT,
56
+ middlewares: ta.Iterable[HttpClientMiddleware],
57
+ ) -> None:
58
+ super().__init__()
59
+
60
+ self._client = client
61
+ self._middlewares = list(middlewares)
62
+
63
+ def _process_request(
64
+ self,
65
+ ctx: HttpClientContext,
66
+ req: HttpRequest,
67
+ ) -> HttpRequest:
68
+ for mw in self._middlewares:
69
+ req = mw.process_request(ctx, req)
70
+ return req
71
+
72
+ def _process_response(
73
+ self,
74
+ ctx: HttpClientContext,
75
+ req: HttpRequest,
76
+ resp: BaseHttpResponse,
77
+ ) -> ta.Union[BaseHttpResponse, HttpRequest]:
78
+ for mw in self._middlewares:
79
+ nxt = mw.process_response(ctx, req, resp)
80
+ if isinstance(nxt, HttpRequest):
81
+ return nxt
82
+ else:
83
+ resp = nxt
84
+ return resp
85
+
86
+
87
+ #
88
+
89
+
90
+ class MiddlewareHttpClient(AbstractMiddlewareHttpClient[HttpClient], HttpClient):
91
+ def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> StreamHttpResponse:
92
+ while True:
93
+ req = self._process_request(ctx, req)
94
+
95
+ resp = self._client.stream_request(req, context=ctx)
96
+
97
+ try:
98
+ out = self._process_response(ctx, req, resp)
99
+
100
+ if isinstance(out, HttpRequest):
101
+ close_http_client_response(resp)
102
+ req = out
103
+ continue
104
+
105
+ elif isinstance(out, StreamHttpResponse):
106
+ return out
107
+
108
+ else:
109
+ raise TypeError(out) # noqa
110
+
111
+ except Exception:
112
+ close_http_client_response(resp)
113
+ raise
114
+
115
+ raise RuntimeError
116
+
117
+
118
+ class MiddlewareAsyncHttpClient(AbstractMiddlewareHttpClient[AsyncHttpClient], AsyncHttpClient):
119
+ def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> ta.Awaitable[AsyncStreamHttpResponse]:
120
+ return self._client.stream_request(self._process_request(ctx, req))
121
+
122
+
123
+ ##
124
+
125
+
126
+ class TooManyRedirectsHttpClientError(HttpClientError):
127
+ pass
128
+
129
+
130
+ class RedirectHandlingHttpClientMiddleware(HttpClientMiddleware):
131
+ DEFAULT_MAX_REDIRECTS: ta.ClassVar[int] = 5
132
+
133
+ def __init__(
134
+ self,
135
+ *,
136
+ max_redirects: ta.Optional[int] = None,
137
+ ) -> None:
138
+ super().__init__()
139
+
140
+ if max_redirects is None:
141
+ max_redirects = self.DEFAULT_MAX_REDIRECTS
142
+ self._max_redirects = max_redirects
143
+
144
+ @dc.dataclass()
145
+ class _State:
146
+ num_redirects: int = 0
147
+
148
+ def _get_state(self, ctx: HttpClientContext) -> _State:
149
+ try:
150
+ return ctx._dct[self._State] # noqa
151
+ except KeyError:
152
+ ret = ctx._dct[self._State] = self._State() # noqa
153
+ return ret
154
+
155
+ def process_response(
156
+ self,
157
+ ctx: HttpClientContext,
158
+ req: HttpRequest,
159
+ resp: BaseHttpResponse,
160
+ ) -> ta.Union[BaseHttpResponse, HttpRequest]: # noqa
161
+ if resp.status == 302:
162
+ st = self._get_state(ctx)
163
+ if st.num_redirects >= self._max_redirects:
164
+ raise TooManyRedirectsHttpClientError
165
+ st.num_redirects += 1
166
+
167
+ rd_url = check.not_none(resp.headers).single_str_dct['location']
168
+
169
+ rd_purl = urllib.parse.urlparse(rd_url)
170
+ if not rd_purl.netloc:
171
+ rq_purl = urllib.parse.urlparse(req.url)
172
+ rd_purl = parsed_url_replace(
173
+ rd_purl,
174
+ scheme=rq_purl.scheme,
175
+ netloc=rq_purl.netloc,
176
+ )
177
+ rd_url = urllib.parse.urlunparse(rd_purl)
178
+
179
+ return dc.replace(req, url=rd_url)
180
+
181
+ return resp
@@ -3,13 +3,15 @@
3
3
  import abc
4
4
  import contextlib
5
5
  import dataclasses as dc
6
+ import io
6
7
  import typing as ta
7
8
 
8
9
  from ...lite.abstract import Abstract
9
- from ...lite.dataclasses import dataclass_maybe_post_init
10
10
  from ...lite.dataclasses import dataclass_shallow_asdict
11
+ from .base import BaseHttpClient
11
12
  from .base import BaseHttpResponse
12
13
  from .base import BaseHttpResponseT
14
+ from .base import HttpClientContext
13
15
  from .base import HttpRequest
14
16
  from .base import HttpResponse
15
17
  from .base import HttpStatusError
@@ -26,21 +28,26 @@ HttpClientT = ta.TypeVar('HttpClientT', bound='HttpClient')
26
28
  @dc.dataclass(frozen=True) # kw_only=True
27
29
  class StreamHttpResponse(BaseHttpResponse):
28
30
  class Stream(ta.Protocol):
29
- def read(self, /, n: int = -1) -> bytes: ...
31
+ def read1(self, /, n: int = -1) -> bytes: ...
30
32
 
31
33
  @ta.final
32
34
  class _NullStream:
33
- def read(self, /, n: int = -1) -> bytes:
35
+ def read1(self, /, n: int = -1) -> bytes:
34
36
  raise TypeError
35
37
 
36
38
  stream: Stream = _NullStream()
37
39
 
38
- _closer: ta.Optional[ta.Callable[[], None]] = None
40
+ @property
41
+ def has_data(self) -> bool:
42
+ return not isinstance(self.stream, StreamHttpResponse._NullStream)
43
+
44
+ def read_all(self) -> bytes:
45
+ buf = io.BytesIO()
46
+ while (b := self.stream.read1()):
47
+ buf.write(b)
48
+ return buf.getvalue()
39
49
 
40
- def __post_init__(self) -> None:
41
- dataclass_maybe_post_init(super())
42
- if isinstance(self.stream, StreamHttpResponse._NullStream):
43
- raise TypeError(self.stream)
50
+ _closer: ta.Optional[ta.Callable[[], None]] = None
44
51
 
45
52
  def __enter__(self: StreamHttpResponseT) -> StreamHttpResponseT:
46
53
  return self
@@ -50,13 +57,13 @@ class StreamHttpResponse(BaseHttpResponse):
50
57
 
51
58
  def close(self) -> None:
52
59
  if (c := self._closer) is not None:
53
- c()
60
+ c() # noqa
54
61
 
55
62
 
56
63
  #
57
64
 
58
65
 
59
- def close_response(resp: BaseHttpResponse) -> None:
66
+ def close_http_client_response(resp: BaseHttpResponse) -> None:
60
67
  if isinstance(resp, HttpResponse):
61
68
  pass
62
69
 
@@ -68,7 +75,7 @@ def close_response(resp: BaseHttpResponse) -> None:
68
75
 
69
76
 
70
77
  @contextlib.contextmanager
71
- def closing_response(resp: BaseHttpResponseT) -> ta.Iterator[BaseHttpResponseT]:
78
+ def closing_http_client_response(resp: BaseHttpResponseT) -> ta.Iterator[BaseHttpResponseT]:
72
79
  if isinstance(resp, HttpResponse):
73
80
  yield resp
74
81
  return
@@ -81,15 +88,14 @@ def closing_response(resp: BaseHttpResponseT) -> ta.Iterator[BaseHttpResponseT]:
81
88
  raise TypeError(resp)
82
89
 
83
90
 
84
- def read_response(resp: BaseHttpResponse) -> HttpResponse:
91
+ def read_http_client_response(resp: BaseHttpResponse) -> HttpResponse:
85
92
  if isinstance(resp, HttpResponse):
86
93
  return resp
87
94
 
88
95
  elif isinstance(resp, StreamHttpResponse):
89
- data = resp.stream.read()
90
96
  return HttpResponse(**{
91
97
  **{k: v for k, v in dataclass_shallow_asdict(resp).items() if k not in ('stream', '_closer')},
92
- 'data': data,
98
+ **({'data': resp.read_all()} if resp.has_data else {}),
93
99
  })
94
100
 
95
101
  else:
@@ -99,7 +105,7 @@ def read_response(resp: BaseHttpResponse) -> HttpResponse:
99
105
  ##
100
106
 
101
107
 
102
- class HttpClient(Abstract):
108
+ class HttpClient(BaseHttpClient, Abstract):
103
109
  def __enter__(self: HttpClientT) -> HttpClientT:
104
110
  return self
105
111
 
@@ -110,21 +116,27 @@ class HttpClient(Abstract):
110
116
  self,
111
117
  req: HttpRequest,
112
118
  *,
119
+ context: ta.Optional[HttpClientContext] = None,
113
120
  check: bool = False,
114
121
  ) -> HttpResponse:
115
- with closing_response(self.stream_request(
122
+ with closing_http_client_response(self.stream_request(
116
123
  req,
124
+ context=context,
117
125
  check=check,
118
126
  )) as resp:
119
- return read_response(resp)
127
+ return read_http_client_response(resp)
120
128
 
121
129
  def stream_request(
122
130
  self,
123
131
  req: HttpRequest,
124
132
  *,
133
+ context: ta.Optional[HttpClientContext] = None,
125
134
  check: bool = False,
126
135
  ) -> StreamHttpResponse:
127
- resp = self._stream_request(req)
136
+ if context is None:
137
+ context = HttpClientContext()
138
+
139
+ resp = self._stream_request(context, req)
128
140
 
129
141
  try:
130
142
  if check and not resp.is_success:
@@ -132,14 +144,14 @@ class HttpClient(Abstract):
132
144
  cause = resp.underlying
133
145
  else:
134
146
  cause = None
135
- raise HttpStatusError(read_response(resp)) from cause # noqa
147
+ raise HttpStatusError(read_http_client_response(resp)) from cause # noqa
136
148
 
137
149
  except Exception:
138
- close_response(resp)
150
+ close_http_client_response(resp)
139
151
  raise
140
152
 
141
153
  return resp
142
154
 
143
155
  @abc.abstractmethod
144
- def _stream_request(self, req: HttpRequest) -> StreamHttpResponse:
156
+ def _stream_request(self, ctx: HttpClientContext, req: HttpRequest) -> StreamHttpResponse:
145
157
  raise NotImplementedError