Skip to content

Commit 14d51d2

Browse files
committed
Strap a RequestSessionManager to each instance of http providers
1 parent 6f23635 commit 14d51d2

File tree

10 files changed

+340
-331
lines changed

10 files changed

+340
-331
lines changed

tests/core/utilities/test_request.py

Lines changed: 72 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,12 @@
2929
from web3._utils.caching import (
3030
generate_cache_key,
3131
)
32-
from web3._utils.request import (
33-
async_cache_and_return_session,
34-
cache_and_return_session,
35-
)
3632
from web3.utils.caching import (
3733
SimpleCache,
3834
)
3935

36+
request_session_manager = request.RequestSessionManager()
37+
4038

4139
class MockedResponse:
4240
def __init__(self, text="", status_code=200):
@@ -72,7 +70,7 @@ def check_adapters_mounted(session: Session):
7270

7371

7472
def _simulate_call(uri):
75-
_session = cache_and_return_session(uri)
73+
_session = request_session_manager.cache_and_return_session(uri)
7674

7775
# simulate a call taking 0.01s to return a response
7876
time.sleep(0.01)
@@ -81,24 +79,22 @@ def _simulate_call(uri):
8179

8280
@pytest.fixture(autouse=True)
8381
def setup_and_teardown():
84-
# clear session caches before and after each test
85-
request._session_cache.clear()
86-
request._async_session_cache.clear()
82+
# clear session cache before and after each test
83+
request_session_manager.session_cache.clear()
8784
yield
88-
request._session_cache.clear()
89-
request._async_session_cache.clear()
85+
request_session_manager.session_cache.clear()
9086

9187

9288
def test_json_make_get_request(mocker):
9389
mocker.patch("requests.Session.get", return_value=MockedResponse())
9490

9591
# Submit a first request to create a session with default parameters
96-
assert len(request._session_cache) == 0
97-
response = request.json_make_get_request(TEST_URI)
92+
assert len(request_session_manager.session_cache) == 0
93+
response = request_session_manager.json_make_get_request(TEST_URI)
9894
assert response == json.dumps({"data": "content"})
99-
assert len(request._session_cache) == 1
95+
assert len(request_session_manager.session_cache) == 1
10096
cache_key = generate_cache_key(f"{threading.get_ident()}:{TEST_URI}")
101-
session = request._session_cache.get_cache_entry(cache_key)
97+
session = request_session_manager.session_cache.get_cache_entry(cache_key)
10298
session.get.assert_called_once_with(TEST_URI, timeout=30)
10399

104100
# Ensure the adapter was created with default values
@@ -113,12 +109,12 @@ def test_make_post_request_no_args(mocker):
113109
mocker.patch("requests.Session.post", return_value=MockedResponse())
114110

115111
# Submit a first request to create a session with default parameters
116-
assert len(request._session_cache) == 0
117-
response = request.make_post_request(TEST_URI, data=b"request")
112+
assert len(request_session_manager.session_cache) == 0
113+
response = request_session_manager.make_post_request(TEST_URI, data=b"request")
118114
assert response == "content"
119-
assert len(request._session_cache) == 1
115+
assert len(request_session_manager.session_cache) == 1
120116
cache_key = generate_cache_key(f"{threading.get_ident()}:{TEST_URI}")
121-
session = request._session_cache.get_cache_entry(cache_key)
117+
session = request_session_manager.session_cache.get_cache_entry(cache_key)
122118
session.post.assert_called_once_with(TEST_URI, data=b"request", timeout=30)
123119

124120
# Ensure the adapter was created with default values
@@ -137,16 +133,18 @@ def test_precached_session(mocker):
137133
session = Session()
138134
session.mount("http://", adapter)
139135
session.mount("https://", adapter)
140-
request.cache_and_return_session(TEST_URI, session)
136+
request_session_manager.cache_and_return_session(TEST_URI, session)
141137

142138
# Submit a second request with different arguments
143-
assert len(request._session_cache) == 1
144-
response = request.make_post_request(TEST_URI, data=b"request", timeout=60)
139+
assert len(request_session_manager.session_cache) == 1
140+
response = request_session_manager.make_post_request(
141+
TEST_URI, data=b"request", timeout=60
142+
)
145143
assert response == "content"
146-
assert len(request._session_cache) == 1
144+
assert len(request_session_manager.session_cache) == 1
147145

148146
# Ensure the timeout was passed to the request
149-
session = request.cache_and_return_session(TEST_URI)
147+
session = request_session_manager.cache_and_return_session(TEST_URI)
150148
session.post.assert_called_once_with(TEST_URI, data=b"request", timeout=60)
151149

152150
# Ensure the adapter parameters match those we specified
@@ -187,19 +185,19 @@ def test_cache_session_class():
187185

188186
def test_cache_does_not_close_session_before_a_call_when_multithreading():
189187
# save default values
190-
session_cache_default = request._session_cache
188+
session_cache_default = request_session_manager.session_cache
191189
timeout_default = request.DEFAULT_TIMEOUT
192190

193191
# set cache size to 1 + set future session close thread time to 0.01s
194-
request._session_cache = SimpleCache(1)
192+
request_session_manager.session_cache = SimpleCache(1)
195193
_timeout_for_testing = 0.01
196194
request.DEFAULT_TIMEOUT = _timeout_for_testing
197195

198196
with ThreadPoolExecutor(max_workers=len(UNIQUE_URIS)) as exc:
199197
all_sessions = [exc.submit(_simulate_call, uri) for uri in UNIQUE_URIS]
200198

201199
# assert last session remains in cache, all others evicted
202-
cache_data = request._session_cache._data
200+
cache_data = request_session_manager.session_cache._data
203201
assert len(cache_data) == 1
204202
_key, cached_session = cache_data.popitem()
205203
assert cached_session == all_sessions[-1].result() # result of the `Future`
@@ -210,7 +208,7 @@ def test_cache_does_not_close_session_before_a_call_when_multithreading():
210208
cached_session.close()
211209

212210
# reset default values
213-
request._session_cache = session_cache_default
211+
request_session_manager.session_cache = session_cache_default
214212
request.DEFAULT_TIMEOUT = timeout_default
215213

216214

@@ -221,7 +219,7 @@ def test_unique_cache_keys_created_per_thread_with_same_uri():
221219
test_sessions = [exc.submit(_simulate_call, TEST_URI) for _ in range(2)]
222220

223221
# assert unique keys are generated per thread for the same uri
224-
assert len(request._session_cache._data) == 2
222+
assert len(request_session_manager.session_cache._data) == 2
225223

226224
# -- teardown -- #
227225

@@ -257,12 +255,12 @@ async def test_async_json_make_get_request(mocker):
257255
mocker.patch("aiohttp.ClientSession.get", return_value=AsyncMockedResponse())
258256

259257
# Submit a first request to create a session with default parameters
260-
assert len(request._async_session_cache) == 0
261-
response = await request.async_json_make_get_request(TEST_URI)
258+
assert len(request_session_manager.session_cache) == 0
259+
response = await request_session_manager.async_json_make_get_request(TEST_URI)
262260
assert response == json.dumps({"data": "content"})
263-
assert len(request._async_session_cache) == 1
261+
assert len(request_session_manager.session_cache) == 1
264262
cache_key = generate_cache_key(f"{threading.get_ident()}:{TEST_URI}")
265-
session = request._async_session_cache.get_cache_entry(cache_key)
263+
session = request_session_manager.session_cache.get_cache_entry(cache_key)
266264
assert isinstance(session, ClientSession)
267265
session.get.assert_called_once_with(
268266
TEST_URI,
@@ -278,12 +276,14 @@ async def test_async_make_post_request(mocker):
278276
mocker.patch("aiohttp.ClientSession.post", return_value=AsyncMockedResponse())
279277

280278
# Submit a first request to create a session with default parameters
281-
assert len(request._async_session_cache) == 0
282-
response = await request.async_make_post_request(TEST_URI, data=b"request")
279+
assert len(request_session_manager.session_cache) == 0
280+
response = await request_session_manager.async_make_post_request(
281+
TEST_URI, data=b"request"
282+
)
283283
assert response == "content"
284-
assert len(request._async_session_cache) == 1
284+
assert len(request_session_manager.session_cache) == 1
285285
cache_key = generate_cache_key(f"{threading.get_ident()}:{TEST_URI}")
286-
session = request._async_session_cache.get_cache_entry(cache_key)
286+
session = request_session_manager.session_cache.get_cache_entry(cache_key)
287287
assert isinstance(session, ClientSession)
288288
session.post.assert_called_once_with(
289289
TEST_URI,
@@ -299,36 +299,41 @@ async def test_async_make_post_request(mocker):
299299
async def test_async_precached_session():
300300
# Add a session
301301
session = ClientSession()
302-
await request.async_cache_and_return_session(TEST_URI, session)
303-
assert len(request._async_session_cache) == 1
302+
await request_session_manager.async_cache_and_return_session(TEST_URI, session)
303+
assert len(request_session_manager.session_cache) == 1
304304

305305
# Make sure the session isn't duplicated
306-
await request.async_cache_and_return_session(TEST_URI, session)
307-
assert len(request._async_session_cache) == 1
306+
await request_session_manager.async_cache_and_return_session(TEST_URI, session)
307+
assert len(request_session_manager.session_cache) == 1
308308

309309
# Make sure a request with a different URI adds another cached session
310-
await request.async_cache_and_return_session(URI(f"{TEST_URI}/test"), session)
311-
assert len(request._async_session_cache) == 2
310+
await request_session_manager.async_cache_and_return_session(
311+
URI(f"{TEST_URI}/test"), session
312+
)
313+
assert len(request_session_manager.session_cache) == 2
312314

313315
# -- teardown -- #
314316

315317
# appropriately close the cached sessions
316-
[await session.close() for session in request._async_session_cache._data.values()]
318+
[
319+
await session.close()
320+
for session in request_session_manager.session_cache._data.values()
321+
]
317322

318323

319324
@pytest.mark.asyncio
320325
async def test_async_cache_does_not_close_session_before_a_call_when_multithreading():
321326
# save default values
322-
session_cache_default = request._async_session_cache
327+
session_cache_default = request_session_manager.session_cache
323328
timeout_default = request.DEFAULT_TIMEOUT
324329

325330
# set cache size to 1 + set future session close thread time to 0.01s
326-
request._async_session_cache = SimpleCache(1)
331+
request_session_manager.session_cache = SimpleCache(1)
327332
_timeout_for_testing = 0.01
328333
request.DEFAULT_TIMEOUT = _timeout_for_testing
329334

330335
async def cache_uri_and_return_session(uri):
331-
_session = await async_cache_and_return_session(uri)
336+
_session = await request_session_manager.async_cache_and_return_session(uri)
332337

333338
# simulate a call taking 0.01s to return a response
334339
await asyncio.sleep(0.01)
@@ -343,7 +348,7 @@ async def cache_uri_and_return_session(uri):
343348
assert all(isinstance(s, ClientSession) for s in all_sessions)
344349

345350
# last session remains in cache, all others evicted
346-
cache_data = request._async_session_cache._data
351+
cache_data = request_session_manager.session_cache._data
347352
assert len(cache_data) == 1
348353
_key, cached_session = cache_data.popitem()
349354
assert cached_session == all_sessions[-1]
@@ -358,8 +363,8 @@ async def cache_uri_and_return_session(uri):
358363
await cached_session.close()
359364

360365
# reset default values
361-
request._async_session_cache = session_cache_default
362-
request.DEFAULT_TIMEOUT = timeout_default
366+
request_session_manager.session_cache = session_cache_default
367+
request_session_manager.DEFAULT_TIMEOUT = timeout_default
363368

364369

365370
@pytest.mark.asyncio
@@ -373,7 +378,7 @@ async def test_async_unique_cache_keys_created_per_thread_with_same_uri():
373378
def target_function(endpoint_uri):
374379
event_loop = asyncio.new_event_loop()
375380
unique_session = event_loop.run_until_complete(
376-
async_cache_and_return_session(endpoint_uri)
381+
request_session_manager.async_cache_and_return_session(endpoint_uri)
377382
)
378383
event_loop.close()
379384
test_sessions.append(unique_session)
@@ -393,7 +398,7 @@ def target_function(endpoint_uri):
393398

394399
# assert unique keys are generated per thread, w/ unique event loops,
395400
# for the same uri
396-
assert len(request._async_session_cache._data) == 2
401+
assert len(request_session_manager.session_cache._data) == 2
397402

398403
# -- teardown -- #
399404

@@ -409,29 +414,31 @@ async def test_async_use_new_session_if_loop_closed_for_cached_session():
409414
session1 = ClientSession(raise_for_status=True)
410415
session1._loop = loop1
411416

412-
await async_cache_and_return_session(TEST_URI, session=session1)
417+
await request_session_manager.async_cache_and_return_session(
418+
TEST_URI, session=session1
419+
)
413420

414421
# assert session1 was cached
415422
cache_key = generate_cache_key(f"{threading.get_ident()}:{TEST_URI}")
416423

417-
assert len(request._async_session_cache) == 1
418-
cached_session = request._async_session_cache.get_cache_entry(cache_key)
424+
assert len(request_session_manager.session_cache) == 1
425+
cached_session = request_session_manager.session_cache.get_cache_entry(cache_key)
419426
assert cached_session == session1
420427

421428
# close loop that was used with session1
422429
loop1.close()
423430

424431
# assert we create a new session when trying to retrieve the session at the
425432
# cache key for TEST_URI
426-
session2 = await async_cache_and_return_session(TEST_URI)
433+
session2 = await request_session_manager.async_cache_and_return_session(TEST_URI)
427434
assert not session2._loop.is_closed()
428435
assert session2 != session1
429436

430437
# assert we appropriately closed session1, evicted it from the cache, and cached
431438
# the new session2 at the cache key
432439
assert session1.closed
433-
assert len(request._async_session_cache) == 1
434-
cached_session = request._async_session_cache.get_cache_entry(cache_key)
440+
assert len(request_session_manager.session_cache) == 1
441+
cached_session = request_session_manager.session_cache.get_cache_entry(cache_key)
435442
assert cached_session == session2
436443

437444
# -- teardown -- #
@@ -445,24 +452,26 @@ async def test_async_use_new_session_if_session_closed_for_cached_session():
445452
# create a session, close it, and cache it at the cache key for TEST_URI
446453
session1 = ClientSession(raise_for_status=True)
447454
await session1.close()
448-
await async_cache_and_return_session(TEST_URI, session=session1)
455+
await request_session_manager.async_cache_and_return_session(
456+
TEST_URI, session=session1
457+
)
449458

450459
# assert session1 was cached
451460
cache_key = generate_cache_key(f"{threading.get_ident()}:{TEST_URI}")
452461

453-
assert len(request._async_session_cache) == 1
454-
cached_session = request._async_session_cache.get_cache_entry(cache_key)
462+
assert len(request_session_manager.session_cache) == 1
463+
cached_session = request_session_manager.session_cache.get_cache_entry(cache_key)
455464
assert cached_session == session1
456465

457466
# assert we create a new session when trying to retrieve closed session from cache
458-
session2 = await async_cache_and_return_session(TEST_URI)
467+
session2 = await request_session_manager.async_cache_and_return_session(TEST_URI)
459468
assert not session2.closed
460469
assert session2 != session1
461470

462471
# assert we evicted session1 from the cache, and cached the new session2
463472
# at the cache key
464-
assert len(request._async_session_cache) == 1
465-
cached_session = request._async_session_cache.get_cache_entry(cache_key)
473+
assert len(request_session_manager.session_cache) == 1
474+
cached_session = request_session_manager.session_cache.get_cache_entry(cache_key)
466475
assert cached_session == session2
467476

468477
# -- teardown -- #

web3/_utils/module_testing/module_testing_utils.py

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212
Union,
1313
)
1414

15-
from aiohttp import (
16-
ClientTimeout,
17-
)
15+
import aiohttp
1816
from eth_typing import (
1917
ChecksumAddress,
2018
HexStr,
@@ -28,11 +26,8 @@
2826
from hexbytes import (
2927
HexBytes,
3028
)
29+
import requests
3130

32-
from web3._utils.request import (
33-
async_cache_and_return_session,
34-
cache_and_return_session,
35-
)
3631
from web3.types import (
3732
BlockData,
3833
LogReceipt,
@@ -102,13 +97,12 @@ def _mock_specific_request(
10297

10398
# mock response only to specified url while validating appropriate fields
10499
if url_from_args == mocked_request_url:
105-
assert kwargs["timeout"] == 30
106100
if http_method.upper() == "POST":
107101
assert kwargs["data"] == {"data": calldata, "sender": sender}
108102
return MockedResponse()
109103

110104
# else, make a normal request (no mocking)
111-
session = cache_and_return_session(url_from_args)
105+
session = requests.Session()
112106
return session.request(method=http_method.upper(), url=url_from_args, **kwargs)
113107

114108
monkeypatch.setattr(
@@ -152,13 +146,12 @@ async def _mock_specific_request(
152146

153147
# mock response only to specified url while validating appropriate fields
154148
if url_from_args == mocked_request_url:
155-
assert kwargs["timeout"] == ClientTimeout(30)
156149
if http_method.upper() == "post":
157150
assert kwargs["data"] == {"data": calldata, "sender": sender}
158151
return AsyncMockedResponse()
159152

160153
# else, make a normal request (no mocking)
161-
session = await async_cache_and_return_session(url_from_args)
154+
session = aiohttp.ClientSession()
162155
return await session.request(
163156
method=http_method.upper(), url=url_from_args, **kwargs
164157
)

0 commit comments

Comments
 (0)