fixturify 0.1.9__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.
- fixturify/__init__.py +21 -0
- fixturify/_utils/__init__.py +7 -0
- fixturify/_utils/_constants.py +10 -0
- fixturify/_utils/_fixture_discovery.py +165 -0
- fixturify/_utils/_path_resolver.py +135 -0
- fixturify/http_d/__init__.py +80 -0
- fixturify/http_d/_config.py +214 -0
- fixturify/http_d/_decorator.py +267 -0
- fixturify/http_d/_exceptions.py +153 -0
- fixturify/http_d/_fixture_discovery.py +33 -0
- fixturify/http_d/_matcher.py +372 -0
- fixturify/http_d/_mock_context.py +154 -0
- fixturify/http_d/_models.py +205 -0
- fixturify/http_d/_patcher.py +524 -0
- fixturify/http_d/_player.py +222 -0
- fixturify/http_d/_recorder.py +1350 -0
- fixturify/http_d/_stubs/__init__.py +8 -0
- fixturify/http_d/_stubs/_aiohttp.py +220 -0
- fixturify/http_d/_stubs/_connection.py +478 -0
- fixturify/http_d/_stubs/_httpcore.py +269 -0
- fixturify/http_d/_stubs/_tornado.py +95 -0
- fixturify/http_d/_utils.py +194 -0
- fixturify/json_assert/__init__.py +13 -0
- fixturify/json_assert/_actual_saver.py +67 -0
- fixturify/json_assert/_assert.py +173 -0
- fixturify/json_assert/_comparator.py +183 -0
- fixturify/json_assert/_diff_formatter.py +265 -0
- fixturify/json_assert/_normalizer.py +83 -0
- fixturify/object_mapper/__init__.py +5 -0
- fixturify/object_mapper/_deserializers/__init__.py +19 -0
- fixturify/object_mapper/_deserializers/_base.py +186 -0
- fixturify/object_mapper/_deserializers/_dataclass.py +52 -0
- fixturify/object_mapper/_deserializers/_plain.py +55 -0
- fixturify/object_mapper/_deserializers/_pydantic_v1.py +38 -0
- fixturify/object_mapper/_deserializers/_pydantic_v2.py +41 -0
- fixturify/object_mapper/_deserializers/_sqlalchemy.py +72 -0
- fixturify/object_mapper/_deserializers/_sqlmodel.py +43 -0
- fixturify/object_mapper/_detectors/__init__.py +5 -0
- fixturify/object_mapper/_detectors/_type_detector.py +186 -0
- fixturify/object_mapper/_serializers/__init__.py +19 -0
- fixturify/object_mapper/_serializers/_base.py +260 -0
- fixturify/object_mapper/_serializers/_dataclass.py +55 -0
- fixturify/object_mapper/_serializers/_plain.py +49 -0
- fixturify/object_mapper/_serializers/_pydantic_v1.py +49 -0
- fixturify/object_mapper/_serializers/_pydantic_v2.py +49 -0
- fixturify/object_mapper/_serializers/_sqlalchemy.py +70 -0
- fixturify/object_mapper/_serializers/_sqlmodel.py +54 -0
- fixturify/object_mapper/mapper.py +256 -0
- fixturify/read_d/__init__.py +5 -0
- fixturify/read_d/_decorator.py +193 -0
- fixturify/read_d/_fixture_loader.py +88 -0
- fixturify/sql_d/__init__.py +7 -0
- fixturify/sql_d/_config.py +30 -0
- fixturify/sql_d/_decorator.py +373 -0
- fixturify/sql_d/_driver_registry.py +133 -0
- fixturify/sql_d/_executor.py +82 -0
- fixturify/sql_d/_fixture_discovery.py +55 -0
- fixturify/sql_d/_phase.py +10 -0
- fixturify/sql_d/_strategies/__init__.py +11 -0
- fixturify/sql_d/_strategies/_aiomysql.py +63 -0
- fixturify/sql_d/_strategies/_aiosqlite.py +29 -0
- fixturify/sql_d/_strategies/_asyncpg.py +34 -0
- fixturify/sql_d/_strategies/_base.py +118 -0
- fixturify/sql_d/_strategies/_mysql.py +70 -0
- fixturify/sql_d/_strategies/_psycopg.py +35 -0
- fixturify/sql_d/_strategies/_psycopg2.py +40 -0
- fixturify/sql_d/_strategies/_registry.py +109 -0
- fixturify/sql_d/_strategies/_sqlite.py +33 -0
- fixturify-0.1.9.dist-info/METADATA +122 -0
- fixturify-0.1.9.dist-info/RECORD +71 -0
- fixturify-0.1.9.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
"""Patcher builder for HTTP client mocking.
|
|
2
|
+
|
|
3
|
+
Uses unittest.mock.patch.object to patch HTTP clients at various levels:
|
|
4
|
+
- http.client (covers requests, urllib3, httplib2, boto3)
|
|
5
|
+
- httpcore (covers httpx sync and async)
|
|
6
|
+
- aiohttp (async HTTP client)
|
|
7
|
+
- tornado (async HTTP client)
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import contextlib
|
|
11
|
+
from typing import TYPE_CHECKING, Dict, Iterator, Type
|
|
12
|
+
from unittest import mock
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from ._mock_context import HttpMockContext
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Save original classes at import time for force_reset()
|
|
19
|
+
_originals: Dict[str, object] = {}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _save_originals():
|
|
23
|
+
"""Save original HTTP classes for later restoration."""
|
|
24
|
+
import http.client as httplib
|
|
25
|
+
_originals["httplib.HTTPConnection"] = httplib.HTTPConnection
|
|
26
|
+
_originals["httplib.HTTPSConnection"] = httplib.HTTPSConnection
|
|
27
|
+
|
|
28
|
+
# urllib3 has its own connection classes
|
|
29
|
+
try:
|
|
30
|
+
import urllib3.connection
|
|
31
|
+
_originals["urllib3.HTTPConnection"] = urllib3.connection.HTTPConnection
|
|
32
|
+
_originals["urllib3.HTTPSConnection"] = urllib3.connection.HTTPSConnection
|
|
33
|
+
except ImportError:
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
# urllib3 connection pools have ConnectionCls attribute
|
|
37
|
+
try:
|
|
38
|
+
from urllib3.connectionpool import HTTPConnectionPool, HTTPSConnectionPool
|
|
39
|
+
_originals["urllib3.HTTPConnectionPool.ConnectionCls"] = HTTPConnectionPool.ConnectionCls
|
|
40
|
+
_originals["urllib3.HTTPSConnectionPool.ConnectionCls"] = HTTPSConnectionPool.ConnectionCls
|
|
41
|
+
except ImportError:
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
# Also check requests.packages.urllib3 (bundled version)
|
|
45
|
+
try:
|
|
46
|
+
import requests.packages.urllib3.connection as req_urllib3_conn
|
|
47
|
+
_originals["requests.urllib3.HTTPConnection"] = req_urllib3_conn.HTTPConnection
|
|
48
|
+
_originals["requests.urllib3.HTTPSConnection"] = req_urllib3_conn.HTTPSConnection
|
|
49
|
+
except ImportError:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
try:
|
|
53
|
+
from requests.packages.urllib3.connectionpool import (
|
|
54
|
+
HTTPConnectionPool as ReqHTTPConnectionPool,
|
|
55
|
+
HTTPSConnectionPool as ReqHTTPSConnectionPool,
|
|
56
|
+
)
|
|
57
|
+
_originals["requests.urllib3.HTTPConnectionPool.ConnectionCls"] = ReqHTTPConnectionPool.ConnectionCls
|
|
58
|
+
_originals["requests.urllib3.HTTPSConnectionPool.ConnectionCls"] = ReqHTTPSConnectionPool.ConnectionCls
|
|
59
|
+
except ImportError:
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
try:
|
|
63
|
+
import httpcore
|
|
64
|
+
_originals["httpcore.ConnectionPool.handle_request"] = (
|
|
65
|
+
httpcore.ConnectionPool.handle_request
|
|
66
|
+
)
|
|
67
|
+
_originals["httpcore.AsyncConnectionPool.handle_async_request"] = (
|
|
68
|
+
httpcore.AsyncConnectionPool.handle_async_request
|
|
69
|
+
)
|
|
70
|
+
except ImportError:
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
try:
|
|
74
|
+
import aiohttp.client
|
|
75
|
+
_originals["aiohttp.ClientSession._request"] = (
|
|
76
|
+
aiohttp.client.ClientSession._request
|
|
77
|
+
)
|
|
78
|
+
except ImportError:
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
try:
|
|
82
|
+
import tornado.simple_httpclient
|
|
83
|
+
_originals["tornado.SimpleAsyncHTTPClient.fetch_impl"] = (
|
|
84
|
+
tornado.simple_httpclient.SimpleAsyncHTTPClient.fetch_impl
|
|
85
|
+
)
|
|
86
|
+
except ImportError:
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
try:
|
|
90
|
+
import tornado.curl_httpclient
|
|
91
|
+
_originals["tornado.CurlAsyncHTTPClient.fetch_impl"] = (
|
|
92
|
+
tornado.curl_httpclient.CurlAsyncHTTPClient.fetch_impl
|
|
93
|
+
)
|
|
94
|
+
except ImportError:
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
# httplib2 uses SCHEME_TO_CONNECTION dict
|
|
98
|
+
try:
|
|
99
|
+
import httplib2
|
|
100
|
+
_originals["httplib2.SCHEME_TO_CONNECTION"] = dict(httplib2.SCHEME_TO_CONNECTION)
|
|
101
|
+
except ImportError:
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
# botocore has its own connection classes
|
|
105
|
+
try:
|
|
106
|
+
import botocore.awsrequest
|
|
107
|
+
_originals["botocore.AWSHTTPConnectionPool.ConnectionCls"] = (
|
|
108
|
+
botocore.awsrequest.AWSHTTPConnectionPool.ConnectionCls
|
|
109
|
+
)
|
|
110
|
+
_originals["botocore.AWSHTTPSConnectionPool.ConnectionCls"] = (
|
|
111
|
+
botocore.awsrequest.AWSHTTPSConnectionPool.ConnectionCls
|
|
112
|
+
)
|
|
113
|
+
except ImportError:
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
# Save originals at module load
|
|
118
|
+
_save_originals()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@contextlib.contextmanager
|
|
122
|
+
def force_reset():
|
|
123
|
+
"""
|
|
124
|
+
Context manager that temporarily restores original HTTP classes.
|
|
125
|
+
|
|
126
|
+
Used when making real HTTP requests (e.g., in record mode) to avoid
|
|
127
|
+
infinite recursion through our patched classes.
|
|
128
|
+
"""
|
|
129
|
+
import http.client as httplib
|
|
130
|
+
|
|
131
|
+
patchers = []
|
|
132
|
+
|
|
133
|
+
# Always restore http.client
|
|
134
|
+
patchers.append(mock.patch.object(
|
|
135
|
+
httplib, "HTTPConnection", _originals["httplib.HTTPConnection"]
|
|
136
|
+
))
|
|
137
|
+
patchers.append(mock.patch.object(
|
|
138
|
+
httplib, "HTTPSConnection", _originals["httplib.HTTPSConnection"]
|
|
139
|
+
))
|
|
140
|
+
|
|
141
|
+
# Restore urllib3 if available
|
|
142
|
+
if "urllib3.HTTPConnection" in _originals:
|
|
143
|
+
import urllib3.connection
|
|
144
|
+
patchers.append(mock.patch.object(
|
|
145
|
+
urllib3.connection, "HTTPConnection",
|
|
146
|
+
_originals["urllib3.HTTPConnection"]
|
|
147
|
+
))
|
|
148
|
+
patchers.append(mock.patch.object(
|
|
149
|
+
urllib3.connection, "HTTPSConnection",
|
|
150
|
+
_originals["urllib3.HTTPSConnection"]
|
|
151
|
+
))
|
|
152
|
+
|
|
153
|
+
# Restore urllib3 ConnectionCls
|
|
154
|
+
if "urllib3.HTTPConnectionPool.ConnectionCls" in _originals:
|
|
155
|
+
from urllib3.connectionpool import HTTPConnectionPool, HTTPSConnectionPool
|
|
156
|
+
patchers.append(mock.patch.object(
|
|
157
|
+
HTTPConnectionPool, "ConnectionCls",
|
|
158
|
+
_originals["urllib3.HTTPConnectionPool.ConnectionCls"]
|
|
159
|
+
))
|
|
160
|
+
patchers.append(mock.patch.object(
|
|
161
|
+
HTTPSConnectionPool, "ConnectionCls",
|
|
162
|
+
_originals["urllib3.HTTPSConnectionPool.ConnectionCls"]
|
|
163
|
+
))
|
|
164
|
+
|
|
165
|
+
# Restore requests.packages.urllib3 if available
|
|
166
|
+
if "requests.urllib3.HTTPConnection" in _originals:
|
|
167
|
+
import requests.packages.urllib3.connection as req_urllib3_conn
|
|
168
|
+
patchers.append(mock.patch.object(
|
|
169
|
+
req_urllib3_conn, "HTTPConnection",
|
|
170
|
+
_originals["requests.urllib3.HTTPConnection"]
|
|
171
|
+
))
|
|
172
|
+
patchers.append(mock.patch.object(
|
|
173
|
+
req_urllib3_conn, "HTTPSConnection",
|
|
174
|
+
_originals["requests.urllib3.HTTPSConnection"]
|
|
175
|
+
))
|
|
176
|
+
|
|
177
|
+
# Restore requests.packages.urllib3 ConnectionCls
|
|
178
|
+
if "requests.urllib3.HTTPConnectionPool.ConnectionCls" in _originals:
|
|
179
|
+
from requests.packages.urllib3.connectionpool import (
|
|
180
|
+
HTTPConnectionPool as ReqHTTPConnectionPool,
|
|
181
|
+
HTTPSConnectionPool as ReqHTTPSConnectionPool,
|
|
182
|
+
)
|
|
183
|
+
patchers.append(mock.patch.object(
|
|
184
|
+
ReqHTTPConnectionPool, "ConnectionCls",
|
|
185
|
+
_originals["requests.urllib3.HTTPConnectionPool.ConnectionCls"]
|
|
186
|
+
))
|
|
187
|
+
patchers.append(mock.patch.object(
|
|
188
|
+
ReqHTTPSConnectionPool, "ConnectionCls",
|
|
189
|
+
_originals["requests.urllib3.HTTPSConnectionPool.ConnectionCls"]
|
|
190
|
+
))
|
|
191
|
+
|
|
192
|
+
# Restore httplib2 if available
|
|
193
|
+
if "httplib2.SCHEME_TO_CONNECTION" in _originals:
|
|
194
|
+
import httplib2
|
|
195
|
+
patchers.append(mock.patch.dict(
|
|
196
|
+
httplib2.SCHEME_TO_CONNECTION,
|
|
197
|
+
_originals["httplib2.SCHEME_TO_CONNECTION"],
|
|
198
|
+
clear=True
|
|
199
|
+
))
|
|
200
|
+
|
|
201
|
+
# Restore botocore if available
|
|
202
|
+
if "botocore.AWSHTTPConnectionPool.ConnectionCls" in _originals:
|
|
203
|
+
import botocore.awsrequest
|
|
204
|
+
patchers.append(mock.patch.object(
|
|
205
|
+
botocore.awsrequest.AWSHTTPConnectionPool, "ConnectionCls",
|
|
206
|
+
_originals["botocore.AWSHTTPConnectionPool.ConnectionCls"]
|
|
207
|
+
))
|
|
208
|
+
patchers.append(mock.patch.object(
|
|
209
|
+
botocore.awsrequest.AWSHTTPSConnectionPool, "ConnectionCls",
|
|
210
|
+
_originals["botocore.AWSHTTPSConnectionPool.ConnectionCls"]
|
|
211
|
+
))
|
|
212
|
+
|
|
213
|
+
# Restore httpcore if available
|
|
214
|
+
if "httpcore.ConnectionPool.handle_request" in _originals:
|
|
215
|
+
import httpcore
|
|
216
|
+
patchers.append(mock.patch.object(
|
|
217
|
+
httpcore.ConnectionPool, "handle_request",
|
|
218
|
+
_originals["httpcore.ConnectionPool.handle_request"]
|
|
219
|
+
))
|
|
220
|
+
patchers.append(mock.patch.object(
|
|
221
|
+
httpcore.AsyncConnectionPool, "handle_async_request",
|
|
222
|
+
_originals["httpcore.AsyncConnectionPool.handle_async_request"]
|
|
223
|
+
))
|
|
224
|
+
|
|
225
|
+
# Restore aiohttp if available
|
|
226
|
+
if "aiohttp.ClientSession._request" in _originals:
|
|
227
|
+
import aiohttp.client
|
|
228
|
+
patchers.append(mock.patch.object(
|
|
229
|
+
aiohttp.client.ClientSession, "_request",
|
|
230
|
+
_originals["aiohttp.ClientSession._request"]
|
|
231
|
+
))
|
|
232
|
+
|
|
233
|
+
# Restore tornado if available
|
|
234
|
+
if "tornado.SimpleAsyncHTTPClient.fetch_impl" in _originals:
|
|
235
|
+
import tornado.simple_httpclient
|
|
236
|
+
patchers.append(mock.patch.object(
|
|
237
|
+
tornado.simple_httpclient.SimpleAsyncHTTPClient, "fetch_impl",
|
|
238
|
+
_originals["tornado.SimpleAsyncHTTPClient.fetch_impl"]
|
|
239
|
+
))
|
|
240
|
+
|
|
241
|
+
if "tornado.CurlAsyncHTTPClient.fetch_impl" in _originals:
|
|
242
|
+
import tornado.curl_httpclient
|
|
243
|
+
patchers.append(mock.patch.object(
|
|
244
|
+
tornado.curl_httpclient.CurlAsyncHTTPClient, "fetch_impl",
|
|
245
|
+
_originals["tornado.CurlAsyncHTTPClient.fetch_impl"]
|
|
246
|
+
))
|
|
247
|
+
|
|
248
|
+
# Start all patchers
|
|
249
|
+
for patcher in patchers:
|
|
250
|
+
patcher.start()
|
|
251
|
+
|
|
252
|
+
try:
|
|
253
|
+
yield
|
|
254
|
+
finally:
|
|
255
|
+
# Stop all patchers in reverse order
|
|
256
|
+
for patcher in reversed(patchers):
|
|
257
|
+
patcher.stop()
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class PatcherBuilder:
|
|
261
|
+
"""
|
|
262
|
+
Builds mock.patch objects for all HTTP clients.
|
|
263
|
+
|
|
264
|
+
The key insight is that we create dynamic subclasses of our stub classes
|
|
265
|
+
with the mock_context attribute set. This allows the stubs to access
|
|
266
|
+
the mock context without using thread-local storage or context variables.
|
|
267
|
+
"""
|
|
268
|
+
|
|
269
|
+
def __init__(self, mock_context: "HttpMockContext"):
|
|
270
|
+
"""
|
|
271
|
+
Initialize the patcher builder.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
mock_context: The HttpMockContext to use for recording/playback
|
|
275
|
+
"""
|
|
276
|
+
self._mock_context = mock_context
|
|
277
|
+
self._class_cache: Dict[Type, Type] = {}
|
|
278
|
+
|
|
279
|
+
def build(self) -> Iterator[mock._patch]:
|
|
280
|
+
"""
|
|
281
|
+
Yield all patches to apply.
|
|
282
|
+
|
|
283
|
+
Each patch is a context manager that can be entered/exited.
|
|
284
|
+
"""
|
|
285
|
+
yield from self._httplib_patches()
|
|
286
|
+
yield from self._urllib3_patches()
|
|
287
|
+
yield from self._httplib2_patches()
|
|
288
|
+
yield from self._botocore_patches()
|
|
289
|
+
yield from self._httpcore_patches()
|
|
290
|
+
yield from self._aiohttp_patches()
|
|
291
|
+
yield from self._tornado_patches()
|
|
292
|
+
|
|
293
|
+
def _get_stub_with_context(self, stub_class: Type) -> Type:
|
|
294
|
+
"""
|
|
295
|
+
Create a subclass of stub_class with mock_context attribute set.
|
|
296
|
+
|
|
297
|
+
This is the key mechanism for passing context to stubs without
|
|
298
|
+
using thread-local storage. Each test gets its own subclass
|
|
299
|
+
with its own mock_context.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
stub_class: The stub class to subclass
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
A new class with mock_context set to self._mock_context
|
|
306
|
+
"""
|
|
307
|
+
if stub_class not in self._class_cache:
|
|
308
|
+
# Create dynamic subclass with mock_context attribute
|
|
309
|
+
self._class_cache[stub_class] = type(
|
|
310
|
+
stub_class.__name__,
|
|
311
|
+
(stub_class,),
|
|
312
|
+
{"mock_context": self._mock_context}
|
|
313
|
+
)
|
|
314
|
+
return self._class_cache[stub_class]
|
|
315
|
+
|
|
316
|
+
def _httplib_patches(self) -> Iterator[mock._patch]:
|
|
317
|
+
"""
|
|
318
|
+
Patches for http.client (stdlib).
|
|
319
|
+
|
|
320
|
+
This covers most HTTP libraries:
|
|
321
|
+
- requests (uses urllib3 which uses http.client)
|
|
322
|
+
- urllib3 (uses http.client)
|
|
323
|
+
- httplib2 (uses http.client)
|
|
324
|
+
- boto3/botocore (uses urllib3)
|
|
325
|
+
"""
|
|
326
|
+
import http.client as httplib
|
|
327
|
+
from ._stubs._connection import MockHTTPConnection, MockHTTPSConnection
|
|
328
|
+
|
|
329
|
+
yield mock.patch.object(
|
|
330
|
+
httplib, "HTTPConnection",
|
|
331
|
+
self._get_stub_with_context(MockHTTPConnection)
|
|
332
|
+
)
|
|
333
|
+
yield mock.patch.object(
|
|
334
|
+
httplib, "HTTPSConnection",
|
|
335
|
+
self._get_stub_with_context(MockHTTPSConnection)
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
def _urllib3_patches(self) -> Iterator[mock._patch]:
|
|
339
|
+
"""
|
|
340
|
+
Patches for urllib3 connection classes.
|
|
341
|
+
|
|
342
|
+
urllib3 imports HTTPConnection at module load time, so we need
|
|
343
|
+
to patch its own connection module in addition to http.client.
|
|
344
|
+
|
|
345
|
+
This also covers requests, which uses urllib3 under the hood.
|
|
346
|
+
"""
|
|
347
|
+
from ._stubs._connection import MockHTTPConnection, MockHTTPSConnection
|
|
348
|
+
|
|
349
|
+
mock_http = self._get_stub_with_context(MockHTTPConnection)
|
|
350
|
+
mock_https = self._get_stub_with_context(MockHTTPSConnection)
|
|
351
|
+
|
|
352
|
+
# Patch urllib3.connection
|
|
353
|
+
try:
|
|
354
|
+
import urllib3.connection
|
|
355
|
+
yield mock.patch.object(
|
|
356
|
+
urllib3.connection, "HTTPConnection", mock_http
|
|
357
|
+
)
|
|
358
|
+
yield mock.patch.object(
|
|
359
|
+
urllib3.connection, "HTTPSConnection", mock_https
|
|
360
|
+
)
|
|
361
|
+
except ImportError:
|
|
362
|
+
pass
|
|
363
|
+
|
|
364
|
+
# Patch urllib3 connection pool ConnectionCls
|
|
365
|
+
try:
|
|
366
|
+
from urllib3.connectionpool import HTTPConnectionPool, HTTPSConnectionPool
|
|
367
|
+
yield mock.patch.object(
|
|
368
|
+
HTTPConnectionPool, "ConnectionCls", mock_http
|
|
369
|
+
)
|
|
370
|
+
yield mock.patch.object(
|
|
371
|
+
HTTPSConnectionPool, "ConnectionCls", mock_https
|
|
372
|
+
)
|
|
373
|
+
except ImportError:
|
|
374
|
+
pass
|
|
375
|
+
|
|
376
|
+
# Patch requests.packages.urllib3.connection (bundled urllib3)
|
|
377
|
+
try:
|
|
378
|
+
import requests.packages.urllib3.connection as req_urllib3_conn
|
|
379
|
+
yield mock.patch.object(
|
|
380
|
+
req_urllib3_conn, "HTTPConnection", mock_http
|
|
381
|
+
)
|
|
382
|
+
yield mock.patch.object(
|
|
383
|
+
req_urllib3_conn, "HTTPSConnection", mock_https
|
|
384
|
+
)
|
|
385
|
+
except ImportError:
|
|
386
|
+
pass
|
|
387
|
+
|
|
388
|
+
# Patch requests.packages.urllib3 connection pool ConnectionCls
|
|
389
|
+
try:
|
|
390
|
+
from requests.packages.urllib3.connectionpool import (
|
|
391
|
+
HTTPConnectionPool as ReqHTTPConnectionPool,
|
|
392
|
+
HTTPSConnectionPool as ReqHTTPSConnectionPool,
|
|
393
|
+
)
|
|
394
|
+
yield mock.patch.object(
|
|
395
|
+
ReqHTTPConnectionPool, "ConnectionCls", mock_http
|
|
396
|
+
)
|
|
397
|
+
yield mock.patch.object(
|
|
398
|
+
ReqHTTPSConnectionPool, "ConnectionCls", mock_https
|
|
399
|
+
)
|
|
400
|
+
except ImportError:
|
|
401
|
+
pass
|
|
402
|
+
|
|
403
|
+
def _httplib2_patches(self) -> Iterator[mock._patch]:
|
|
404
|
+
"""
|
|
405
|
+
Patches for httplib2 connection classes.
|
|
406
|
+
|
|
407
|
+
httplib2 uses SCHEME_TO_CONNECTION dict to look up connection classes.
|
|
408
|
+
We patch this dict to use our mock classes.
|
|
409
|
+
"""
|
|
410
|
+
from ._stubs._connection import MockHTTPConnection, MockHTTPSConnection
|
|
411
|
+
|
|
412
|
+
mock_http = self._get_stub_with_context(MockHTTPConnection)
|
|
413
|
+
mock_https = self._get_stub_with_context(MockHTTPSConnection)
|
|
414
|
+
|
|
415
|
+
try:
|
|
416
|
+
import httplib2
|
|
417
|
+
# Patch the scheme-to-connection mapping
|
|
418
|
+
yield mock.patch.dict(
|
|
419
|
+
httplib2.SCHEME_TO_CONNECTION,
|
|
420
|
+
{"http": mock_http, "https": mock_https}
|
|
421
|
+
)
|
|
422
|
+
except ImportError:
|
|
423
|
+
pass
|
|
424
|
+
|
|
425
|
+
def _botocore_patches(self) -> Iterator[mock._patch]:
|
|
426
|
+
"""
|
|
427
|
+
Patches for botocore (boto3) connection classes.
|
|
428
|
+
|
|
429
|
+
botocore has its own connection pool classes that use
|
|
430
|
+
AWSHTTPConnection and AWSHTTPSConnection.
|
|
431
|
+
"""
|
|
432
|
+
from ._stubs._connection import MockHTTPConnection, MockHTTPSConnection
|
|
433
|
+
|
|
434
|
+
mock_http = self._get_stub_with_context(MockHTTPConnection)
|
|
435
|
+
mock_https = self._get_stub_with_context(MockHTTPSConnection)
|
|
436
|
+
|
|
437
|
+
try:
|
|
438
|
+
import botocore.awsrequest
|
|
439
|
+
yield mock.patch.object(
|
|
440
|
+
botocore.awsrequest.AWSHTTPConnectionPool, "ConnectionCls", mock_http
|
|
441
|
+
)
|
|
442
|
+
yield mock.patch.object(
|
|
443
|
+
botocore.awsrequest.AWSHTTPSConnectionPool, "ConnectionCls", mock_https
|
|
444
|
+
)
|
|
445
|
+
except ImportError:
|
|
446
|
+
pass
|
|
447
|
+
|
|
448
|
+
def _httpcore_patches(self) -> Iterator[mock._patch]:
|
|
449
|
+
"""
|
|
450
|
+
Patches for httpcore.
|
|
451
|
+
|
|
452
|
+
This covers:
|
|
453
|
+
- httpx (sync and async)
|
|
454
|
+
- FastAPI TestClient (uses httpx internally)
|
|
455
|
+
"""
|
|
456
|
+
try:
|
|
457
|
+
import httpcore
|
|
458
|
+
except ImportError:
|
|
459
|
+
return
|
|
460
|
+
|
|
461
|
+
from ._stubs._httpcore import make_sync_handler, make_async_handler
|
|
462
|
+
|
|
463
|
+
yield mock.patch.object(
|
|
464
|
+
httpcore.ConnectionPool, "handle_request",
|
|
465
|
+
make_sync_handler(self._mock_context)
|
|
466
|
+
)
|
|
467
|
+
yield mock.patch.object(
|
|
468
|
+
httpcore.AsyncConnectionPool, "handle_async_request",
|
|
469
|
+
make_async_handler(self._mock_context)
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
def _aiohttp_patches(self) -> Iterator[mock._patch]:
|
|
473
|
+
"""
|
|
474
|
+
Patches for aiohttp.
|
|
475
|
+
|
|
476
|
+
aiohttp is an async HTTP client that doesn't use http.client.
|
|
477
|
+
"""
|
|
478
|
+
try:
|
|
479
|
+
import aiohttp.client
|
|
480
|
+
except ImportError:
|
|
481
|
+
return
|
|
482
|
+
|
|
483
|
+
from ._stubs._aiohttp import make_aiohttp_request_handler
|
|
484
|
+
|
|
485
|
+
yield mock.patch.object(
|
|
486
|
+
aiohttp.client.ClientSession, "_request",
|
|
487
|
+
make_aiohttp_request_handler(self._mock_context)
|
|
488
|
+
)
|
|
489
|
+
|
|
490
|
+
def _tornado_patches(self) -> Iterator[mock._patch]:
|
|
491
|
+
"""
|
|
492
|
+
Patches for tornado HTTP clients.
|
|
493
|
+
|
|
494
|
+
Tornado has multiple HTTP client implementations.
|
|
495
|
+
"""
|
|
496
|
+
from ._stubs._tornado import make_tornado_fetch_handler
|
|
497
|
+
|
|
498
|
+
# SimpleAsyncHTTPClient
|
|
499
|
+
try:
|
|
500
|
+
import tornado.simple_httpclient
|
|
501
|
+
original = _originals.get(
|
|
502
|
+
"tornado.SimpleAsyncHTTPClient.fetch_impl",
|
|
503
|
+
tornado.simple_httpclient.SimpleAsyncHTTPClient.fetch_impl
|
|
504
|
+
)
|
|
505
|
+
yield mock.patch.object(
|
|
506
|
+
tornado.simple_httpclient.SimpleAsyncHTTPClient, "fetch_impl",
|
|
507
|
+
make_tornado_fetch_handler(self._mock_context, original)
|
|
508
|
+
)
|
|
509
|
+
except ImportError:
|
|
510
|
+
pass
|
|
511
|
+
|
|
512
|
+
# CurlAsyncHTTPClient
|
|
513
|
+
try:
|
|
514
|
+
import tornado.curl_httpclient
|
|
515
|
+
original = _originals.get(
|
|
516
|
+
"tornado.CurlAsyncHTTPClient.fetch_impl",
|
|
517
|
+
tornado.curl_httpclient.CurlAsyncHTTPClient.fetch_impl
|
|
518
|
+
)
|
|
519
|
+
yield mock.patch.object(
|
|
520
|
+
tornado.curl_httpclient.CurlAsyncHTTPClient, "fetch_impl",
|
|
521
|
+
make_tornado_fetch_handler(self._mock_context, original)
|
|
522
|
+
)
|
|
523
|
+
except ImportError:
|
|
524
|
+
pass
|