web3 7.0.0b2__py3-none-any.whl → 7.7.0__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.
Files changed (144) hide show
  1. ens/__init__.py +13 -2
  2. ens/_normalization.py +4 -4
  3. ens/async_ens.py +27 -15
  4. ens/base_ens.py +3 -1
  5. ens/contract_data.py +2 -2
  6. ens/ens.py +10 -7
  7. ens/exceptions.py +16 -29
  8. ens/specs/nf.json +1 -1
  9. ens/specs/normalization_spec.json +1 -1
  10. ens/utils.py +24 -32
  11. web3/__init__.py +23 -12
  12. web3/_utils/abi.py +157 -263
  13. web3/_utils/async_transactions.py +34 -20
  14. web3/_utils/batching.py +217 -0
  15. web3/_utils/blocks.py +6 -2
  16. web3/_utils/caching/__init__.py +12 -0
  17. web3/_utils/caching/caching_utils.py +433 -0
  18. web3/_utils/caching/request_caching_validation.py +287 -0
  19. web3/_utils/compat/__init__.py +2 -3
  20. web3/_utils/contract_sources/compile_contracts.py +1 -1
  21. web3/_utils/contract_sources/contract_data/ambiguous_function_contract.py +42 -0
  22. web3/_utils/contract_sources/contract_data/arrays_contract.py +3 -3
  23. web3/_utils/contract_sources/contract_data/bytes_contracts.py +5 -5
  24. web3/_utils/contract_sources/contract_data/constructor_contracts.py +7 -7
  25. web3/_utils/contract_sources/contract_data/contract_caller_tester.py +3 -3
  26. web3/_utils/contract_sources/contract_data/emitter_contract.py +3 -3
  27. web3/_utils/contract_sources/contract_data/event_contracts.py +50 -5
  28. web3/_utils/contract_sources/contract_data/extended_resolver.py +3 -3
  29. web3/_utils/contract_sources/contract_data/fallback_function_contract.py +3 -3
  30. web3/_utils/contract_sources/contract_data/function_name_tester_contract.py +3 -3
  31. web3/_utils/contract_sources/contract_data/math_contract.py +3 -3
  32. web3/_utils/contract_sources/contract_data/offchain_lookup.py +3 -3
  33. web3/_utils/contract_sources/contract_data/offchain_resolver.py +3 -3
  34. web3/_utils/contract_sources/contract_data/panic_errors_contract.py +3 -3
  35. web3/_utils/contract_sources/contract_data/payable_tester.py +3 -3
  36. web3/_utils/contract_sources/contract_data/receive_function_contracts.py +5 -5
  37. web3/_utils/contract_sources/contract_data/reflector_contracts.py +3 -3
  38. web3/_utils/contract_sources/contract_data/revert_contract.py +3 -3
  39. web3/_utils/contract_sources/contract_data/simple_resolver.py +3 -3
  40. web3/_utils/contract_sources/contract_data/storage_contract.py +3 -3
  41. web3/_utils/contract_sources/contract_data/string_contract.py +3 -3
  42. web3/_utils/contract_sources/contract_data/tuple_contracts.py +5 -5
  43. web3/_utils/contracts.py +172 -220
  44. web3/_utils/datatypes.py +5 -1
  45. web3/_utils/decorators.py +6 -1
  46. web3/_utils/empty.py +1 -1
  47. web3/_utils/encoding.py +16 -12
  48. web3/_utils/error_formatters_utils.py +5 -3
  49. web3/_utils/events.py +78 -72
  50. web3/_utils/fee_utils.py +1 -3
  51. web3/_utils/filters.py +24 -22
  52. web3/_utils/formatters.py +2 -2
  53. web3/_utils/http.py +8 -2
  54. web3/_utils/http_session_manager.py +314 -0
  55. web3/_utils/math.py +14 -15
  56. web3/_utils/method_formatters.py +161 -34
  57. web3/_utils/module.py +2 -1
  58. web3/_utils/module_testing/__init__.py +3 -2
  59. web3/_utils/module_testing/eth_module.py +736 -583
  60. web3/_utils/module_testing/go_ethereum_debug_module.py +128 -0
  61. web3/_utils/module_testing/module_testing_utils.py +81 -24
  62. web3/_utils/module_testing/persistent_connection_provider.py +702 -220
  63. web3/_utils/module_testing/utils.py +114 -33
  64. web3/_utils/module_testing/web3_module.py +438 -17
  65. web3/_utils/normalizers.py +13 -11
  66. web3/_utils/rpc_abi.py +10 -22
  67. web3/_utils/threads.py +8 -7
  68. web3/_utils/transactions.py +32 -25
  69. web3/_utils/type_conversion.py +5 -1
  70. web3/_utils/validation.py +20 -17
  71. web3/beacon/__init__.py +5 -0
  72. web3/beacon/api_endpoints.py +3 -0
  73. web3/beacon/async_beacon.py +29 -6
  74. web3/beacon/beacon.py +24 -6
  75. web3/contract/__init__.py +7 -0
  76. web3/contract/async_contract.py +285 -82
  77. web3/contract/base_contract.py +556 -258
  78. web3/contract/contract.py +295 -84
  79. web3/contract/utils.py +251 -55
  80. web3/datastructures.py +49 -34
  81. web3/eth/__init__.py +7 -0
  82. web3/eth/async_eth.py +89 -69
  83. web3/eth/base_eth.py +7 -3
  84. web3/eth/eth.py +43 -66
  85. web3/exceptions.py +158 -83
  86. web3/gas_strategies/time_based.py +8 -6
  87. web3/geth.py +53 -184
  88. web3/main.py +77 -17
  89. web3/manager.py +362 -95
  90. web3/method.py +43 -15
  91. web3/middleware/__init__.py +17 -0
  92. web3/middleware/attrdict.py +12 -22
  93. web3/middleware/base.py +55 -2
  94. web3/middleware/filter.py +45 -23
  95. web3/middleware/formatting.py +6 -3
  96. web3/middleware/names.py +4 -1
  97. web3/middleware/signing.py +15 -6
  98. web3/middleware/stalecheck.py +2 -1
  99. web3/module.py +61 -25
  100. web3/providers/__init__.py +21 -0
  101. web3/providers/async_base.py +87 -32
  102. web3/providers/base.py +77 -32
  103. web3/providers/eth_tester/__init__.py +5 -0
  104. web3/providers/eth_tester/defaults.py +2 -55
  105. web3/providers/eth_tester/main.py +41 -15
  106. web3/providers/eth_tester/middleware.py +16 -17
  107. web3/providers/ipc.py +41 -17
  108. web3/providers/legacy_websocket.py +26 -1
  109. web3/providers/persistent/__init__.py +7 -0
  110. web3/providers/persistent/async_ipc.py +61 -121
  111. web3/providers/persistent/persistent.py +323 -16
  112. web3/providers/persistent/persistent_connection.py +54 -5
  113. web3/providers/persistent/request_processor.py +136 -56
  114. web3/providers/persistent/subscription_container.py +56 -0
  115. web3/providers/persistent/subscription_manager.py +233 -0
  116. web3/providers/persistent/websocket.py +29 -92
  117. web3/providers/rpc/__init__.py +5 -0
  118. web3/providers/rpc/async_rpc.py +73 -18
  119. web3/providers/rpc/rpc.py +73 -30
  120. web3/providers/rpc/utils.py +1 -13
  121. web3/scripts/install_pre_releases.py +33 -0
  122. web3/scripts/parse_pygeth_version.py +16 -0
  123. web3/testing.py +4 -4
  124. web3/tracing.py +9 -5
  125. web3/types.py +141 -74
  126. web3/utils/__init__.py +64 -5
  127. web3/utils/abi.py +790 -10
  128. web3/utils/address.py +8 -0
  129. web3/utils/async_exception_handling.py +20 -11
  130. web3/utils/caching.py +34 -4
  131. web3/utils/exception_handling.py +9 -12
  132. web3/utils/subscriptions.py +285 -0
  133. {web3-7.0.0b2.dist-info → web3-7.7.0.dist-info}/LICENSE +1 -1
  134. web3-7.7.0.dist-info/METADATA +130 -0
  135. web3-7.7.0.dist-info/RECORD +171 -0
  136. {web3-7.0.0b2.dist-info → web3-7.7.0.dist-info}/WHEEL +1 -1
  137. web3/_utils/caching.py +0 -155
  138. web3/_utils/contract_sources/contract_data/address_reflector.py +0 -29
  139. web3/_utils/module_testing/go_ethereum_personal_module.py +0 -300
  140. web3/_utils/request.py +0 -265
  141. web3-7.0.0b2.dist-info/METADATA +0 -106
  142. web3-7.0.0b2.dist-info/RECORD +0 -163
  143. /web3/_utils/{function_identifiers.py → abi_element_identifiers.py} +0 -0
  144. {web3-7.0.0b2.dist-info → web3-7.7.0.dist-info}/top_level.txt +0 -0
@@ -22,13 +22,12 @@ from websockets.client import (
22
22
  connect,
23
23
  )
24
24
  from websockets.exceptions import (
25
+ ConnectionClosedOK,
25
26
  WebSocketException,
26
27
  )
27
28
 
28
- from web3._utils.caching import (
29
- async_handle_request_caching,
30
- )
31
29
  from web3.exceptions import (
30
+ PersistentConnectionClosedOK,
32
31
  ProviderConnectionError,
33
32
  Web3ValidationError,
34
33
  )
@@ -36,7 +35,6 @@ from web3.providers.persistent import (
36
35
  PersistentConnectionProvider,
37
36
  )
38
37
  from web3.types import (
39
- RPCEndpoint,
40
38
  RPCResponse,
41
39
  )
42
40
 
@@ -61,9 +59,6 @@ class WebSocketProvider(PersistentConnectionProvider):
61
59
  logger = logging.getLogger("web3.providers.WebSocketProvider")
62
60
  is_async: bool = True
63
61
 
64
- _max_connection_retries: int = 5
65
- _ws: Optional[WebSocketClientProtocol] = None
66
-
67
62
  def __init__(
68
63
  self,
69
64
  endpoint_uri: Optional[Union[URI, str]] = None,
@@ -71,9 +66,12 @@ class WebSocketProvider(PersistentConnectionProvider):
71
66
  # `PersistentConnectionProvider` kwargs can be passed through
72
67
  **kwargs: Any,
73
68
  ) -> None:
74
- self.endpoint_uri = URI(endpoint_uri)
75
- if self.endpoint_uri is None:
76
- self.endpoint_uri = get_default_endpoint()
69
+ # initialize the endpoint_uri before calling the super constructor
70
+ self.endpoint_uri = (
71
+ URI(endpoint_uri) if endpoint_uri is not None else get_default_endpoint()
72
+ )
73
+ super().__init__(**kwargs)
74
+ self._ws: Optional[WebSocketClientProtocol] = None
77
75
 
78
76
  if not any(
79
77
  self.endpoint_uri.startswith(prefix)
@@ -96,8 +94,6 @@ class WebSocketProvider(PersistentConnectionProvider):
96
94
 
97
95
  self.websocket_kwargs = merge(DEFAULT_WEBSOCKET_KWARGS, websocket_kwargs or {})
98
96
 
99
- super().__init__(**kwargs)
100
-
101
97
  def __str__(self) -> str:
102
98
  return f"WebSocket connection: {self.endpoint_uri}"
103
99
 
@@ -116,52 +112,7 @@ class WebSocketProvider(PersistentConnectionProvider):
116
112
  ) from e
117
113
  return False
118
114
 
119
- async def connect(self) -> None:
120
- _connection_attempts = 0
121
- _backoff_rate_change = 1.75
122
- _backoff_time = 1.75
123
-
124
- while _connection_attempts != self._max_connection_retries:
125
- try:
126
- _connection_attempts += 1
127
- self._ws = await connect(self.endpoint_uri, **self.websocket_kwargs)
128
- self._message_listener_task = asyncio.create_task(
129
- self._message_listener()
130
- )
131
- break
132
- except WebSocketException as e:
133
- if _connection_attempts == self._max_connection_retries:
134
- raise ProviderConnectionError(
135
- f"Could not connect to endpoint: {self.endpoint_uri}. "
136
- f"Retries exceeded max of {self._max_connection_retries}."
137
- ) from e
138
- self.logger.info(
139
- f"Could not connect to endpoint: {self.endpoint_uri}. Retrying in "
140
- f"{round(_backoff_time, 1)} seconds.",
141
- exc_info=True,
142
- )
143
- await asyncio.sleep(_backoff_time)
144
- _backoff_time *= _backoff_rate_change
145
-
146
- async def disconnect(self) -> None:
147
- if self._ws is not None and not self._ws.closed:
148
- await self._ws.close()
149
- self._ws = None
150
- self.logger.debug(
151
- f'Successfully disconnected from endpoint: "{self.endpoint_uri}'
152
- )
153
-
154
- try:
155
- self._message_listener_task.cancel()
156
- await self._message_listener_task
157
- except (asyncio.CancelledError, StopAsyncIteration):
158
- pass
159
- self._request_processor.clear_caches()
160
-
161
- @async_handle_request_caching
162
- async def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
163
- request_data = self.encode_rpc_request(method, params)
164
-
115
+ async def socket_send(self, request_data: bytes) -> None:
165
116
  if self._ws is None:
166
117
  raise ProviderConnectionError(
167
118
  "Connection to websocket has not been initiated for the provider."
@@ -171,39 +122,25 @@ class WebSocketProvider(PersistentConnectionProvider):
171
122
  self._ws.send(request_data), timeout=self.request_timeout
172
123
  )
173
124
 
174
- current_request_id = json.loads(request_data)["id"]
175
- response = await self._get_response_for_request_id(current_request_id)
125
+ async def socket_recv(self) -> RPCResponse:
126
+ raw_response = await self._ws.recv()
127
+ return json.loads(raw_response)
176
128
 
177
- return response
129
+ # -- private methods -- #
178
130
 
179
- async def _message_listener(self) -> None:
180
- self.logger.info(
181
- "WebSocket listener background task started. Storing all messages in "
182
- "appropriate request processor queues / caches to be processed."
183
- )
184
- while True:
185
- # the use of sleep(0) seems to be the most efficient way to yield control
186
- # back to the event loop to share the loop with other tasks.
187
- await asyncio.sleep(0)
188
-
189
- try:
190
- async for raw_message in self._ws:
191
- await asyncio.sleep(0)
192
-
193
- response = json.loads(raw_message)
194
- subscription = response.get("method") == "eth_subscription"
195
- await self._request_processor.cache_raw_response(
196
- response, subscription=subscription
197
- )
198
- except Exception as e:
199
- if not self.silence_listener_task_exceptions:
200
- loop = asyncio.get_event_loop()
201
- for task in asyncio.all_tasks(loop=loop):
202
- task.cancel()
203
- raise e
204
-
205
- self.logger.error(
206
- "Exception caught in listener, error logging and keeping "
207
- "listener background task alive."
208
- f"\n error={e.__class__.__name__}: {e}"
209
- )
131
+ async def _provider_specific_connect(self) -> None:
132
+ self._ws = await connect(self.endpoint_uri, **self.websocket_kwargs)
133
+
134
+ async def _provider_specific_disconnect(self) -> None:
135
+ # this should remain idempotent
136
+ if self._ws is not None and not self._ws.closed:
137
+ await self._ws.close()
138
+ self._ws = None
139
+
140
+ async def _provider_specific_socket_reader(self) -> RPCResponse:
141
+ try:
142
+ return await self.socket_recv()
143
+ except ConnectionClosedOK:
144
+ raise PersistentConnectionClosedOK(
145
+ user_message="WebSocket connection received `ConnectionClosedOK`."
146
+ )
@@ -4,3 +4,8 @@ from .async_rpc import (
4
4
  from .rpc import (
5
5
  HTTPProvider,
6
6
  )
7
+
8
+ __all__ = [
9
+ "AsyncHTTPProvider",
10
+ "HTTPProvider",
11
+ ]
@@ -4,9 +4,11 @@ from typing import (
4
4
  Any,
5
5
  Dict,
6
6
  Iterable,
7
+ List,
7
8
  Optional,
8
9
  Tuple,
9
10
  Union,
11
+ cast,
10
12
  )
11
13
 
12
14
  from aiohttp import (
@@ -17,25 +19,31 @@ from eth_typing import (
17
19
  URI,
18
20
  )
19
21
  from eth_utils import (
22
+ combomethod,
20
23
  to_dict,
21
24
  )
22
25
 
26
+ from web3._utils.empty import (
27
+ Empty,
28
+ empty,
29
+ )
23
30
  from web3._utils.http import (
24
31
  construct_user_agent,
25
32
  )
26
- from web3._utils.request import (
27
- async_cache_and_return_session as _async_cache_and_return_session,
28
- async_make_post_request,
29
- get_default_http_endpoint,
30
- )
31
33
  from web3.types import (
32
34
  RPCEndpoint,
33
35
  RPCResponse,
34
36
  )
35
37
 
38
+ from ..._utils.batching import (
39
+ sort_batch_response_by_response_ids,
40
+ )
36
41
  from ..._utils.caching import (
37
42
  async_handle_request_caching,
38
43
  )
44
+ from ..._utils.http_session_manager import (
45
+ HTTPSessionManager,
46
+ )
39
47
  from ..async_base import (
40
48
  AsyncJSONBaseProvider,
41
49
  )
@@ -55,36 +63,63 @@ class AsyncHTTPProvider(AsyncJSONBaseProvider):
55
63
  endpoint_uri: Optional[Union[URI, str]] = None,
56
64
  request_kwargs: Optional[Any] = None,
57
65
  exception_retry_configuration: Optional[
58
- ExceptionRetryConfiguration
59
- ] = ExceptionRetryConfiguration(errors=(ClientError, TimeoutError)),
66
+ Union[ExceptionRetryConfiguration, Empty]
67
+ ] = empty,
68
+ **kwargs: Any,
60
69
  ) -> None:
70
+ self._request_session_manager = HTTPSessionManager()
71
+
61
72
  if endpoint_uri is None:
62
- self.endpoint_uri = get_default_http_endpoint()
73
+ self.endpoint_uri = (
74
+ self._request_session_manager.get_default_http_endpoint()
75
+ )
63
76
  else:
64
77
  self.endpoint_uri = URI(endpoint_uri)
65
78
 
66
79
  self._request_kwargs = request_kwargs or {}
67
- self.exception_retry_configuration = exception_retry_configuration
80
+ self._exception_retry_configuration = exception_retry_configuration
68
81
 
69
- super().__init__()
82
+ super().__init__(**kwargs)
70
83
 
71
84
  async def cache_async_session(self, session: ClientSession) -> ClientSession:
72
- return await _async_cache_and_return_session(self.endpoint_uri, session)
85
+ return await self._request_session_manager.async_cache_and_return_session(
86
+ self.endpoint_uri, session
87
+ )
73
88
 
74
89
  def __str__(self) -> str:
75
90
  return f"RPC connection {self.endpoint_uri}"
76
91
 
92
+ @property
93
+ def exception_retry_configuration(self) -> ExceptionRetryConfiguration:
94
+ if isinstance(self._exception_retry_configuration, Empty):
95
+ self._exception_retry_configuration = ExceptionRetryConfiguration(
96
+ errors=(ClientError, TimeoutError)
97
+ )
98
+ return self._exception_retry_configuration
99
+
100
+ @exception_retry_configuration.setter
101
+ def exception_retry_configuration(
102
+ self, value: Union[ExceptionRetryConfiguration, Empty]
103
+ ) -> None:
104
+ self._exception_retry_configuration = value
105
+
77
106
  @to_dict
78
107
  def get_request_kwargs(self) -> Iterable[Tuple[str, Any]]:
79
108
  if "headers" not in self._request_kwargs:
80
109
  yield "headers", self.get_request_headers()
81
- for key, value in self._request_kwargs.items():
82
- yield key, value
110
+ yield from self._request_kwargs.items()
111
+
112
+ @combomethod
113
+ def get_request_headers(cls) -> Dict[str, str]:
114
+ if isinstance(cls, AsyncHTTPProvider):
115
+ cls_name = cls.__class__.__name__
116
+ else:
117
+ cls_name = cls.__name__
118
+ module = cls.__module__
83
119
 
84
- def get_request_headers(self) -> Dict[str, str]:
85
120
  return {
86
121
  "Content-Type": "application/json",
87
- "User-Agent": construct_user_agent(type(self)),
122
+ "User-Agent": construct_user_agent(module, cls_name),
88
123
  }
89
124
 
90
125
  async def _make_request(self, method: RPCEndpoint, request_data: bytes) -> bytes:
@@ -100,20 +135,20 @@ class AsyncHTTPProvider(AsyncJSONBaseProvider):
100
135
  ):
101
136
  for i in range(self.exception_retry_configuration.retries):
102
137
  try:
103
- return await async_make_post_request(
138
+ return await self._request_session_manager.async_make_post_request(
104
139
  self.endpoint_uri, request_data, **self.get_request_kwargs()
105
140
  )
106
141
  except tuple(self.exception_retry_configuration.errors):
107
142
  if i < self.exception_retry_configuration.retries - 1:
108
143
  await asyncio.sleep(
109
- self.exception_retry_configuration.backoff_factor
144
+ self.exception_retry_configuration.backoff_factor * 2**i
110
145
  )
111
146
  continue
112
147
  else:
113
148
  raise
114
149
  return None
115
150
  else:
116
- return await async_make_post_request(
151
+ return await self._request_session_manager.async_make_post_request(
117
152
  self.endpoint_uri, request_data, **self.get_request_kwargs()
118
153
  )
119
154
 
@@ -130,3 +165,23 @@ class AsyncHTTPProvider(AsyncJSONBaseProvider):
130
165
  f"Method: {method}, Response: {response}"
131
166
  )
132
167
  return response
168
+
169
+ async def make_batch_request(
170
+ self, batch_requests: List[Tuple[RPCEndpoint, Any]]
171
+ ) -> List[RPCResponse]:
172
+ self.logger.debug(f"Making batch request HTTP - uri: `{self.endpoint_uri}`")
173
+ request_data = self.encode_batch_rpc_request(batch_requests)
174
+ raw_response = await self._request_session_manager.async_make_post_request(
175
+ self.endpoint_uri, request_data, **self.get_request_kwargs()
176
+ )
177
+ self.logger.debug("Received batch response HTTP.")
178
+ responses_list = cast(List[RPCResponse], self.decode_rpc_response(raw_response))
179
+ return sort_batch_response_by_response_ids(responses_list)
180
+
181
+ async def disconnect(self) -> None:
182
+ cache = self._request_session_manager.session_cache
183
+ for _, session in cache.items():
184
+ await session.close()
185
+ cache.clear()
186
+
187
+ self.logger.info(f"Successfully disconnected from: {self.endpoint_uri}")
web3/providers/rpc/rpc.py CHANGED
@@ -5,35 +5,43 @@ from typing import (
5
5
  Any,
6
6
  Dict,
7
7
  Iterable,
8
+ List,
8
9
  Optional,
9
10
  Tuple,
10
11
  Union,
12
+ cast,
11
13
  )
12
14
 
13
15
  from eth_typing import (
14
16
  URI,
15
17
  )
16
18
  from eth_utils import (
19
+ combomethod,
17
20
  to_dict,
18
21
  )
19
22
  import requests
20
23
 
24
+ from web3._utils.empty import (
25
+ Empty,
26
+ empty,
27
+ )
21
28
  from web3._utils.http import (
22
29
  construct_user_agent,
23
30
  )
24
- from web3._utils.request import (
25
- cache_and_return_session,
26
- get_default_http_endpoint,
27
- make_post_request,
28
- )
29
31
  from web3.types import (
30
32
  RPCEndpoint,
31
33
  RPCResponse,
32
34
  )
33
35
 
36
+ from ..._utils.batching import (
37
+ sort_batch_response_by_response_ids,
38
+ )
34
39
  from ..._utils.caching import (
35
40
  handle_request_caching,
36
41
  )
42
+ from ..._utils.http_session_manager import (
43
+ HTTPSessionManager,
44
+ )
37
45
  from ..base import (
38
46
  JSONBaseProvider,
39
47
  )
@@ -51,54 +59,75 @@ if TYPE_CHECKING:
51
59
  class HTTPProvider(JSONBaseProvider):
52
60
  logger = logging.getLogger("web3.providers.HTTPProvider")
53
61
  endpoint_uri = None
54
-
55
- _request_args = None
56
62
  _request_kwargs = None
57
63
 
58
- exception_retry_configuration: Optional[ExceptionRetryConfiguration] = None
59
-
60
64
  def __init__(
61
65
  self,
62
66
  endpoint_uri: Optional[Union[URI, str]] = None,
63
67
  request_kwargs: Optional[Any] = None,
64
68
  session: Optional[Any] = None,
65
- exception_retry_configuration: Optional[ExceptionRetryConfiguration] = (
66
- ExceptionRetryConfiguration(
67
- errors=(
68
- ConnectionError,
69
- requests.HTTPError,
70
- requests.Timeout,
71
- )
72
- )
73
- ),
69
+ exception_retry_configuration: Optional[
70
+ Union[ExceptionRetryConfiguration, Empty]
71
+ ] = empty,
72
+ **kwargs: Any,
74
73
  ) -> None:
74
+ super().__init__(**kwargs)
75
+ self._request_session_manager = HTTPSessionManager()
76
+
75
77
  if endpoint_uri is None:
76
- self.endpoint_uri = get_default_http_endpoint()
78
+ self.endpoint_uri = (
79
+ self._request_session_manager.get_default_http_endpoint()
80
+ )
77
81
  else:
78
82
  self.endpoint_uri = URI(endpoint_uri)
79
83
 
80
84
  self._request_kwargs = request_kwargs or {}
81
- self.exception_retry_configuration = exception_retry_configuration
85
+ self._exception_retry_configuration = exception_retry_configuration
82
86
 
83
87
  if session:
84
- cache_and_return_session(self.endpoint_uri, session)
85
-
86
- super().__init__()
88
+ self._request_session_manager.cache_and_return_session(
89
+ self.endpoint_uri, session
90
+ )
87
91
 
88
92
  def __str__(self) -> str:
89
93
  return f"RPC connection {self.endpoint_uri}"
90
94
 
95
+ @property
96
+ def exception_retry_configuration(self) -> ExceptionRetryConfiguration:
97
+ if isinstance(self._exception_retry_configuration, Empty):
98
+ self._exception_retry_configuration = ExceptionRetryConfiguration(
99
+ errors=(
100
+ ConnectionError,
101
+ requests.HTTPError,
102
+ requests.Timeout,
103
+ )
104
+ )
105
+ return self._exception_retry_configuration
106
+
107
+ @exception_retry_configuration.setter
108
+ def exception_retry_configuration(
109
+ self, value: Union[ExceptionRetryConfiguration, Empty]
110
+ ) -> None:
111
+ self._exception_retry_configuration = value
112
+
91
113
  @to_dict
92
114
  def get_request_kwargs(self) -> Iterable[Tuple[str, Any]]:
93
115
  if "headers" not in self._request_kwargs:
94
116
  yield "headers", self.get_request_headers()
95
- for key, value in self._request_kwargs.items():
96
- yield key, value
117
+ yield from self._request_kwargs.items()
118
+
119
+ @combomethod
120
+ def get_request_headers(cls) -> Dict[str, str]:
121
+ if isinstance(cls, HTTPProvider):
122
+ cls_name = cls.__class__.__name__
123
+ else:
124
+ cls_name = cls.__name__
125
+
126
+ module = cls.__module__
97
127
 
98
- def get_request_headers(self) -> Dict[str, str]:
99
128
  return {
100
129
  "Content-Type": "application/json",
101
- "User-Agent": construct_user_agent(type(self)),
130
+ "User-Agent": construct_user_agent(module, cls_name),
102
131
  }
103
132
 
104
133
  def _make_request(self, method: RPCEndpoint, request_data: bytes) -> bytes:
@@ -114,18 +143,20 @@ class HTTPProvider(JSONBaseProvider):
114
143
  ):
115
144
  for i in range(self.exception_retry_configuration.retries):
116
145
  try:
117
- return make_post_request(
146
+ return self._request_session_manager.make_post_request(
118
147
  self.endpoint_uri, request_data, **self.get_request_kwargs()
119
148
  )
120
149
  except tuple(self.exception_retry_configuration.errors) as e:
121
150
  if i < self.exception_retry_configuration.retries - 1:
122
- time.sleep(self.exception_retry_configuration.backoff_factor)
151
+ time.sleep(
152
+ self.exception_retry_configuration.backoff_factor * 2**i
153
+ )
123
154
  continue
124
155
  else:
125
156
  raise e
126
157
  return None
127
158
  else:
128
- return make_post_request(
159
+ return self._request_session_manager.make_post_request(
129
160
  self.endpoint_uri, request_data, **self.get_request_kwargs()
130
161
  )
131
162
 
@@ -142,3 +173,15 @@ class HTTPProvider(JSONBaseProvider):
142
173
  f"Method: {method}, Response: {response}"
143
174
  )
144
175
  return response
176
+
177
+ def make_batch_request(
178
+ self, batch_requests: List[Tuple[RPCEndpoint, Any]]
179
+ ) -> List[RPCResponse]:
180
+ self.logger.debug(f"Making batch request HTTP, uri: `{self.endpoint_uri}`")
181
+ request_data = self.encode_batch_rpc_request(batch_requests)
182
+ raw_response = self._request_session_manager.make_post_request(
183
+ self.endpoint_uri, request_data, **self.get_request_kwargs()
184
+ )
185
+ self.logger.debug("Received batch response HTTP.")
186
+ responses_list = cast(List[RPCResponse], self.decode_rpc_response(raw_response))
187
+ return sort_batch_response_by_response_ids(responses_list)
@@ -19,9 +19,6 @@ REQUEST_RETRY_ALLOWLIST = [
19
19
  "evm",
20
20
  "eth_protocolVersion",
21
21
  "eth_syncing",
22
- "eth_coinbase",
23
- "eth_mining",
24
- "eth_hashrate",
25
22
  "eth_chainId",
26
23
  "eth_gasPrice",
27
24
  "eth_accounts",
@@ -58,15 +55,6 @@ REQUEST_RETRY_ALLOWLIST = [
58
55
  "eth_sign",
59
56
  "eth_signTypedData",
60
57
  "eth_sendRawTransaction",
61
- "personal_importRawKey",
62
- "personal_newAccount",
63
- "personal_listAccounts",
64
- "personal_listWallets",
65
- "personal_lockAccount",
66
- "personal_unlockAccount",
67
- "personal_ecRecover",
68
- "personal_sign",
69
- "personal_signTypedData",
70
58
  ]
71
59
 
72
60
 
@@ -93,7 +81,7 @@ class ExceptionRetryConfiguration(BaseModel):
93
81
  self,
94
82
  errors: Sequence[Type[BaseException]] = None,
95
83
  retries: int = 5,
96
- backoff_factor: float = 0.5,
84
+ backoff_factor: float = 0.125,
97
85
  method_allowlist: Sequence[str] = None,
98
86
  ):
99
87
  super().__init__(
@@ -0,0 +1,33 @@
1
+ """
2
+ The goal of this script is to install the latest versions, including pre-releases, for
3
+ libraries that we maintain (and therefore control the release process) during our CI
4
+ runs. This helps us test pre-releases before they cause any issues once stable versions
5
+ are released.
6
+ """
7
+
8
+ import subprocess
9
+ import sys
10
+
11
+ ETHEREUM_LIBRARIES = [
12
+ "eth-account",
13
+ "eth-abi",
14
+ "eth-account",
15
+ "eth-hash[pycryptodome]",
16
+ "eth-typing",
17
+ "eth-utils",
18
+ "hexbytes",
19
+ "eth-tester[py-evm]",
20
+ "py-geth",
21
+ ]
22
+
23
+
24
+ def install_eth_pre_releases() -> None:
25
+ for lib in ETHEREUM_LIBRARIES:
26
+ print(f"Installing {lib} with `--pre` and `-U`")
27
+ subprocess.check_call(
28
+ [sys.executable, "-m", "pip", "install", "--pre", "-U", lib]
29
+ )
30
+
31
+
32
+ if __name__ == "__main__":
33
+ install_eth_pre_releases()
@@ -0,0 +1,16 @@
1
+ import re
2
+
3
+
4
+ def get_pygeth_version() -> str:
5
+ with open("setup.py") as f:
6
+ setup_contents = f.read()
7
+ version_match = re.search(r"py-geth\s*([><=~!]+)\s*([\d.]+)", setup_contents)
8
+ if version_match:
9
+ return "".join(version_match.group(1, 2))
10
+ else:
11
+ raise ValueError("py-geth not found in setup.py")
12
+
13
+
14
+ if __name__ == "__main__":
15
+ version = get_pygeth_version()
16
+ print(version)
web3/testing.py CHANGED
@@ -12,21 +12,21 @@ from web3.module import (
12
12
 
13
13
  class Testing(Module):
14
14
  def timeTravel(self, timestamp: int) -> None:
15
- return self.w3.manager.request_blocking(RPC.testing_timeTravel, [timestamp])
15
+ self.w3.manager.request_blocking(RPC.testing_timeTravel, [timestamp])
16
16
 
17
17
  def mine(self, num_blocks: int = 1) -> None:
18
- return self.w3.manager.request_blocking(RPC.evm_mine, [num_blocks])
18
+ self.w3.manager.request_blocking(RPC.evm_mine, [num_blocks])
19
19
 
20
20
  def snapshot(self) -> int:
21
21
  self.last_snapshot_idx = self.w3.manager.request_blocking(RPC.evm_snapshot, [])
22
22
  return self.last_snapshot_idx
23
23
 
24
24
  def reset(self) -> None:
25
- return self.w3.manager.request_blocking(RPC.evm_reset, [])
25
+ self.w3.manager.request_blocking(RPC.evm_reset, [])
26
26
 
27
27
  def revert(self, snapshot_idx: Optional[int] = None) -> None:
28
28
  if snapshot_idx is None:
29
29
  revert_target = self.last_snapshot_idx
30
30
  else:
31
31
  revert_target = snapshot_idx
32
- return self.w3.manager.request_blocking(RPC.evm_revert, [revert_target])
32
+ self.w3.manager.request_blocking(RPC.evm_revert, [revert_target])
web3/tracing.py CHANGED
@@ -53,10 +53,10 @@ class Tracing(Module):
53
53
  self._default_block = value
54
54
 
55
55
  def trace_replay_transaction_munger(
56
- self,
57
- block_identifier: Union[_Hash32, BlockIdentifier],
58
- mode: TraceMode = ["trace"],
56
+ self, block_identifier: Union[_Hash32, BlockIdentifier], mode: TraceMode = None
59
57
  ) -> Tuple[Union[BlockIdentifier, _Hash32], TraceMode]:
58
+ if mode is None:
59
+ mode = ["trace"]
60
60
  return (block_identifier, mode)
61
61
 
62
62
  trace_replay_transaction: Method[Callable[..., BlockTrace]] = Method(
@@ -86,9 +86,11 @@ class Tracing(Module):
86
86
  def trace_call_munger(
87
87
  self,
88
88
  transaction: TxParams,
89
- mode: TraceMode = ["trace"],
89
+ mode: TraceMode = None,
90
90
  block_identifier: Optional[BlockIdentifier] = None,
91
91
  ) -> Tuple[TxParams, TraceMode, BlockIdentifier]:
92
+ if mode is None:
93
+ mode = ["trace"]
92
94
  if "from" not in transaction and is_checksum_address(
93
95
  self.w3.eth.default_account
94
96
  ):
@@ -105,8 +107,10 @@ class Tracing(Module):
105
107
  )
106
108
 
107
109
  def trace_transactions_munger(
108
- self, raw_transaction: HexStr, mode: TraceMode = ["trace"]
110
+ self, raw_transaction: HexStr, mode: TraceMode = None
109
111
  ) -> Tuple[HexStr, TraceMode]:
112
+ if mode is None:
113
+ mode = ["trace"]
110
114
  return raw_transaction, mode
111
115
 
112
116
  trace_raw_transaction: Method[Callable[..., BlockTrace]] = Method(