web3 6.18.0__tar.gz → 6.20.0__tar.gz

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 (293) hide show
  1. {web3-6.18.0 → web3-6.20.0}/PKG-INFO +1 -1
  2. {web3-6.18.0 → web3-6.20.0}/pyproject.toml +1 -1
  3. {web3-6.18.0 → web3-6.20.0}/setup.py +1 -1
  4. {web3-6.18.0 → web3-6.20.0}/web3/__init__.py +21 -3
  5. {web3-6.18.0 → web3-6.20.0}/web3/_utils/module_testing/module_testing_utils.py +13 -0
  6. {web3-6.18.0 → web3-6.20.0}/web3/exceptions.py +19 -1
  7. {web3-6.18.0 → web3-6.20.0}/web3/main.py +5 -8
  8. {web3-6.18.0 → web3-6.20.0}/web3/manager.py +19 -8
  9. {web3-6.18.0 → web3-6.20.0}/web3/providers/__init__.py +19 -0
  10. web3-6.20.0/web3/providers/persistent.py +214 -0
  11. {web3-6.18.0 → web3-6.20.0}/web3/providers/websocket/request_processor.py +40 -5
  12. web3-6.20.0/web3/providers/websocket/websocket_v2.py +146 -0
  13. {web3-6.18.0 → web3-6.20.0}/web3/tools/pytest_ethereum/deployer.py +1 -1
  14. {web3-6.18.0 → web3-6.20.0}/web3/types.py +27 -1
  15. {web3-6.18.0 → web3-6.20.0}/web3.egg-info/PKG-INFO +1 -1
  16. web3-6.18.0/web3/providers/persistent.py +0 -55
  17. web3-6.18.0/web3/providers/websocket/websocket_v2.py +0 -242
  18. {web3-6.18.0 → web3-6.20.0}/LICENSE +0 -0
  19. {web3-6.18.0 → web3-6.20.0}/MANIFEST.in +0 -0
  20. {web3-6.18.0 → web3-6.20.0}/README.md +0 -0
  21. {web3-6.18.0 → web3-6.20.0}/ens/__init__.py +0 -0
  22. {web3-6.18.0 → web3-6.20.0}/ens/_normalization.py +0 -0
  23. {web3-6.18.0 → web3-6.20.0}/ens/abis.py +0 -0
  24. {web3-6.18.0 → web3-6.20.0}/ens/async_ens.py +0 -0
  25. {web3-6.18.0 → web3-6.20.0}/ens/auto.py +0 -0
  26. {web3-6.18.0 → web3-6.20.0}/ens/base_ens.py +0 -0
  27. {web3-6.18.0 → web3-6.20.0}/ens/constants.py +0 -0
  28. {web3-6.18.0 → web3-6.20.0}/ens/contract_data.py +0 -0
  29. {web3-6.18.0 → web3-6.20.0}/ens/ens.py +0 -0
  30. {web3-6.18.0 → web3-6.20.0}/ens/exceptions.py +0 -0
  31. {web3-6.18.0 → web3-6.20.0}/ens/specs/nf.json +0 -0
  32. {web3-6.18.0 → web3-6.20.0}/ens/specs/normalization_spec.json +0 -0
  33. {web3-6.18.0 → web3-6.20.0}/ens/utils.py +0 -0
  34. {web3-6.18.0 → web3-6.20.0}/ethpm/__init__.py +0 -0
  35. {web3-6.18.0 → web3-6.20.0}/ethpm/_utils/__init__.py +0 -0
  36. {web3-6.18.0 → web3-6.20.0}/ethpm/_utils/backend.py +0 -0
  37. {web3-6.18.0 → web3-6.20.0}/ethpm/_utils/cache.py +0 -0
  38. {web3-6.18.0 → web3-6.20.0}/ethpm/_utils/chains.py +0 -0
  39. {web3-6.18.0 → web3-6.20.0}/ethpm/_utils/contract.py +0 -0
  40. {web3-6.18.0 → web3-6.20.0}/ethpm/_utils/deployments.py +0 -0
  41. {web3-6.18.0 → web3-6.20.0}/ethpm/_utils/ipfs.py +0 -0
  42. {web3-6.18.0 → web3-6.20.0}/ethpm/_utils/protobuf/__init__.py +0 -0
  43. {web3-6.18.0 → web3-6.20.0}/ethpm/_utils/protobuf/ipfs_file_pb2.py +0 -0
  44. {web3-6.18.0 → web3-6.20.0}/ethpm/_utils/registry.py +0 -0
  45. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/__init__.py +0 -0
  46. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/ens/v3.json +0 -0
  47. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/escrow/with_bytecode_v3.json +0 -0
  48. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/ipfs_file.proto +0 -0
  49. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/owned/output_v3.json +0 -0
  50. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/owned/with_contract_type_v3.json +0 -0
  51. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/registry/contracts/Authority.sol +0 -0
  52. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/registry/contracts/IndexedOrderedSetLib.sol +0 -0
  53. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/registry/contracts/PackageDB.sol +0 -0
  54. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/registry/contracts/PackageRegistry.sol +0 -0
  55. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/registry/contracts/PackageRegistryInterface.sol +0 -0
  56. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/registry/contracts/ReleaseDB.sol +0 -0
  57. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/registry/contracts/ReleaseValidator.sol +0 -0
  58. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/registry/solc_input.json +0 -0
  59. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/registry/solc_output.json +0 -0
  60. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/registry/v3.json +0 -0
  61. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/safe-math-lib/v3-strict-no-deployments.json +0 -0
  62. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/simple-registry/contracts/Ownable.sol +0 -0
  63. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/simple-registry/contracts/PackageRegistry.sol +0 -0
  64. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/simple-registry/contracts/PackageRegistryInterface.sol +0 -0
  65. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/simple-registry/solc_input.json +0 -0
  66. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/simple-registry/solc_output.json +0 -0
  67. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/simple-registry/v3.json +0 -0
  68. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/standard-token/output_v3.json +0 -0
  69. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/standard-token/with_bytecode_v3.json +0 -0
  70. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/vyper_registry/0.1.0.json +0 -0
  71. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/vyper_registry/registry.vy +0 -0
  72. {web3-6.18.0 → web3-6.20.0}/ethpm/assets/vyper_registry/registry_with_delete.vy +0 -0
  73. {web3-6.18.0 → web3-6.20.0}/ethpm/backends/__init__.py +0 -0
  74. {web3-6.18.0 → web3-6.20.0}/ethpm/backends/base.py +0 -0
  75. {web3-6.18.0 → web3-6.20.0}/ethpm/backends/http.py +0 -0
  76. {web3-6.18.0 → web3-6.20.0}/ethpm/backends/ipfs.py +0 -0
  77. {web3-6.18.0 → web3-6.20.0}/ethpm/backends/registry.py +0 -0
  78. {web3-6.18.0 → web3-6.20.0}/ethpm/constants.py +0 -0
  79. {web3-6.18.0 → web3-6.20.0}/ethpm/contract.py +0 -0
  80. {web3-6.18.0 → web3-6.20.0}/ethpm/dependencies.py +0 -0
  81. {web3-6.18.0 → web3-6.20.0}/ethpm/deployments.py +0 -0
  82. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/escrow/1.0.0-pretty.json +0 -0
  83. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/escrow/1.0.0.json +0 -0
  84. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/escrow/contracts/Escrow.sol +0 -0
  85. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/escrow/contracts/SafeSendLib.sol +0 -0
  86. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/escrow/v3-pretty.json +0 -0
  87. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/escrow/v3.json +0 -0
  88. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/owned/1.0.0-pretty.json +0 -0
  89. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/owned/1.0.0.json +0 -0
  90. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/owned/contracts/Owned.sol +0 -0
  91. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/owned/v3-pretty.json +0 -0
  92. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/owned/v3.json +0 -0
  93. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/piper-coin/1.0.0-pretty.json +0 -0
  94. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/piper-coin/1.0.0.json +0 -0
  95. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/piper-coin/v3-pretty.json +0 -0
  96. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/piper-coin/v3.json +0 -0
  97. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/safe-math-lib/1.0.0-pretty.json +0 -0
  98. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/safe-math-lib/1.0.0.json +0 -0
  99. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/safe-math-lib/contracts/SafeMathLib.sol +0 -0
  100. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/safe-math-lib/v3-pretty.json +0 -0
  101. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/safe-math-lib/v3.json +0 -0
  102. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/standard-token/1.0.0-pretty.json +0 -0
  103. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/standard-token/1.0.0.json +0 -0
  104. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/standard-token/contracts/AbstractToken.sol +0 -0
  105. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/standard-token/contracts/StandardToken.sol +0 -0
  106. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/standard-token/v3-pretty.json +0 -0
  107. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/standard-token/v3.json +0 -0
  108. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/transferable/1.0.0-pretty.json +0 -0
  109. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/transferable/1.0.0.json +0 -0
  110. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/transferable/contracts/Transferable.sol +0 -0
  111. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/transferable/v3-pretty.json +0 -0
  112. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/transferable/v3.json +0 -0
  113. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/wallet/1.0.0-pretty.json +0 -0
  114. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/wallet/1.0.0.json +0 -0
  115. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/wallet/contracts/Wallet.sol +0 -0
  116. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/wallet/v3-pretty.json +0 -0
  117. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/wallet/v3.json +0 -0
  118. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/wallet-with-send/1.0.0-pretty.json +0 -0
  119. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/wallet-with-send/1.0.0.json +0 -0
  120. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/wallet-with-send/contracts/WalletWithSend.sol +0 -0
  121. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/wallet-with-send/v3-pretty.json +0 -0
  122. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/examples/wallet-with-send/v3.json +0 -0
  123. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/spec/package.spec.json +0 -0
  124. {web3-6.18.0 → web3-6.20.0}/ethpm/ethpm-spec/spec/v3.spec.json +0 -0
  125. {web3-6.18.0 → web3-6.20.0}/ethpm/exceptions.py +0 -0
  126. {web3-6.18.0 → web3-6.20.0}/ethpm/package.py +0 -0
  127. {web3-6.18.0 → web3-6.20.0}/ethpm/tools/__init__.py +0 -0
  128. {web3-6.18.0 → web3-6.20.0}/ethpm/tools/builder.py +0 -0
  129. {web3-6.18.0 → web3-6.20.0}/ethpm/tools/checker.py +0 -0
  130. {web3-6.18.0 → web3-6.20.0}/ethpm/tools/get_manifest.py +0 -0
  131. {web3-6.18.0 → web3-6.20.0}/ethpm/uri.py +0 -0
  132. {web3-6.18.0 → web3-6.20.0}/ethpm/validation/__init__.py +0 -0
  133. {web3-6.18.0 → web3-6.20.0}/ethpm/validation/manifest.py +0 -0
  134. {web3-6.18.0 → web3-6.20.0}/ethpm/validation/misc.py +0 -0
  135. {web3-6.18.0 → web3-6.20.0}/ethpm/validation/package.py +0 -0
  136. {web3-6.18.0 → web3-6.20.0}/ethpm/validation/uri.py +0 -0
  137. {web3-6.18.0 → web3-6.20.0}/setup.cfg +0 -0
  138. {web3-6.18.0 → web3-6.20.0}/web3/_utils/__init__.py +0 -0
  139. {web3-6.18.0 → web3-6.20.0}/web3/_utils/abi.py +0 -0
  140. {web3-6.18.0 → web3-6.20.0}/web3/_utils/async_caching.py +0 -0
  141. {web3-6.18.0 → web3-6.20.0}/web3/_utils/async_transactions.py +0 -0
  142. {web3-6.18.0 → web3-6.20.0}/web3/_utils/blocks.py +0 -0
  143. {web3-6.18.0 → web3-6.20.0}/web3/_utils/caching.py +0 -0
  144. {web3-6.18.0 → web3-6.20.0}/web3/_utils/compat/__init__.py +0 -0
  145. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/__init__.py +0 -0
  146. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/compile_contracts.py +0 -0
  147. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/__init__.py +0 -0
  148. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/_custom_contract_data.py +0 -0
  149. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/address_reflector.py +0 -0
  150. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/arrays_contract.py +0 -0
  151. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/bytes_contracts.py +0 -0
  152. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/constructor_contracts.py +0 -0
  153. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/contract_caller_tester.py +0 -0
  154. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/emitter_contract.py +0 -0
  155. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/event_contracts.py +0 -0
  156. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/extended_resolver.py +0 -0
  157. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/fallback_function_contract.py +0 -0
  158. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/function_name_tester_contract.py +0 -0
  159. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/math_contract.py +0 -0
  160. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/offchain_lookup.py +0 -0
  161. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/offchain_resolver.py +0 -0
  162. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/panic_errors_contract.py +0 -0
  163. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/payable_tester.py +0 -0
  164. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/receive_function_contracts.py +0 -0
  165. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/reflector_contracts.py +0 -0
  166. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/revert_contract.py +0 -0
  167. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/simple_resolver.py +0 -0
  168. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/storage_contract.py +0 -0
  169. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/string_contract.py +0 -0
  170. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contract_sources/contract_data/tuple_contracts.py +0 -0
  171. {web3-6.18.0 → web3-6.20.0}/web3/_utils/contracts.py +0 -0
  172. {web3-6.18.0 → web3-6.20.0}/web3/_utils/datatypes.py +0 -0
  173. {web3-6.18.0 → web3-6.20.0}/web3/_utils/decorators.py +0 -0
  174. {web3-6.18.0 → web3-6.20.0}/web3/_utils/empty.py +0 -0
  175. {web3-6.18.0 → web3-6.20.0}/web3/_utils/encoding.py +0 -0
  176. {web3-6.18.0 → web3-6.20.0}/web3/_utils/ens.py +0 -0
  177. {web3-6.18.0 → web3-6.20.0}/web3/_utils/error_formatters_utils.py +0 -0
  178. {web3-6.18.0 → web3-6.20.0}/web3/_utils/events.py +0 -0
  179. {web3-6.18.0 → web3-6.20.0}/web3/_utils/fee_utils.py +0 -0
  180. {web3-6.18.0 → web3-6.20.0}/web3/_utils/filters.py +0 -0
  181. {web3-6.18.0 → web3-6.20.0}/web3/_utils/formatters.py +0 -0
  182. {web3-6.18.0 → web3-6.20.0}/web3/_utils/function_identifiers.py +0 -0
  183. {web3-6.18.0 → web3-6.20.0}/web3/_utils/http.py +0 -0
  184. {web3-6.18.0 → web3-6.20.0}/web3/_utils/hypothesis.py +0 -0
  185. {web3-6.18.0 → web3-6.20.0}/web3/_utils/math.py +0 -0
  186. {web3-6.18.0 → web3-6.20.0}/web3/_utils/method_formatters.py +0 -0
  187. {web3-6.18.0 → web3-6.20.0}/web3/_utils/miner.py +0 -0
  188. {web3-6.18.0 → web3-6.20.0}/web3/_utils/module.py +0 -0
  189. {web3-6.18.0 → web3-6.20.0}/web3/_utils/module_testing/__init__.py +0 -0
  190. {web3-6.18.0 → web3-6.20.0}/web3/_utils/module_testing/eth_module.py +0 -0
  191. {web3-6.18.0 → web3-6.20.0}/web3/_utils/module_testing/go_ethereum_admin_module.py +0 -0
  192. {web3-6.18.0 → web3-6.20.0}/web3/_utils/module_testing/go_ethereum_personal_module.py +0 -0
  193. {web3-6.18.0 → web3-6.20.0}/web3/_utils/module_testing/go_ethereum_txpool_module.py +0 -0
  194. {web3-6.18.0 → web3-6.20.0}/web3/_utils/module_testing/net_module.py +0 -0
  195. {web3-6.18.0 → web3-6.20.0}/web3/_utils/module_testing/persistent_connection_provider.py +0 -0
  196. {web3-6.18.0 → web3-6.20.0}/web3/_utils/module_testing/web3_module.py +0 -0
  197. {web3-6.18.0 → web3-6.20.0}/web3/_utils/normalizers.py +0 -0
  198. {web3-6.18.0 → web3-6.20.0}/web3/_utils/request.py +0 -0
  199. {web3-6.18.0 → web3-6.20.0}/web3/_utils/rpc_abi.py +0 -0
  200. {web3-6.18.0 → web3-6.20.0}/web3/_utils/threads.py +0 -0
  201. {web3-6.18.0 → web3-6.20.0}/web3/_utils/transactions.py +0 -0
  202. {web3-6.18.0 → web3-6.20.0}/web3/_utils/type_conversion.py +0 -0
  203. {web3-6.18.0 → web3-6.20.0}/web3/_utils/utility_methods.py +0 -0
  204. {web3-6.18.0 → web3-6.20.0}/web3/_utils/validation.py +0 -0
  205. {web3-6.18.0 → web3-6.20.0}/web3/_utils/windows.py +0 -0
  206. {web3-6.18.0 → web3-6.20.0}/web3/auto/__init__.py +0 -0
  207. {web3-6.18.0 → web3-6.20.0}/web3/auto/gethdev.py +0 -0
  208. {web3-6.18.0 → web3-6.20.0}/web3/beacon/__init__.py +0 -0
  209. {web3-6.18.0 → web3-6.20.0}/web3/beacon/api_endpoints.py +0 -0
  210. {web3-6.18.0 → web3-6.20.0}/web3/beacon/async_beacon.py +0 -0
  211. {web3-6.18.0 → web3-6.20.0}/web3/beacon/main.py +0 -0
  212. {web3-6.18.0 → web3-6.20.0}/web3/constants.py +0 -0
  213. {web3-6.18.0 → web3-6.20.0}/web3/contract/__init__.py +0 -0
  214. {web3-6.18.0 → web3-6.20.0}/web3/contract/async_contract.py +0 -0
  215. {web3-6.18.0 → web3-6.20.0}/web3/contract/base_contract.py +0 -0
  216. {web3-6.18.0 → web3-6.20.0}/web3/contract/contract.py +0 -0
  217. {web3-6.18.0 → web3-6.20.0}/web3/contract/utils.py +0 -0
  218. {web3-6.18.0 → web3-6.20.0}/web3/datastructures.py +0 -0
  219. {web3-6.18.0 → web3-6.20.0}/web3/eth/__init__.py +0 -0
  220. {web3-6.18.0 → web3-6.20.0}/web3/eth/async_eth.py +0 -0
  221. {web3-6.18.0 → web3-6.20.0}/web3/eth/base_eth.py +0 -0
  222. {web3-6.18.0 → web3-6.20.0}/web3/eth/eth.py +0 -0
  223. {web3-6.18.0 → web3-6.20.0}/web3/gas_strategies/__init__.py +0 -0
  224. {web3-6.18.0 → web3-6.20.0}/web3/gas_strategies/rpc.py +0 -0
  225. {web3-6.18.0 → web3-6.20.0}/web3/gas_strategies/time_based.py +0 -0
  226. {web3-6.18.0 → web3-6.20.0}/web3/geth.py +0 -0
  227. {web3-6.18.0 → web3-6.20.0}/web3/logs.py +0 -0
  228. {web3-6.18.0 → web3-6.20.0}/web3/method.py +0 -0
  229. {web3-6.18.0 → web3-6.20.0}/web3/middleware/__init__.py +0 -0
  230. {web3-6.18.0 → web3-6.20.0}/web3/middleware/abi.py +0 -0
  231. {web3-6.18.0 → web3-6.20.0}/web3/middleware/async_cache.py +0 -0
  232. {web3-6.18.0 → web3-6.20.0}/web3/middleware/attrdict.py +0 -0
  233. {web3-6.18.0 → web3-6.20.0}/web3/middleware/buffered_gas_estimate.py +0 -0
  234. {web3-6.18.0 → web3-6.20.0}/web3/middleware/cache.py +0 -0
  235. {web3-6.18.0 → web3-6.20.0}/web3/middleware/exception_handling.py +0 -0
  236. {web3-6.18.0 → web3-6.20.0}/web3/middleware/exception_retry_request.py +0 -0
  237. {web3-6.18.0 → web3-6.20.0}/web3/middleware/filter.py +0 -0
  238. {web3-6.18.0 → web3-6.20.0}/web3/middleware/fixture.py +0 -0
  239. {web3-6.18.0 → web3-6.20.0}/web3/middleware/formatting.py +0 -0
  240. {web3-6.18.0 → web3-6.20.0}/web3/middleware/gas_price_strategy.py +0 -0
  241. {web3-6.18.0 → web3-6.20.0}/web3/middleware/geth_poa.py +0 -0
  242. {web3-6.18.0 → web3-6.20.0}/web3/middleware/names.py +0 -0
  243. {web3-6.18.0 → web3-6.20.0}/web3/middleware/normalize_request_parameters.py +0 -0
  244. {web3-6.18.0 → web3-6.20.0}/web3/middleware/pythonic.py +0 -0
  245. {web3-6.18.0 → web3-6.20.0}/web3/middleware/signing.py +0 -0
  246. {web3-6.18.0 → web3-6.20.0}/web3/middleware/simulate_unmined_transaction.py +0 -0
  247. {web3-6.18.0 → web3-6.20.0}/web3/middleware/stalecheck.py +0 -0
  248. {web3-6.18.0 → web3-6.20.0}/web3/middleware/validation.py +0 -0
  249. {web3-6.18.0 → web3-6.20.0}/web3/module.py +0 -0
  250. {web3-6.18.0 → web3-6.20.0}/web3/net.py +0 -0
  251. {web3-6.18.0 → web3-6.20.0}/web3/pm.py +0 -0
  252. {web3-6.18.0 → web3-6.20.0}/web3/providers/async_base.py +0 -0
  253. {web3-6.18.0 → web3-6.20.0}/web3/providers/async_rpc.py +0 -0
  254. {web3-6.18.0 → web3-6.20.0}/web3/providers/auto.py +0 -0
  255. {web3-6.18.0 → web3-6.20.0}/web3/providers/base.py +0 -0
  256. {web3-6.18.0 → web3-6.20.0}/web3/providers/eth_tester/__init__.py +0 -0
  257. {web3-6.18.0 → web3-6.20.0}/web3/providers/eth_tester/defaults.py +0 -0
  258. {web3-6.18.0 → web3-6.20.0}/web3/providers/eth_tester/main.py +0 -0
  259. {web3-6.18.0 → web3-6.20.0}/web3/providers/eth_tester/middleware.py +0 -0
  260. {web3-6.18.0 → web3-6.20.0}/web3/providers/ipc.py +0 -0
  261. {web3-6.18.0 → web3-6.20.0}/web3/providers/rpc.py +0 -0
  262. {web3-6.18.0 → web3-6.20.0}/web3/providers/websocket/__init__.py +0 -0
  263. {web3-6.18.0 → web3-6.20.0}/web3/providers/websocket/websocket.py +0 -0
  264. {web3-6.18.0 → web3-6.20.0}/web3/providers/websocket/websocket_connection.py +0 -0
  265. {web3-6.18.0 → web3-6.20.0}/web3/py.typed +0 -0
  266. {web3-6.18.0 → web3-6.20.0}/web3/scripts/__init__.py +0 -0
  267. {web3-6.18.0 → web3-6.20.0}/web3/scripts/release/__init__.py +0 -0
  268. {web3-6.18.0 → web3-6.20.0}/web3/scripts/release/test_package.py +0 -0
  269. {web3-6.18.0 → web3-6.20.0}/web3/testing.py +0 -0
  270. {web3-6.18.0 → web3-6.20.0}/web3/tools/__init__.py +0 -0
  271. {web3-6.18.0 → web3-6.20.0}/web3/tools/benchmark/__init__.py +0 -0
  272. {web3-6.18.0 → web3-6.20.0}/web3/tools/benchmark/main.py +0 -0
  273. {web3-6.18.0 → web3-6.20.0}/web3/tools/benchmark/node.py +0 -0
  274. {web3-6.18.0 → web3-6.20.0}/web3/tools/benchmark/reporting.py +0 -0
  275. {web3-6.18.0 → web3-6.20.0}/web3/tools/benchmark/utils.py +0 -0
  276. {web3-6.18.0 → web3-6.20.0}/web3/tools/pytest_ethereum/__init__.py +0 -0
  277. {web3-6.18.0 → web3-6.20.0}/web3/tools/pytest_ethereum/_utils.py +0 -0
  278. {web3-6.18.0 → web3-6.20.0}/web3/tools/pytest_ethereum/exceptions.py +0 -0
  279. {web3-6.18.0 → web3-6.20.0}/web3/tools/pytest_ethereum/linker.py +0 -0
  280. {web3-6.18.0 → web3-6.20.0}/web3/tools/pytest_ethereum/plugins.py +0 -0
  281. {web3-6.18.0 → web3-6.20.0}/web3/tracing.py +0 -0
  282. {web3-6.18.0 → web3-6.20.0}/web3/utils/__init__.py +0 -0
  283. {web3-6.18.0 → web3-6.20.0}/web3/utils/abi.py +0 -0
  284. {web3-6.18.0 → web3-6.20.0}/web3/utils/address.py +0 -0
  285. {web3-6.18.0 → web3-6.20.0}/web3/utils/async_exception_handling.py +0 -0
  286. {web3-6.18.0 → web3-6.20.0}/web3/utils/caching.py +0 -0
  287. {web3-6.18.0 → web3-6.20.0}/web3/utils/exception_handling.py +0 -0
  288. {web3-6.18.0 → web3-6.20.0}/web3.egg-info/SOURCES.txt +0 -0
  289. {web3-6.18.0 → web3-6.20.0}/web3.egg-info/dependency_links.txt +0 -0
  290. {web3-6.18.0 → web3-6.20.0}/web3.egg-info/entry_points.txt +0 -0
  291. {web3-6.18.0 → web3-6.20.0}/web3.egg-info/not-zip-safe +0 -0
  292. {web3-6.18.0 → web3-6.20.0}/web3.egg-info/requires.txt +0 -0
  293. {web3-6.18.0 → web3-6.20.0}/web3.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: web3
3
- Version: 6.18.0
3
+ Version: 6.20.0
4
4
  Summary: web3.py
5
5
  Home-page: https://github.com/ethereum/web3.py
6
6
  Author: The Ethereum Foundation
@@ -34,7 +34,7 @@ log_date_format = "%m-%d %H:%M:%S"
34
34
  [tool.towncrier]
35
35
  # Read https://github.com/ethereum/web3.py/blob/main/newsfragments/README.md for instructions
36
36
  package = "web3"
37
- filename = "docs/releases.rst"
37
+ filename = "docs/release_notes.rst"
38
38
  directory = "newsfragments"
39
39
  underlines = ["-", "~", "^"]
40
40
  title_format = "web3.py v{version} ({project_date})"
@@ -51,7 +51,7 @@ with open("./README.md") as readme:
51
51
  setup(
52
52
  name="web3",
53
53
  # *IMPORTANT*: Don't manually change the version here. Use the 'bumpversion' utility.
54
- version="6.18.0",
54
+ version="6.20.0",
55
55
  description="""web3.py""",
56
56
  long_description_content_type="text/markdown",
57
57
  long_description=long_description,
@@ -1,6 +1,15 @@
1
1
  from eth_account import Account # noqa: E402
2
2
  import sys
3
3
 
4
+ from web3.providers import (
5
+ AsyncBaseProvider,
6
+ AutoProvider,
7
+ BaseProvider,
8
+ JSONBaseProvider,
9
+ PersistentConnectionProvider,
10
+ )
11
+
12
+
4
13
  if sys.version_info.major == 3 and sys.version_info.minor < 8:
5
14
  import pkg_resources
6
15
 
@@ -19,6 +28,7 @@ from web3.providers.async_rpc import ( # noqa: E402
19
28
  AsyncHTTPProvider,
20
29
  )
21
30
  from web3.providers.eth_tester import ( # noqa: E402
31
+ AsyncEthereumTesterProvider,
22
32
  EthereumTesterProvider,
23
33
  )
24
34
  from web3.providers.ipc import ( # noqa: E402
@@ -35,13 +45,21 @@ from web3.providers.websocket import ( # noqa: E402
35
45
 
36
46
  __all__ = [
37
47
  "__version__",
48
+ "Account",
49
+ # web3:
38
50
  "AsyncWeb3",
39
51
  "Web3",
52
+ # providers:
53
+ "AsyncBaseProvider",
54
+ "AsyncEthereumTesterProvider",
55
+ "AsyncHTTPProvider",
56
+ "AutoProvider",
57
+ "BaseProvider",
58
+ "EthereumTesterProvider",
40
59
  "HTTPProvider",
41
60
  "IPCProvider",
61
+ "JSONBaseProvider",
62
+ "PersistentConnectionProvider",
42
63
  "WebsocketProvider",
43
64
  "WebsocketProviderV2",
44
- "EthereumTesterProvider",
45
- "Account",
46
- "AsyncHTTPProvider",
47
65
  ]
@@ -193,6 +193,12 @@ class WebsocketMessageStreamMock:
193
193
  self.messages = deque(messages) if messages else deque()
194
194
  self.raise_exception = raise_exception
195
195
 
196
+ def __await__(self) -> Generator[Any, Any, "Self"]:
197
+ async def __async_init__() -> "Self":
198
+ return self
199
+
200
+ return __async_init__().__await__()
201
+
196
202
  def __aiter__(self) -> "Self":
197
203
  return self
198
204
 
@@ -205,6 +211,13 @@ class WebsocketMessageStreamMock:
205
211
 
206
212
  return self.messages.popleft()
207
213
 
214
+ @staticmethod
215
+ async def pong() -> Literal[False]:
216
+ return False
217
+
218
+ async def connect(self) -> None:
219
+ pass
220
+
208
221
  async def send(self, data: bytes) -> None:
209
222
  pass
210
223
 
@@ -1,6 +1,7 @@
1
1
  import datetime
2
2
  import time
3
3
  from typing import (
4
+ TYPE_CHECKING,
4
5
  Any,
5
6
  Dict,
6
7
  Optional,
@@ -15,6 +16,9 @@ from web3.types import (
15
16
  BlockData,
16
17
  )
17
18
 
19
+ if TYPE_CHECKING:
20
+ import asyncio
21
+
18
22
 
19
23
  class Web3Exception(Exception):
20
24
  """
@@ -341,7 +345,21 @@ class BadResponseFormat(Web3Exception):
341
345
  Raised when a JSON-RPC response comes back in an unexpected format
342
346
  """
343
347
 
344
- pass
348
+
349
+ class TaskNotRunning(Web3Exception):
350
+ """
351
+ Used to signal between asyncio contexts that a task that is being awaited
352
+ is not currently running.
353
+ """
354
+
355
+ def __init__(
356
+ self, task: "asyncio.Task[Any]", message: Optional[str] = None
357
+ ) -> None:
358
+ self.task = task
359
+ if message is None:
360
+ message = f"Task {task} is not running."
361
+ self.message = message
362
+ super().__init__(message)
345
363
 
346
364
 
347
365
  class MethodUnavailable(Web3Exception):
@@ -573,12 +573,9 @@ class _PersistentConnectionWeb3(AsyncWeb3):
573
573
 
574
574
  # async for w3 in w3.persistent_websocket(provider)
575
575
  async def __aiter__(self) -> AsyncIterator[Self]:
576
- if not await self.provider.is_connected():
577
- await self.provider.connect()
578
-
576
+ provider = self.provider
579
577
  while True:
580
- try:
581
- yield self
582
- except Exception:
583
- # provider should handle connection / reconnection
584
- continue
578
+ await provider.connect()
579
+ yield self
580
+ provider.logger.error("Connection interrupted, attempting to reconnect...")
581
+ await provider.disconnect()
@@ -35,6 +35,7 @@ from web3.exceptions import (
35
35
  BadResponseFormat,
36
36
  MethodUnavailable,
37
37
  ProviderConnectionError,
38
+ TaskNotRunning,
38
39
  )
39
40
  from web3.middleware import (
40
41
  abi_middleware,
@@ -377,14 +378,24 @@ class RequestManager:
377
378
  raise ProviderConnectionError("No listener found for websocket connection.")
378
379
 
379
380
  while True:
380
- response = await self._request_processor.pop_raw_response(subscription=True)
381
- if (
382
- response is not None
383
- and response.get("params", {}).get("subscription")
384
- in self._request_processor.active_subscriptions
385
- ):
386
- # if response is an active subscription response, process it
387
- yield await self._process_ws_response(response)
381
+ try:
382
+ response = await self._request_processor.pop_raw_response(
383
+ subscription=True
384
+ )
385
+ if (
386
+ response is not None
387
+ and response.get("params", {}).get("subscription")
388
+ in self._request_processor.active_subscriptions
389
+ ):
390
+ # if response is an active subscription response, process it
391
+ yield await self._process_ws_response(response)
392
+ except TaskNotRunning:
393
+ self._provider._handle_listener_task_exceptions()
394
+ self.logger.error(
395
+ "Message listener background task has stopped unexpectedly. "
396
+ "Stopping message stream."
397
+ )
398
+ raise StopAsyncIteration
388
399
 
389
400
  async def _process_ws_response(self, response: RPCResponse) -> RPCResponse:
390
401
  provider = cast(PersistentConnectionProvider, self._provider)
@@ -8,6 +8,10 @@ from .base import (
8
8
  BaseProvider,
9
9
  JSONBaseProvider,
10
10
  )
11
+ from .eth_tester import (
12
+ AsyncEthereumTesterProvider,
13
+ EthereumTesterProvider,
14
+ )
11
15
  from .ipc import (
12
16
  IPCProvider,
13
17
  )
@@ -24,3 +28,18 @@ from .persistent import (
24
28
  from .auto import (
25
29
  AutoProvider,
26
30
  )
31
+
32
+ __all__ = [
33
+ "AsyncBaseProvider",
34
+ "AsyncEthereumTesterProvider",
35
+ "AsyncHTTPProvider",
36
+ "AutoProvider",
37
+ "BaseProvider",
38
+ "EthereumTesterProvider",
39
+ "HTTPProvider",
40
+ "IPCProvider",
41
+ "JSONBaseProvider",
42
+ "PersistentConnectionProvider",
43
+ "WebsocketProvider",
44
+ "WebsocketProviderV2",
45
+ ]
@@ -0,0 +1,214 @@
1
+ from abc import (
2
+ ABC,
3
+ )
4
+ import asyncio
5
+ import logging
6
+ from typing import (
7
+ Optional,
8
+ )
9
+
10
+ from websockets import (
11
+ ConnectionClosed,
12
+ WebSocketClientProtocol,
13
+ WebSocketException,
14
+ )
15
+
16
+ from web3._utils.caching import (
17
+ generate_cache_key,
18
+ )
19
+ from web3.exceptions import (
20
+ ProviderConnectionError,
21
+ TaskNotRunning,
22
+ TimeExhausted,
23
+ )
24
+ from web3.providers.async_base import (
25
+ AsyncJSONBaseProvider,
26
+ )
27
+ from web3.providers.websocket.request_processor import (
28
+ RequestProcessor,
29
+ )
30
+ from web3.types import (
31
+ RPCId,
32
+ RPCResponse,
33
+ )
34
+
35
+ DEFAULT_PERSISTENT_CONNECTION_TIMEOUT = 50.0
36
+
37
+
38
+ class PersistentConnectionProvider(AsyncJSONBaseProvider, ABC):
39
+ logger = logging.getLogger("web3.providers.PersistentConnectionProvider")
40
+ has_persistent_connection = True
41
+ endpoint_uri: Optional[str] = None
42
+
43
+ _max_connection_retries: int = 5
44
+
45
+ _ws: Optional[WebSocketClientProtocol] = None
46
+ _request_processor: RequestProcessor
47
+ _message_listener_task: Optional["asyncio.Task[None]"] = None
48
+ _listen_event: asyncio.Event = asyncio.Event()
49
+
50
+ def __init__(
51
+ self,
52
+ request_timeout: float = DEFAULT_PERSISTENT_CONNECTION_TIMEOUT,
53
+ subscription_response_queue_size: int = 500,
54
+ request_information_cache_size: int = 500,
55
+ silence_listener_task_exceptions: bool = False,
56
+ ) -> None:
57
+ super().__init__()
58
+ self._request_processor = RequestProcessor(
59
+ self,
60
+ subscription_response_queue_size=subscription_response_queue_size,
61
+ request_information_cache_size=request_information_cache_size,
62
+ )
63
+ self.request_timeout = request_timeout
64
+ self.silence_listener_task_exceptions = silence_listener_task_exceptions
65
+
66
+ async def connect(self) -> None:
67
+ _connection_attempts = 0
68
+ _backoff_rate_change = 1.75
69
+ _backoff_time = 1.75
70
+
71
+ while _connection_attempts != self._max_connection_retries:
72
+ try:
73
+ _connection_attempts += 1
74
+ self.logger.info(f"Connecting to: {self.endpoint_uri}")
75
+ await self._provider_specific_connect()
76
+ self._message_listener_task = asyncio.create_task(
77
+ self._message_listener()
78
+ )
79
+ self._message_listener_task.add_done_callback(
80
+ self._message_listener_callback
81
+ )
82
+ self.logger.info(f"Successfully connected to: {self.endpoint_uri}")
83
+ break
84
+ except (WebSocketException, OSError) as e:
85
+ if _connection_attempts == self._max_connection_retries:
86
+ raise ProviderConnectionError(
87
+ f"Could not connect to: {self.endpoint_uri}. "
88
+ f"Retries exceeded max of {self._max_connection_retries}."
89
+ ) from e
90
+ self.logger.info(
91
+ f"Could not connect to: {self.endpoint_uri}. "
92
+ f"Retrying in {round(_backoff_time, 1)} seconds.",
93
+ exc_info=True,
94
+ )
95
+ await asyncio.sleep(_backoff_time)
96
+ _backoff_time *= _backoff_rate_change
97
+
98
+ async def disconnect(self) -> None:
99
+ try:
100
+ if self._message_listener_task:
101
+ self._message_listener_task.cancel()
102
+ await self._message_listener_task
103
+ except (asyncio.CancelledError, StopAsyncIteration, ConnectionClosed):
104
+ pass
105
+ finally:
106
+ self._message_listener_task = None
107
+ self.logger.info("Message listener background task successfully shut down.")
108
+
109
+ await self._provider_specific_disconnect()
110
+ self._request_processor.clear_caches()
111
+ self.logger.info(f"Successfully disconnected from: {self.endpoint_uri}")
112
+
113
+ # -- private methods -- #
114
+
115
+ async def _provider_specific_connect(self) -> None:
116
+ raise NotImplementedError("Must be implemented by subclasses")
117
+
118
+ async def _provider_specific_disconnect(self) -> None:
119
+ raise NotImplementedError("Must be implemented by subclasses")
120
+
121
+ async def _provider_specific_message_listener(self) -> None:
122
+ raise NotImplementedError("Must be implemented by subclasses")
123
+
124
+ def _message_listener_callback(
125
+ self, message_listener_task: "asyncio.Task[None]"
126
+ ) -> None:
127
+ # Puts a `TaskNotRunning` in the queue to signal the end of the listener task
128
+ # to any running subscription streams that are awaiting a response.
129
+ self._request_processor._subscription_response_queue.put_nowait(
130
+ TaskNotRunning(message_listener_task)
131
+ )
132
+
133
+ async def _message_listener(self) -> None:
134
+ self.logger.info(
135
+ f"{self.__class__.__qualname__} listener background task started. Storing "
136
+ "all messages in appropriate request processor queues / caches to be "
137
+ "processed."
138
+ )
139
+ while True:
140
+ # the use of sleep(0) seems to be the most efficient way to yield control
141
+ # back to the event loop to share the loop with other tasks.
142
+ await asyncio.sleep(0)
143
+ try:
144
+ await self._provider_specific_message_listener()
145
+ except Exception as e:
146
+ if not self.silence_listener_task_exceptions:
147
+ raise e
148
+ else:
149
+ self._error_log_listener_task_exception(e)
150
+
151
+ def _error_log_listener_task_exception(self, e: Exception) -> None:
152
+ """
153
+ When silencing listener task exceptions, this method is used to log the
154
+ exception and keep the listener task alive. Override this method to fine-tune
155
+ error logging behavior for the implementation class.
156
+ """
157
+ self.logger.error(
158
+ "Exception caught in listener, error logging and keeping "
159
+ "listener background task alive."
160
+ f"\n error={e.__class__.__name__}: {e}"
161
+ )
162
+
163
+ def _handle_listener_task_exceptions(self) -> None:
164
+ """
165
+ Should be called every time a `PersistentConnectionProvider` is polling for
166
+ messages in the main loop. If the message listener task has completed and an
167
+ exception was recorded, raise the exception in the main loop.
168
+ """
169
+ msg_listener_task = getattr(self, "_message_listener_task", None)
170
+ if (
171
+ msg_listener_task
172
+ and msg_listener_task.done()
173
+ and msg_listener_task.exception()
174
+ ):
175
+ raise msg_listener_task.exception()
176
+
177
+ async def _get_response_for_request_id(
178
+ self, request_id: RPCId, timeout: Optional[float] = None
179
+ ) -> RPCResponse:
180
+ if timeout is None:
181
+ timeout = self.request_timeout
182
+
183
+ async def _match_response_id_to_request_id() -> RPCResponse:
184
+ request_cache_key = generate_cache_key(request_id)
185
+
186
+ while True:
187
+ # check if an exception was recorded in the listener task and raise it
188
+ # in the main loop if so
189
+ self._handle_listener_task_exceptions()
190
+
191
+ if request_cache_key in self._request_processor._request_response_cache:
192
+ self.logger.debug(
193
+ f"Popping response for id {request_id} from cache."
194
+ )
195
+ popped_response = await self._request_processor.pop_raw_response(
196
+ cache_key=request_cache_key,
197
+ )
198
+ return popped_response
199
+ else:
200
+ await asyncio.sleep(0)
201
+
202
+ try:
203
+ # Add the request timeout around the while loop that checks the request
204
+ # cache. If the request is not in the cache within the request_timeout,
205
+ # raise ``TimeExhausted``.
206
+ return await asyncio.wait_for(_match_response_id_to_request_id(), timeout)
207
+ except asyncio.TimeoutError:
208
+ raise TimeExhausted(
209
+ f"Timed out waiting for response with request id `{request_id}` after "
210
+ f"{self.request_timeout} second(s). This may be due to the provider "
211
+ "not returning a response with the same id that was sent in the "
212
+ "request or an exception raised during the request was caught and "
213
+ "allowed to continue."
214
+ )
@@ -2,19 +2,26 @@ import asyncio
2
2
  from copy import (
3
3
  copy,
4
4
  )
5
+ import sys
5
6
  from typing import (
6
7
  TYPE_CHECKING,
7
8
  Any,
8
9
  Callable,
9
10
  Dict,
11
+ Generic,
10
12
  Optional,
11
13
  Tuple,
14
+ TypeVar,
15
+ Union,
12
16
  )
13
17
 
14
18
  from web3._utils.caching import (
15
19
  RequestInformation,
16
20
  generate_cache_key,
17
21
  )
22
+ from web3.exceptions import (
23
+ TaskNotRunning,
24
+ )
18
25
  from web3.types import (
19
26
  RPCEndpoint,
20
27
  RPCResponse,
@@ -28,6 +35,34 @@ if TYPE_CHECKING:
28
35
  PersistentConnectionProvider,
29
36
  )
30
37
 
38
+ T = TypeVar("T")
39
+
40
+ # TODO: This is an ugly hack for python 3.8. Remove this after we drop support for it
41
+ # and use `asyncio.Queue[T]` type directly in the `TaskReliantQueue` class.
42
+ if sys.version_info >= (3, 9):
43
+
44
+ class _TaskReliantQueue(asyncio.Queue[T], Generic[T]):
45
+ pass
46
+
47
+ else:
48
+
49
+ class _TaskReliantQueue(asyncio.Queue, Generic[T]): # type: ignore
50
+ pass
51
+
52
+
53
+ class TaskReliantQueue(_TaskReliantQueue[T]):
54
+ """
55
+ A queue that relies on a task to be running to process items in the queue.
56
+ """
57
+
58
+ async def get(self) -> T:
59
+ item = await super().get()
60
+ if isinstance(item, Exception):
61
+ # if the item is an exception, raise it so the task can handle this case
62
+ # more gracefully
63
+ raise item
64
+ return item
65
+
31
66
 
32
67
  class RequestProcessor:
33
68
  _subscription_queue_synced_with_ws_stream: bool = False
@@ -41,9 +76,9 @@ class RequestProcessor:
41
76
  self._provider = provider
42
77
 
43
78
  self._request_response_cache: SimpleCache = SimpleCache(500)
44
- self._subscription_response_queue: asyncio.Queue[RPCResponse] = asyncio.Queue(
45
- maxsize=subscription_response_queue_size
46
- )
79
+ self._subscription_response_queue: TaskReliantQueue[
80
+ Union[RPCResponse, TaskNotRunning]
81
+ ] = TaskReliantQueue(maxsize=subscription_response_queue_size)
47
82
  self._request_information_cache: SimpleCache = SimpleCache(
48
83
  request_information_cache_size
49
84
  )
@@ -203,7 +238,7 @@ class RequestProcessor:
203
238
  ) -> None:
204
239
  if subscription:
205
240
  if self._subscription_response_queue.full():
206
- self._provider.logger.info(
241
+ self._provider.logger.debug(
207
242
  "Subscription queue is full. Waiting for provider to consume "
208
243
  "messages before caching."
209
244
  )
@@ -276,6 +311,6 @@ class RequestProcessor:
276
311
 
277
312
  self._request_information_cache.clear()
278
313
  self._request_response_cache.clear()
279
- self._subscription_response_queue = asyncio.Queue(
314
+ self._subscription_response_queue = TaskReliantQueue(
280
315
  maxsize=self._subscription_response_queue.maxsize
281
316
  )
@@ -0,0 +1,146 @@
1
+ import asyncio
2
+ import json
3
+ import logging
4
+ import os
5
+ from typing import (
6
+ Any,
7
+ Dict,
8
+ Optional,
9
+ Union,
10
+ )
11
+
12
+ from eth_typing import (
13
+ URI,
14
+ )
15
+ from toolz import (
16
+ merge,
17
+ )
18
+ from websockets.client import (
19
+ connect,
20
+ )
21
+ from websockets.exceptions import (
22
+ WebSocketException,
23
+ )
24
+
25
+ from web3.exceptions import (
26
+ ProviderConnectionError,
27
+ Web3ValidationError,
28
+ )
29
+ from web3.providers.persistent import (
30
+ PersistentConnectionProvider,
31
+ )
32
+ from web3.types import (
33
+ RPCEndpoint,
34
+ RPCResponse,
35
+ )
36
+
37
+ DEFAULT_PING_INTERVAL = 30 # 30 seconds
38
+ DEFAULT_PING_TIMEOUT = 300 # 5 minutes
39
+
40
+ VALID_WEBSOCKET_URI_PREFIXES = {"ws://", "wss://"}
41
+ RESTRICTED_WEBSOCKET_KWARGS = {"uri", "loop"}
42
+ DEFAULT_WEBSOCKET_KWARGS = {
43
+ # set how long to wait between pings from the server
44
+ "ping_interval": DEFAULT_PING_INTERVAL,
45
+ # set how long to wait without a pong response before closing the connection
46
+ "ping_timeout": DEFAULT_PING_TIMEOUT,
47
+ }
48
+
49
+
50
+ def get_default_endpoint() -> URI:
51
+ return URI(os.environ.get("WEB3_WS_PROVIDER_URI", "ws://127.0.0.1:8546"))
52
+
53
+
54
+ class WebsocketProviderV2(PersistentConnectionProvider):
55
+ logger = logging.getLogger("web3.providers.WebsocketProviderV2")
56
+ is_async: bool = True
57
+
58
+ def __init__(
59
+ self,
60
+ endpoint_uri: Optional[Union[URI, str]] = None,
61
+ websocket_kwargs: Optional[Dict[str, Any]] = None,
62
+ silence_listener_task_exceptions: bool = False,
63
+ # `PersistentConnectionProvider` kwargs can be passed through
64
+ **kwargs: Any,
65
+ ) -> None:
66
+ self.endpoint_uri = (
67
+ URI(endpoint_uri) if endpoint_uri is not None else get_default_endpoint()
68
+ )
69
+
70
+ if not any(
71
+ self.endpoint_uri.startswith(prefix)
72
+ for prefix in VALID_WEBSOCKET_URI_PREFIXES
73
+ ):
74
+ raise Web3ValidationError(
75
+ "WebSocket endpoint uri must begin with 'ws://' or 'wss://': "
76
+ f"{self.endpoint_uri}"
77
+ )
78
+
79
+ if websocket_kwargs is not None:
80
+ found_restricted_keys = set(websocket_kwargs).intersection(
81
+ RESTRICTED_WEBSOCKET_KWARGS
82
+ )
83
+ if found_restricted_keys:
84
+ raise Web3ValidationError(
85
+ "Found restricted keys for websocket_kwargs: "
86
+ f"{found_restricted_keys}."
87
+ )
88
+
89
+ self.websocket_kwargs = merge(DEFAULT_WEBSOCKET_KWARGS, websocket_kwargs or {})
90
+
91
+ super().__init__(
92
+ silence_listener_task_exceptions=silence_listener_task_exceptions, **kwargs
93
+ )
94
+
95
+ def __str__(self) -> str:
96
+ return f"WebSocket connection: {self.endpoint_uri}"
97
+
98
+ async def is_connected(self, show_traceback: bool = False) -> bool:
99
+ if not self._ws:
100
+ return False
101
+
102
+ try:
103
+ await self._ws.pong()
104
+ return True
105
+
106
+ except WebSocketException as e:
107
+ if show_traceback:
108
+ raise ProviderConnectionError(
109
+ f"Error connecting to endpoint: '{self.endpoint_uri}'"
110
+ ) from e
111
+ return False
112
+
113
+ async def _provider_specific_connect(self) -> None:
114
+ self._ws = await connect(self.endpoint_uri, **self.websocket_kwargs)
115
+
116
+ async def _provider_specific_disconnect(self) -> None:
117
+ if self._ws is not None and not self._ws.closed:
118
+ await self._ws.close()
119
+ self._ws = None
120
+
121
+ async def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse:
122
+ request_data = self.encode_rpc_request(method, params)
123
+
124
+ if self._ws is None:
125
+ raise ProviderConnectionError(
126
+ "Connection to websocket has not been initiated for the provider."
127
+ )
128
+
129
+ await asyncio.wait_for(
130
+ self._ws.send(request_data), timeout=self.request_timeout
131
+ )
132
+
133
+ current_request_id = json.loads(request_data)["id"]
134
+ response = await self._get_response_for_request_id(current_request_id)
135
+
136
+ return response
137
+
138
+ async def _provider_specific_message_listener(self) -> None:
139
+ async for raw_message in self._ws:
140
+ await asyncio.sleep(0)
141
+
142
+ response = json.loads(raw_message)
143
+ subscription = response.get("method") == "eth_subscription"
144
+ await self._request_processor.cache_raw_response(
145
+ response, subscription=subscription
146
+ )