web3 7.6.1__py3-none-any.whl → 7.8.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 (46) hide show
  1. ens/async_ens.py +1 -1
  2. ens/ens.py +1 -1
  3. web3/_utils/caching/caching_utils.py +64 -0
  4. web3/_utils/caching/request_caching_validation.py +1 -0
  5. web3/_utils/events.py +1 -1
  6. web3/_utils/http_session_manager.py +32 -3
  7. web3/_utils/module_testing/eth_module.py +5 -18
  8. web3/_utils/module_testing/module_testing_utils.py +1 -43
  9. web3/_utils/module_testing/persistent_connection_provider.py +696 -207
  10. web3/_utils/module_testing/utils.py +99 -33
  11. web3/beacon/api_endpoints.py +10 -0
  12. web3/beacon/async_beacon.py +47 -0
  13. web3/beacon/beacon.py +45 -0
  14. web3/contract/async_contract.py +2 -206
  15. web3/contract/base_contract.py +217 -13
  16. web3/contract/contract.py +2 -205
  17. web3/datastructures.py +15 -16
  18. web3/eth/async_eth.py +23 -5
  19. web3/exceptions.py +7 -0
  20. web3/main.py +24 -3
  21. web3/manager.py +140 -48
  22. web3/method.py +1 -1
  23. web3/middleware/attrdict.py +12 -22
  24. web3/middleware/base.py +14 -6
  25. web3/module.py +17 -21
  26. web3/providers/async_base.py +23 -14
  27. web3/providers/base.py +6 -8
  28. web3/providers/ipc.py +7 -6
  29. web3/providers/legacy_websocket.py +1 -1
  30. web3/providers/persistent/async_ipc.py +5 -3
  31. web3/providers/persistent/persistent.py +121 -17
  32. web3/providers/persistent/persistent_connection.py +11 -4
  33. web3/providers/persistent/request_processor.py +49 -41
  34. web3/providers/persistent/subscription_container.py +56 -0
  35. web3/providers/persistent/subscription_manager.py +298 -0
  36. web3/providers/persistent/websocket.py +4 -4
  37. web3/providers/rpc/async_rpc.py +16 -3
  38. web3/providers/rpc/rpc.py +9 -5
  39. web3/types.py +28 -14
  40. web3/utils/__init__.py +4 -0
  41. web3/utils/subscriptions.py +289 -0
  42. {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/LICENSE +1 -1
  43. {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/METADATA +68 -56
  44. {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/RECORD +46 -43
  45. {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/WHEEL +1 -1
  46. {web3-7.6.1.dist-info → web3-7.8.0.dist-info}/top_level.txt +0 -0
@@ -4,6 +4,7 @@ from typing import (
4
4
  Callable,
5
5
  Collection,
6
6
  Dict,
7
+ Generic,
7
8
  Iterable,
8
9
  List,
9
10
  NoReturn,
@@ -58,7 +59,12 @@ from web3._utils.abi_element_identifiers import (
58
59
  FallbackFn,
59
60
  ReceiveFn,
60
61
  )
62
+ from web3._utils.compat import (
63
+ Self,
64
+ )
61
65
  from web3._utils.contracts import (
66
+ copy_contract_event,
67
+ copy_contract_function,
62
68
  decode_transaction_data,
63
69
  encode_abi,
64
70
  prepare_transaction,
@@ -98,6 +104,7 @@ from web3.exceptions import (
98
104
  InvalidEventABI,
99
105
  LogTopicError,
100
106
  MismatchedABI,
107
+ NoABIEventsFound,
101
108
  NoABIFound,
102
109
  NoABIFunctionsFound,
103
110
  Web3AttributeError,
@@ -117,12 +124,17 @@ from web3.types import (
117
124
  BlockIdentifier,
118
125
  EventData,
119
126
  FilterParams,
127
+ LogReceipt,
128
+ StateOverride,
129
+ TContractEvent,
120
130
  TContractFn,
121
131
  TxParams,
122
132
  TxReceipt,
123
133
  )
124
134
  from web3.utils.abi import (
135
+ _filter_by_argument_count,
125
136
  _get_any_abi_signature_with_name,
137
+ _mismatched_abi_error_diagnosis,
126
138
  check_if_arguments_can_be_encoded,
127
139
  get_abi_element,
128
140
  get_abi_element_info,
@@ -164,6 +176,7 @@ class BaseContractEvent:
164
176
  argument_types: Tuple[str, ...] = tuple()
165
177
  args: Any = None
166
178
  kwargs: Any = None
179
+ _topic: HexStr = None
167
180
 
168
181
  def __init__(self, *argument_names: str, abi: Optional[ABIEvent] = None) -> None:
169
182
  self.abi_element_identifier = type(self).__name__
@@ -187,6 +200,15 @@ class BaseContractEvent:
187
200
  return f"<Event {abi_to_signature(self.abi)}>"
188
201
  return f"<Event {get_abi_element_signature(self.abi_element_identifier)}>"
189
202
 
203
+ def __call__(self, *args: Any, **kwargs: Any) -> Self:
204
+ return copy_contract_event(self, *args, **kwargs)
205
+
206
+ @property
207
+ def topic(self) -> HexStr:
208
+ if self._topic is None:
209
+ self._topic = encode_hex(keccak(text=self.signature))
210
+ return self._topic
211
+
190
212
  @combomethod
191
213
  def _get_event_abi(cls) -> ABIEvent:
192
214
  if cls.abi:
@@ -253,7 +275,7 @@ class BaseContractEvent:
253
275
  yield rich_log
254
276
 
255
277
  @combomethod
256
- def process_log(self, log: HexStr) -> EventData:
278
+ def process_log(self, log: LogReceipt) -> EventData:
257
279
  return get_event_data(self.w3.codec, self.abi, log)
258
280
 
259
281
  @combomethod
@@ -302,9 +324,7 @@ class BaseContractEvent:
302
324
  return event_filter_params
303
325
 
304
326
  @classmethod
305
- def factory(
306
- cls, class_name: str, **kwargs: Any
307
- ) -> Union["ContractEvent", "AsyncContractEvent"]:
327
+ def factory(cls, class_name: str, **kwargs: Any) -> Self:
308
328
  return PropertyCheckingFactory(class_name, (cls,), kwargs)()
309
329
 
310
330
  @staticmethod
@@ -432,7 +452,7 @@ class BaseContractEvent:
432
452
  filter_builder.args[arg].match_single(value)
433
453
 
434
454
 
435
- class BaseContractEvents:
455
+ class BaseContractEvents(Generic[TContractEvent]):
436
456
  """
437
457
  Class containing contract event objects
438
458
 
@@ -458,12 +478,13 @@ class BaseContractEvents:
458
478
  self,
459
479
  abi: ABI,
460
480
  w3: Union["Web3", "AsyncWeb3"],
461
- contract_event_type: Union[Type["ContractEvent"], Type["AsyncContractEvent"]],
481
+ contract_event_type: Type[TContractEvent],
462
482
  address: Optional[ChecksumAddress] = None,
463
483
  ) -> None:
464
484
  self.abi = abi
465
485
  self.w3 = w3
466
486
  self.address = address
487
+ self.contract_event_type = contract_event_type
467
488
  _events: Sequence[ABIEvent] = None
468
489
 
469
490
  if self.abi:
@@ -498,6 +519,42 @@ class BaseContractEvents:
498
519
  except ABIEventNotFound:
499
520
  return False
500
521
 
522
+ def __getattr__(self, event_name: str) -> TContractEvent:
523
+ if super().__getattribute__("abi") is None:
524
+ raise NoABIFound(
525
+ "There is no ABI found for this contract.",
526
+ )
527
+ elif "_events" not in self.__dict__ or len(self._events) == 0:
528
+ raise NoABIEventsFound(
529
+ "The abi for this contract contains no event definitions. ",
530
+ "Are you sure you provided the correct contract abi?",
531
+ )
532
+ elif get_name_from_abi_element_identifier(event_name) not in [
533
+ get_name_from_abi_element_identifier(event["name"])
534
+ for event in self._events
535
+ ]:
536
+ raise ABIEventNotFound(
537
+ f"The event '{event_name}' was not found in this contract's abi. ",
538
+ "Are you sure you provided the correct contract abi?",
539
+ )
540
+
541
+ if "(" not in event_name:
542
+ event_name = _get_any_abi_signature_with_name(event_name, self._events)
543
+ else:
544
+ event_name = f"_{event_name}"
545
+
546
+ return super().__getattribute__(event_name)
547
+
548
+ def __getitem__(self, event_name: str) -> TContractEvent:
549
+ return getattr(self, event_name)
550
+
551
+ def __iter__(self) -> Iterable[TContractEvent]:
552
+ if not hasattr(self, "_events") or not self._events:
553
+ return
554
+
555
+ for event in self._events:
556
+ yield self[abi_to_signature(event)]
557
+
501
558
 
502
559
  class BaseContractFunction:
503
560
  """
@@ -713,14 +770,123 @@ class BaseContractFunction:
713
770
  return _repr + ">"
714
771
  return f"<Function {get_abi_element_signature(self.abi_element_identifier)}>"
715
772
 
773
+ def __call__(self, *args: Any, **kwargs: Any) -> Self:
774
+ # When a function is called, check arguments to obtain the correct function
775
+ # in the contract. self will be used if all args and kwargs are
776
+ # encodable to self.abi, otherwise the correct function is obtained from
777
+ # the contract.
778
+ if (
779
+ self.abi_element_identifier in [FallbackFn, ReceiveFn]
780
+ or self.abi_element_identifier == "constructor"
781
+ ):
782
+ return copy_contract_function(self, *args, **kwargs)
783
+
784
+ all_functions = cast(
785
+ List[ABIFunction],
786
+ filter_abi_by_type(
787
+ "function",
788
+ self.contract_abi,
789
+ ),
790
+ )
791
+ # Filter functions by name to obtain function signatures
792
+ function_name = get_name_from_abi_element_identifier(
793
+ self.abi_element_identifier
794
+ )
795
+ function_abis = [
796
+ function for function in all_functions if function["name"] == function_name
797
+ ]
798
+ num_args = len(args) + len(kwargs)
799
+ function_abis_with_arg_count = cast(
800
+ List[ABIFunction],
801
+ _filter_by_argument_count(
802
+ num_args,
803
+ function_abis,
804
+ ),
805
+ )
806
+
807
+ if not len(function_abis_with_arg_count):
808
+ # Build an ABI without arguments to determine if one exists
809
+ function_abis_with_arg_count = [
810
+ ABIFunction({"type": "function", "name": function_name})
811
+ ]
812
+
813
+ function_abi_matches = []
814
+ contract_function = None
815
+ for abi in function_abis_with_arg_count:
816
+ try:
817
+ # Search for a function ABI that matches the arguments used
818
+ function_abi_matches.append(
819
+ cast(
820
+ ABIFunction,
821
+ get_abi_element(
822
+ function_abis,
823
+ abi_to_signature(abi),
824
+ *args,
825
+ abi_codec=self.w3.codec,
826
+ **kwargs,
827
+ ),
828
+ )
829
+ )
830
+ except MismatchedABI:
831
+ # ignore exceptions
832
+ continue
833
+
834
+ if len(function_abi_matches) == 1:
835
+ function_abi = function_abi_matches[0]
836
+ if abi_to_signature(self.abi) == abi_to_signature(function_abi):
837
+ contract_function = self
838
+ else:
839
+ # Found a match that is not self
840
+ contract_function = self.__class__.factory(
841
+ abi_to_signature(function_abi),
842
+ w3=self.w3,
843
+ contract_abi=self.contract_abi,
844
+ address=self.address,
845
+ abi_element_identifier=abi_to_signature(function_abi),
846
+ abi=function_abi,
847
+ )
848
+ else:
849
+ for abi in function_abi_matches:
850
+ if abi_to_signature(self.abi) == abi_to_signature(abi):
851
+ contract_function = self
852
+ break
853
+ else:
854
+ # Raise exception if multiple found
855
+ raise MismatchedABI(
856
+ _mismatched_abi_error_diagnosis(
857
+ function_name,
858
+ self.contract_abi,
859
+ len(function_abi_matches),
860
+ num_args,
861
+ *args,
862
+ abi_codec=self.w3.codec,
863
+ **kwargs,
864
+ )
865
+ )
866
+
867
+ return copy_contract_function(contract_function, *args, **kwargs)
868
+
716
869
  @classmethod
717
- def factory(
718
- cls, class_name: str, **kwargs: Any
719
- ) -> Union["ContractFunction", "AsyncContractFunction"]:
870
+ def factory(cls, class_name: str, **kwargs: Any) -> Self:
720
871
  return PropertyCheckingFactory(class_name, (cls,), kwargs)()
721
872
 
873
+ def call(
874
+ self,
875
+ transaction: Optional[TxParams] = None,
876
+ block_identifier: Optional[BlockIdentifier] = None,
877
+ state_override: Optional[StateOverride] = None,
878
+ ccip_read_enabled: Optional[bool] = None,
879
+ ) -> Any:
880
+ """
881
+ Implementation of ``call`` should create a callable contract function
882
+ and execute it using the `eth_call` interface.
883
+ """
884
+ raise NotImplementedError(
885
+ "This method should be implemented in the inherited class"
886
+ )
887
+
722
888
 
723
- class BaseContractFunctions:
889
+ class BaseContractFunctions(Generic[TContractFn]):
724
890
  """Class containing contract function objects"""
725
891
 
726
892
  _functions: Sequence[ABIFunction] = None
@@ -729,9 +895,7 @@ class BaseContractFunctions:
729
895
  self,
730
896
  abi: ABI,
731
897
  w3: Union["Web3", "AsyncWeb3"],
732
- contract_function_class: Union[
733
- Type["ContractFunction"], Type["AsyncContractFunction"]
734
- ],
898
+ contract_function_class: Type[TContractFn],
735
899
  address: Optional[ChecksumAddress] = None,
736
900
  decode_tuples: Optional[bool] = False,
737
901
  ) -> None:
@@ -777,6 +941,46 @@ class BaseContractFunctions:
777
941
  except ABIFunctionNotFound:
778
942
  return False
779
943
 
944
+ def __iter__(self) -> Iterable[TContractFn]:
945
+ if not hasattr(self, "_functions") or not self._functions:
946
+ return
947
+
948
+ for func in self._functions:
949
+ yield self[abi_to_signature(func)]
950
+
951
+ def __getattr__(self, function_name: str) -> TContractFn:
952
+ if super().__getattribute__("abi") is None:
953
+ raise NoABIFound(
954
+ "There is no ABI found for this contract.",
955
+ )
956
+ elif "_functions" not in self.__dict__ or len(self._functions) == 0:
957
+ raise NoABIFunctionsFound(
958
+ "The abi for this contract contains no function definitions. ",
959
+ "Are you sure you provided the correct contract abi?",
960
+ )
961
+ elif get_name_from_abi_element_identifier(function_name) not in [
962
+ get_name_from_abi_element_identifier(function["name"])
963
+ for function in self._functions
964
+ ]:
965
+ raise ABIFunctionNotFound(
966
+ f"The function '{function_name}' was not found in this ",
967
+ "contract's abi.",
968
+ )
969
+
970
+ if "(" not in function_name:
971
+ function_name = _get_any_abi_signature_with_name(
972
+ function_name, self._functions
973
+ )
974
+ else:
975
+ function_name = f"_{function_name}"
976
+
977
+ return super().__getattribute__(
978
+ function_name,
979
+ )
980
+
981
+ def __getitem__(self, function_name: str) -> TContractFn:
982
+ return getattr(self, function_name)
983
+
780
984
 
781
985
  class BaseContract:
782
986
  """
web3/contract/contract.py CHANGED
@@ -13,7 +13,6 @@ from typing import (
13
13
 
14
14
  from eth_typing import (
15
15
  ABI,
16
- ABIFunction,
17
16
  ChecksumAddress,
18
17
  )
19
18
  from eth_utils import (
@@ -31,7 +30,6 @@ from hexbytes import (
31
30
 
32
31
  from web3._utils.abi import (
33
32
  fallback_func_abi_exists,
34
- get_name_from_abi_element_identifier,
35
33
  receive_func_abi_exists,
36
34
  )
37
35
  from web3._utils.abi_element_identifiers import (
@@ -42,8 +40,6 @@ from web3._utils.compat import (
42
40
  Self,
43
41
  )
44
42
  from web3._utils.contracts import (
45
- copy_contract_event,
46
- copy_contract_function,
47
43
  parse_block_identifier,
48
44
  )
49
45
  from web3._utils.datatypes import (
@@ -86,12 +82,6 @@ from web3.contract.utils import (
86
82
  transact_with_contract_function,
87
83
  )
88
84
  from web3.exceptions import (
89
- ABIEventNotFound,
90
- ABIFunctionNotFound,
91
- MismatchedABI,
92
- NoABIEventsFound,
93
- NoABIFound,
94
- NoABIFunctionsFound,
95
85
  Web3AttributeError,
96
86
  Web3TypeError,
97
87
  Web3ValidationError,
@@ -103,12 +93,6 @@ from web3.types import (
103
93
  StateOverride,
104
94
  TxParams,
105
95
  )
106
- from web3.utils.abi import (
107
- _filter_by_argument_count,
108
- _get_any_abi_signature_with_name,
109
- _mismatched_abi_error_diagnosis,
110
- get_abi_element,
111
- )
112
96
 
113
97
  if TYPE_CHECKING:
114
98
  from ens import ENS # noqa: F401
@@ -119,9 +103,6 @@ class ContractEvent(BaseContractEvent):
119
103
  # mypy types
120
104
  w3: "Web3"
121
105
 
122
- def __call__(self, *args: Any, **kwargs: Any) -> "ContractEvent":
123
- return copy_contract_event(self, *args, **kwargs)
124
-
125
106
  @combomethod
126
107
  def get_logs(
127
108
  self,
@@ -255,162 +236,18 @@ class ContractEvent(BaseContractEvent):
255
236
  builder.address = self.address
256
237
  return builder
257
238
 
258
- @classmethod
259
- def factory(cls, class_name: str, **kwargs: Any) -> Self:
260
- return PropertyCheckingFactory(class_name, (cls,), kwargs)()
261
239
 
262
-
263
- class ContractEvents(BaseContractEvents):
240
+ class ContractEvents(BaseContractEvents[ContractEvent]):
264
241
  def __init__(
265
242
  self, abi: ABI, w3: "Web3", address: Optional[ChecksumAddress] = None
266
243
  ) -> None:
267
244
  super().__init__(abi, w3, ContractEvent, address)
268
245
 
269
- def __getattr__(self, event_name: str) -> "ContractEvent":
270
- if super().__getattribute__("abi") is None:
271
- raise NoABIFound(
272
- "There is no ABI found for this contract.",
273
- )
274
- elif "_events" not in self.__dict__ or len(self._events) == 0:
275
- raise NoABIEventsFound(
276
- "The abi for this contract contains no event definitions. ",
277
- "Are you sure you provided the correct contract abi?",
278
- )
279
- elif get_name_from_abi_element_identifier(event_name) not in [
280
- get_name_from_abi_element_identifier(event["name"])
281
- for event in self._events
282
- ]:
283
- raise ABIEventNotFound(
284
- f"The event '{event_name}' was not found in this contract's abi. ",
285
- "Are you sure you provided the correct contract abi?",
286
- )
287
-
288
- if "(" not in event_name:
289
- event_name = _get_any_abi_signature_with_name(event_name, self._events)
290
- else:
291
- event_name = f"_{event_name}"
292
-
293
- return super().__getattribute__(event_name)
294
-
295
- def __getitem__(self, event_name: str) -> "ContractEvent":
296
- return getattr(self, event_name)
297
-
298
- def __iter__(self) -> Iterable["ContractEvent"]:
299
- if not hasattr(self, "_events") or not self._events:
300
- return
301
-
302
- for event in self._events:
303
- yield self[abi_to_signature(event)]
304
-
305
246
 
306
247
  class ContractFunction(BaseContractFunction):
307
248
  # mypy types
308
249
  w3: "Web3"
309
250
 
310
- def __call__(self, *args: Any, **kwargs: Any) -> "ContractFunction":
311
- # When a function is called, check arguments to obtain the correct function
312
- # in the contract. self will be used if all args and kwargs are
313
- # encodable to self.abi, otherwise the correct function is obtained from
314
- # the contract.
315
- if (
316
- self.abi_element_identifier in [FallbackFn, ReceiveFn]
317
- or self.abi_element_identifier == "constructor"
318
- ):
319
- return copy_contract_function(self, *args, **kwargs)
320
-
321
- all_functions = cast(
322
- List[ABIFunction],
323
- filter_abi_by_type(
324
- "function",
325
- self.contract_abi,
326
- ),
327
- )
328
- # Filter functions by name to obtain function signatures
329
- function_name = get_name_from_abi_element_identifier(
330
- self.abi_element_identifier
331
- )
332
- function_abis = [
333
- function for function in all_functions if function["name"] == function_name
334
- ]
335
- num_args = len(args) + len(kwargs)
336
- function_abis_with_arg_count = cast(
337
- List[ABIFunction],
338
- _filter_by_argument_count(
339
- num_args,
340
- function_abis,
341
- ),
342
- )
343
-
344
- if not len(function_abis_with_arg_count):
345
- # Build an ABI without arguments to determine if one exists
346
- function_abis_with_arg_count = [
347
- ABIFunction({"type": "function", "name": function_name})
348
- ]
349
-
350
- # Check that arguments in call match a function ABI
351
- num_attempts = 0
352
- function_abi_matches = []
353
- contract_function = None
354
- for abi in function_abis_with_arg_count:
355
- try:
356
- num_attempts += 1
357
-
358
- # Search for a function ABI that matches the arguments used
359
- function_abi_matches.append(
360
- cast(
361
- ABIFunction,
362
- get_abi_element(
363
- function_abis,
364
- abi_to_signature(abi),
365
- *args,
366
- abi_codec=self.w3.codec,
367
- **kwargs,
368
- ),
369
- )
370
- )
371
- except MismatchedABI:
372
- # ignore exceptions
373
- continue
374
-
375
- if len(function_abi_matches) == 1:
376
- function_abi = function_abi_matches[0]
377
- if abi_to_signature(self.abi) == abi_to_signature(function_abi):
378
- contract_function = self
379
- else:
380
- # Found a match that is not self
381
- contract_function = ContractFunction.factory(
382
- abi_to_signature(function_abi),
383
- w3=self.w3,
384
- contract_abi=self.contract_abi,
385
- address=self.address,
386
- abi_element_identifier=abi_to_signature(function_abi),
387
- abi=function_abi,
388
- )
389
- else:
390
- for abi in function_abi_matches:
391
- if abi_to_signature(self.abi) == abi_to_signature(abi):
392
- contract_function = self
393
- break
394
- else:
395
- # Raise exception if multiple found
396
- raise MismatchedABI(
397
- _mismatched_abi_error_diagnosis(
398
- function_name,
399
- self.contract_abi,
400
- len(function_abi_matches),
401
- num_args,
402
- *args,
403
- abi_codec=self.w3.codec,
404
- **kwargs,
405
- )
406
- )
407
-
408
- return copy_contract_function(contract_function, *args, **kwargs)
409
-
410
- @classmethod
411
- def factory(cls, class_name: str, **kwargs: Any) -> Self:
412
- return PropertyCheckingFactory(class_name, (cls,), kwargs)()
413
-
414
251
  def call(
415
252
  self,
416
253
  transaction: Optional[TxParams] = None,
@@ -555,7 +392,7 @@ class ContractFunction(BaseContractFunction):
555
392
  return cast(ContractFunction, NonExistentReceiveFunction())
556
393
 
557
394
 
558
- class ContractFunctions(BaseContractFunctions):
395
+ class ContractFunctions(BaseContractFunctions[ContractFunction]):
559
396
  def __init__(
560
397
  self,
561
398
  abi: ABI,
@@ -565,46 +402,6 @@ class ContractFunctions(BaseContractFunctions):
565
402
  ) -> None:
566
403
  super().__init__(abi, w3, ContractFunction, address, decode_tuples)
567
404
 
568
- def __iter__(self) -> Iterable["ContractFunction"]:
569
- if not hasattr(self, "_functions") or not self._functions:
570
- return
571
-
572
- for func in self._functions:
573
- yield self[abi_to_signature(func)]
574
-
575
- def __getattr__(self, function_name: str) -> "ContractFunction":
576
- if super().__getattribute__("abi") is None:
577
- raise NoABIFound(
578
- "There is no ABI found for this contract.",
579
- )
580
- elif "_functions" not in self.__dict__ or len(self._functions) == 0:
581
- raise NoABIFunctionsFound(
582
- "The abi for this contract contains no function definitions. ",
583
- "Are you sure you provided the correct contract abi?",
584
- )
585
- elif get_name_from_abi_element_identifier(function_name) not in [
586
- get_name_from_abi_element_identifier(function["name"])
587
- for function in self._functions
588
- ]:
589
- raise ABIFunctionNotFound(
590
- f"The function '{function_name}' was not found in this ",
591
- "contract's abi.",
592
- )
593
-
594
- if "(" not in function_name:
595
- function_name = _get_any_abi_signature_with_name(
596
- function_name, self._functions
597
- )
598
- else:
599
- function_name = f"_{function_name}"
600
-
601
- return super().__getattribute__(
602
- function_name,
603
- )
604
-
605
- def __getitem__(self, function_name: str) -> "ContractFunction":
606
- return getattr(self, function_name)
607
-
608
405
 
609
406
  class Contract(BaseContract):
610
407
  # mypy types
web3/datastructures.py CHANGED
@@ -15,9 +15,9 @@ from typing import (
15
15
  Optional,
16
16
  Sequence,
17
17
  Tuple,
18
- Type,
19
18
  TypeVar,
20
19
  Union,
20
+ ValuesView,
21
21
  cast,
22
22
  )
23
23
 
@@ -25,9 +25,6 @@ from eth_utils import (
25
25
  is_integer,
26
26
  )
27
27
 
28
- from web3._utils.formatters import (
29
- recursive_map,
30
- )
31
28
  from web3.exceptions import (
32
29
  Web3AssertionError,
33
30
  Web3TypeError,
@@ -81,19 +78,18 @@ class ReadableAttributeDict(Mapping[TKey, TValue]):
81
78
  builder.text(")")
82
79
 
83
80
  @classmethod
84
- def _apply_if_mapping(cls: Type[T], value: TValue) -> Union[T, TValue]:
81
+ def recursive(cls, value: TValue) -> Any:
82
+ """
83
+ Recursively convert mappings to ReadableAttributeDict instances and
84
+ process nested collections (e.g., lists, sets, and dictionaries).
85
+ """
85
86
  if isinstance(value, Mapping):
86
- # error: Too many arguments for "object"
87
- return cls(value) # type: ignore
88
- else:
89
- return value
90
-
91
- @classmethod
92
- def recursive(cls, value: TValue) -> "ReadableAttributeDict[TKey, TValue]":
93
- return cast(
94
- "ReadableAttributeDict[TKey, TValue]",
95
- recursive_map(cls._apply_if_mapping, value),
96
- )
87
+ return cls({k: cls.recursive(v) for k, v in value.items()})
88
+ elif isinstance(value, Sequence) and not isinstance(value, (str, bytes)):
89
+ return type(value)([cls.recursive(v) for v in value]) # type: ignore
90
+ elif isinstance(value, set):
91
+ return {cls.recursive(v) for v in value}
92
+ return value
97
93
 
98
94
 
99
95
  class MutableAttributeDict(
@@ -342,3 +338,6 @@ class NamedElementOnion(Mapping[TKey, TValue]):
342
338
  # This leads to typing issues, so it's better to use
343
339
  # ``as_tuple_of_middleware()`` to achieve the same result.
344
340
  return iter(self._reversed_middleware()) # type: ignore
341
+
342
+ def values(self) -> ValuesView[TValue]:
343
+ return ValuesView(self._queue)