From 5cf1fb7b84b590baa9b98a79d04e67b1554349a5 Mon Sep 17 00:00:00 2001 From: Mikko Ohtamaa Date: Thu, 10 Mar 2022 00:21:07 +0100 Subject: [PATCH 01/15] An example how to manage local private key using env (#2380) * An example how to manage local private key using env How to manage private keys using environment variable, how to generate them, and some security warnings. * Fix extra period Inserted by the editor? * add content tweaks and newsfragment Co-authored-by: Marc Garreau --- docs/web3.eth.account.rst | 55 +++++++++++++++++++++++++++++++++++++- newsfragments/2380.doc.rst | 1 + 2 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 newsfragments/2380.doc.rst diff --git a/docs/web3.eth.account.rst b/docs/web3.eth.account.rst index 60a25839f2..a331c3b733 100644 --- a/docs/web3.eth.account.rst +++ b/docs/web3.eth.account.rst @@ -39,7 +39,7 @@ Hosted Private Key not your Ether" in the wise words of Andreas Antonopoulos. Some Common Uses for Local Private Keys -------------------------------------------- +--------------------------------------- A very common reason to work with local private keys is to interact with a hosted node. @@ -55,6 +55,59 @@ Using private keys usually involves ``w3.eth.account`` in one way or another. Re or see a full list of things you can do in the docs for :class:`eth_account.Account `. +Read a private key from an environment variable +----------------------------------------------- + +In this example we pass the private key to our Python application in an +`environment variable `_. +This private key is then added to the transaction signing keychain +with ``Signing`` middleware. + +If unfamiliar, note that you can `export your private keys from Metamask and other wallets `_. + +.. warning :: + + - **Never** share your private keys. + - **Never** put your private keys in source code. + - **Never** commit private keys to a Git repository. + +Example ``account_test_script.py`` + +.. code-block:: python + + import os + from eth_account import Account + from eth_account.signers.local import LocalAccount + from web3.auto import w3 + from web3.middleware import construct_sign_and_send_raw_middleware + + private_key = os.environ.get("PRIVATE_KEY") + assert private_key is not None, "You must set PRIVATE_KEY environment variable" + assert private_key.startswith("0x"), "Private key must start with 0x hex prefix" + + account: LocalAccount = Account.from_key(private_key) + w3.middleware_onion.add(construct_sign_and_send_raw_middleware(account)) + + print(f"Your hot wallet address is {account.address}") + +Example how to run this in UNIX shell: + +.. code-block:: shell + + # Generate a new 256-bit random integer using openssl UNIX command that acts as a private key. + # You can also do: + # python -c "from web3 import Web3; w3 = Web3(); acc = w3.eth.account.create(); print(f'private key={w3.toHex(acc.key)}, account={acc.address}')" + # Store this in a safe place, like in your password manager. + export PRIVATE_KEY=0x`openssl rand -hex 32` + + # Run our script + python account_test_script.py + +This will print:: + + Your hot wallet address is 0x27C8F899bb69E1501BBB96d09d7477a2a7518918 + + .. _extract_geth_pk: Extract private key from geth keyfile diff --git a/newsfragments/2380.doc.rst b/newsfragments/2380.doc.rst new file mode 100644 index 0000000000..6baf4bb2d6 --- /dev/null +++ b/newsfragments/2380.doc.rst @@ -0,0 +1 @@ +Document reading private keys from environment variables From b9d99fc40114938d39b26e726ddc09a8b4307998 Mon Sep 17 00:00:00 2001 From: Harmouch101 Date: Wed, 2 Mar 2022 20:19:05 +0200 Subject: [PATCH 02/15] :broom: clean python2 syntax. Signed-off-by: Harmouch101 --- docs/conf.py | 16 ++++++++-------- docs/examples.rst | 15 +++++---------- ens/main.py | 15 +++++++-------- ens/utils.py | 2 +- tests/core/contracts/conftest.py | 2 +- .../test_contract_ambiguous_functions.py | 2 +- .../contracts/test_contract_call_interface.py | 2 +- tests/core/pm-module/test_ens_integration.py | 2 +- tests/core/providers/test_auto_provider.py | 8 ++++---- tests/core/utilities/test_formatters.py | 2 +- tests/ens/conftest.py | 2 +- web3/_utils/blocks.py | 2 +- web3/_utils/decorators.py | 2 +- web3/_utils/empty.py | 3 --- web3/_utils/encoding.py | 6 +++--- web3/_utils/ens.py | 2 +- web3/_utils/method_formatters.py | 6 ++---- web3/_utils/module_testing/eth_module.py | 4 ++-- web3/_utils/normalizers.py | 17 ++++++++--------- web3/_utils/rpc_abi.py | 2 +- web3/_utils/transactions.py | 2 +- web3/_utils/validation.py | 2 +- web3/auto/infura/endpoints.py | 8 ++++---- web3/contract.py | 12 ++++++------ web3/datastructures.py | 10 +++------- web3/exceptions.py | 8 +++++--- web3/main.py | 11 +++++------ web3/manager.py | 4 ++-- web3/middleware/validation.py | 17 ++++++----------- web3/providers/async_rpc.py | 8 +++----- web3/providers/auto.py | 6 ++---- web3/providers/ipc.py | 8 +++++--- web3/providers/rpc.py | 8 +++----- web3/providers/websocket.py | 4 ++-- web3/tools/pytest_ethereum/linker.py | 7 +++---- 35 files changed, 101 insertions(+), 126 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index fd50c0c203..a4f5ec9031 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -52,8 +52,8 @@ master_doc = 'index' # General information about the project. -project = u'Web3.py' -copyright = u'2018, Piper Merriam, Jason Carver' +project = 'Web3.py' +copyright = '2018, Piper Merriam, Jason Carver' __version__ = setup_version # The version info for the project you're documenting, acts as replacement for @@ -222,8 +222,8 @@ # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ - ('index', 'Populus.tex', u'Populus Documentation', - u'Piper Merriam', 'manual'), + ('index', 'Populus.tex', 'Populus Documentation', + 'Piper Merriam', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of @@ -252,8 +252,8 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'web3', u'Web3.py Documentation', - [u'Piper Merriam'], 1) + ('index', 'web3', 'Web3.py Documentation', + ['Piper Merriam'], 1) ] # If true, show URL addresses after external links. @@ -266,8 +266,8 @@ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'Web3.py', u'Web3.py Documentation', - u'Piper Merriam', 'Web3.py', 'Backend agnostic Ethereum client interactions.', + ('index', 'Web3.py', 'Web3.py Documentation', + 'Piper Merriam', 'Web3.py', 'Backend agnostic Ethereum client interactions.', 'Miscellaneous'), ] diff --git a/docs/examples.rst b/docs/examples.rst index e54ed70d43..83d41a6ee7 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -992,7 +992,7 @@ The script can be run with: ``python ./eventscanner.py `` # from our in-memory cache block_when = get_block_when(block_number) - logger.debug("Processing event %s, block:%d count:%d", evt["event"], evt["blockNumber"]) + logger.debug(f"Processing event {evt["event"]}, block: {evt["blockNumber"]} count: {evt["blockNumber"]}") processed = self.state.process_event(block_when, evt) all_processed.append(processed) @@ -1064,8 +1064,8 @@ The script can be run with: ``python ./eventscanner.py `` # Print some diagnostics to logs to try to fiddle with real world JSON-RPC API performance estimated_end_block = current_block + chunk_size logger.debug( - "Scanning token transfers for blocks: %d - %d, chunk size %d, last chunk scan took %f, last logs found %d", - current_block, estimated_end_block, chunk_size, last_scan_duration, last_logs_found) + f"Scanning token transfers for blocks: {current_block} - {estimated_end_block}, chunk size {chunk_size}, last chunk scan took {last_scan_duration}, last logs found {last_logs_found}" + ) start = time.time() actual_end_block, end_block_timestamp, new_entries = self.scan_chunk(current_block, estimated_end_block) @@ -1116,12 +1116,7 @@ The script can be run with: ``python ./eventscanner.py `` if i < retries - 1: # Give some more verbose info than the default middleware logger.warning( - "Retrying events for block range %d - %d (%d) failed with %s, retrying in %s seconds", - start_block, - end_block, - end_block-start_block, - e, - delay) + f"Retrying events for block range {start_block} - {end_block} ({end_block-start_block}) failed with {e} , retrying in {delay} seconds") # Decrease the `eth_getBlocks` range end_block = start_block + ((end_block - start_block) // 2) # Let the JSON-RPC to recover e.g. from restart @@ -1175,7 +1170,7 @@ The script can be run with: ``python ./eventscanner.py `` toBlock=to_block ) - logger.debug("Querying eth_getLogs with the following parameters: %s", event_filter_params) + logger.debug(f"Querying eth_getLogs with the following parameters: {event_filter_params}") # Call JSON-RPC API on your Ethereum node. # get_logs() returns raw AttributedDict entries diff --git a/ens/main.py b/ens/main.py index 928a3106a4..d38fc3cedb 100644 --- a/ens/main.py +++ b/ens/main.py @@ -227,10 +227,10 @@ def setup_name( address = resolved elif resolved and address != resolved and resolved != EMPTY_ADDR_HEX: raise AddressMismatch( - "Could not set address %r to point to name, because the name resolves to %r. " - "To change the name for an existing address, call setup_address() first." % ( - address, resolved - ) + f"Could not set address {address!r} to point to name, " + f"because the name resolves to {resolved!r}. " + "To change the name for an existing address, call " + "setup_address() first." ) if is_none_or_zero_address(address): address = self.owner(name) @@ -335,9 +335,8 @@ def _assert_control(self, account: ChecksumAddress, name: str, parent_owned: Optional[str] = None) -> None: if not address_in(account, self.w3.eth.accounts): raise UnauthorizedError( - "in order to modify %r, you must control account %r, which owns %r" % ( - name, account, parent_owned or name - ) + f"in order to modify {name!r}, you must control account" + f" {account!r}, which owns {parent_owned or name!r}" ) def _first_owner(self, name: str) -> Tuple[Optional[ChecksumAddress], Sequence[str], str]: @@ -374,7 +373,7 @@ def _claim_ownership( label_to_hash(label), owner ).transact(transact) - owned = "%s.%s" % (label, owned) + owned = f"{label}.{owned}" def _set_resolver( self, diff --git a/ens/utils.py b/ens/utils.py index 43c223b5e2..33fda9ee29 100644 --- a/ens/utils.py +++ b/ens/utils.py @@ -140,7 +140,7 @@ def sha3_text(val: Union[str, bytes]) -> HexBytes: def label_to_hash(label: str) -> HexBytes: label = normalize_name(label) if '.' in label: - raise ValueError("Cannot generate hash for label %r with a '.'" % label) + raise ValueError(f"Cannot generate hash for label {label!r} with a '.'") return Web3().keccak(text=label) diff --git a/tests/core/contracts/conftest.py b/tests/core/contracts/conftest.py index 2941572be8..91d01d5875 100644 --- a/tests/core/contracts/conftest.py +++ b/tests/core/contracts/conftest.py @@ -1010,7 +1010,7 @@ def invoke_contract(api_call_desig='call', tx_params={}): allowable_call_desig = ['call', 'transact', 'estimateGas', 'buildTransaction'] if api_call_desig not in allowable_call_desig: - raise ValueError("allowable_invoke_method must be one of: %s" % allowable_call_desig) + raise ValueError(f"allowable_invoke_method must be one of: {allowable_call_desig}") function = contract.functions[contract_function] result = getattr(function(*func_args, **func_kwargs), api_call_desig)(tx_params) diff --git a/tests/core/contracts/test_contract_ambiguous_functions.py b/tests/core/contracts/test_contract_ambiguous_functions.py index 69e15e4ff6..8f6c0b5ae2 100644 --- a/tests/core/contracts/test_contract_ambiguous_functions.py +++ b/tests/core/contracts/test_contract_ambiguous_functions.py @@ -153,7 +153,7 @@ def test_find_or_get_functions_by_type(w3, method, args, repr_func, expected): ( 'get_function_by_selector', (b'\x00' * (4 + 1), ), - r'expected value of size 4 bytes. Got: %s bytes' % (4 + 1), + f'expected value of size 4 bytes. Got: {(4 + 1)} bytes', ValueError ), ( diff --git a/tests/core/contracts/test_contract_call_interface.py b/tests/core/contracts/test_contract_call_interface.py index 6b1c1809b8..4e67c7d1cd 100644 --- a/tests/core/contracts/test_contract_call_interface.py +++ b/tests/core/contracts/test_contract_call_interface.py @@ -726,7 +726,7 @@ def test_reflect_fixed_value(fixed_reflection_contract, function, value): ('reflect_short_u', Decimal('0.01'), "no matching argument types"), ( 'reflect_short_u', - Decimal('1e-%d' % (DEFAULT_DECIMALS + 1)), + Decimal(f'1e-{DEFAULT_DECIMALS + 1}'), "no matching argument types", ), ('reflect_short_u', Decimal('25.4' + '9' * DEFAULT_DECIMALS), "no matching argument types"), diff --git a/tests/core/pm-module/test_ens_integration.py b/tests/core/pm-module/test_ens_integration.py index c6bd38b519..76dc64a1d6 100644 --- a/tests/core/pm-module/test_ens_integration.py +++ b/tests/core/pm-module/test_ens_integration.py @@ -20,7 +20,7 @@ def bytes32(val): if isinstance(val, int): result = to_bytes(val) else: - raise TypeError('val %r could not be converted to bytes') + raise TypeError(f'{val!r} could not be converted to bytes') return result.rjust(32, b'\0') diff --git a/tests/core/providers/test_auto_provider.py b/tests/core/providers/test_auto_provider.py index c0e30295f6..1647d5d502 100644 --- a/tests/core/providers/test_auto_provider.py +++ b/tests/core/providers/test_auto_provider.py @@ -108,7 +108,7 @@ def test_web3_auto_infura(monkeypatch, caplog, environ_name): w3 = infura.w3 assert isinstance(w3.provider, HTTPProvider) - expected_url = 'https://%s/v3/%s' % (infura.INFURA_MAINNET_DOMAIN, API_KEY) + expected_url = f'https://{infura.INFURA_MAINNET_DOMAIN}/v3/{API_KEY}' assert getattr(w3.provider, 'endpoint_uri') == expected_url @@ -117,7 +117,7 @@ def test_web3_auto_infura_websocket_default(monkeypatch, caplog, environ_name): monkeypatch.setenv('WEB3_INFURA_SCHEME', 'wss') API_KEY = 'aoeuhtns' monkeypatch.setenv(environ_name, API_KEY) - expected_url = 'wss://%s/ws/v3/%s' % (infura.INFURA_MAINNET_DOMAIN, API_KEY) + expected_url = f'wss://{infura.INFURA_MAINNET_DOMAIN}/ws/v3/{API_KEY}' importlib.reload(infura) assert len(caplog.record_tuples) == 0 @@ -145,7 +145,7 @@ def test_web3_auto_infura_websocket_with_secret(monkeypatch, caplog, environ_nam w3 = infura.w3 assert isinstance(w3.provider, WebsocketProvider) - expected_url = 'wss://:secret@%s/ws/v3/test' % (infura.INFURA_MAINNET_DOMAIN) + expected_url = f'wss://:secret@{infura.INFURA_MAINNET_DOMAIN}/ws/v3/test' assert getattr(w3.provider, 'endpoint_uri') == expected_url @@ -159,7 +159,7 @@ def test_web3_auto_infura_with_secret(monkeypatch, caplog, environ_name): w3 = infura.w3 assert isinstance(w3.provider, HTTPProvider) - expected_url = 'https://%s/v3/test' % (infura.INFURA_MAINNET_DOMAIN) + expected_url = f'https://{infura.INFURA_MAINNET_DOMAIN}/v3/test' expected_auth_value = ('', 'secret') assert getattr(w3.provider, 'endpoint_uri') == expected_url assert w3.provider.get_request_kwargs()['auth'] == expected_auth_value diff --git a/tests/core/utilities/test_formatters.py b/tests/core/utilities/test_formatters.py index 649578f7ce..45a56772f8 100644 --- a/tests/core/utilities/test_formatters.py +++ b/tests/core/utilities/test_formatters.py @@ -18,7 +18,7 @@ def square_int(x): return x -@pytest.mark.parametrize('non_collection', [1, 'abc', u'def', True, None]) +@pytest.mark.parametrize('non_collection', [1, 'abc', 'def', True, None]) def test_map_collection_on_non_collection(non_collection): assert map_collection(lambda x: x + 2, non_collection) == non_collection diff --git a/tests/ens/conftest.py b/tests/ens/conftest.py index 7a3d2044dd..a11c5ff7dd 100644 --- a/tests/ens/conftest.py +++ b/tests/ens/conftest.py @@ -34,7 +34,7 @@ def bytes32(val): if isinstance(val, int): result = Web3.toBytes(val) else: - raise TypeError('val %r could not be converted to bytes') + raise TypeError(f'{val!r} could not be converted to bytes') if len(result) < 32: return result.rjust(32, b'\0') else: diff --git a/web3/_utils/blocks.py b/web3/_utils/blocks.py index ac875f3c03..63ff37328c 100644 --- a/web3/_utils/blocks.py +++ b/web3/_utils/blocks.py @@ -31,7 +31,7 @@ def is_predefined_block_number(value: Any) -> bool: elif is_integer(value): return False else: - raise TypeError("unrecognized block reference: %r" % value) + raise TypeError(f"unrecognized block reference: {value!r}") return value_text in {"latest", "pending", "earliest"} diff --git a/web3/_utils/decorators.py b/web3/_utils/decorators.py index 63e14d9506..d93ff99277 100644 --- a/web3/_utils/decorators.py +++ b/web3/_utils/decorators.py @@ -24,7 +24,7 @@ def wrapped(*args: Any) -> Any: thread_id = threading.get_ident() thread_local_args = (thread_id,) + arg_instances if thread_local_args in to_wrap.__already_called: # type: ignore - raise ValueError('Recursively called %s with %r' % (to_wrap, args)) + raise ValueError(f'Recursively called {to_wrap} with {args!r}') to_wrap.__already_called[thread_local_args] = True # type: ignore try: wrapped_val = to_wrap(*args) diff --git a/web3/_utils/empty.py b/web3/_utils/empty.py index e6d299b3d6..1fb94fda60 100644 --- a/web3/_utils/empty.py +++ b/web3/_utils/empty.py @@ -7,8 +7,5 @@ class Empty: def __bool__(self) -> Literal[False]: return False - def __nonzero__(self) -> Literal[False]: - return False - empty = Empty() diff --git a/web3/_utils/encoding.py b/web3/_utils/encoding.py index 085cbf1378..5b9a4a4a56 100644 --- a/web3/_utils/encoding.py +++ b/web3/_utils/encoding.py @@ -191,14 +191,14 @@ def _json_mapping_errors(self, mapping: Dict[Any, Any]) -> Iterable[str]: try: self._friendly_json_encode(val) except TypeError as exc: - yield "%r: because (%s)" % (key, exc) + yield f"{key!r}: because ({exc})" def _json_list_errors(self, iterable: Iterable[Any]) -> Iterable[str]: for index, element in enumerate(iterable): try: self._friendly_json_encode(element) except TypeError as exc: - yield "%d: because (%s)" % (index, exc) + yield f"{index}: because ({exc})" def _friendly_json_encode(self, obj: Dict[Any, Any], cls: Optional[Type[json.JSONEncoder]] = None) -> str: @@ -238,7 +238,7 @@ def to_4byte_hex(hex_or_str_or_bytes: Union[HexStr, str, bytes, int]) -> HexStr: byte_str = hexstr_if_str(to_bytes, hex_or_str_or_bytes) if len(byte_str) > 4: raise ValueError( - 'expected value of size 4 bytes. Got: %d bytes' % len(byte_str) + f'expected value of size 4 bytes. Got: {len(byte_str)} bytes' ) hex_str = encode_hex(byte_str) return pad_hex(hex_str, size_of_4bytes) diff --git a/web3/_utils/ens.py b/web3/_utils/ens.py index f924d306d7..c5ba63deb5 100644 --- a/web3/_utils/ens.py +++ b/web3/_utils/ens.py @@ -46,7 +46,7 @@ def validate_name_has_address(ens: ENS, name: str) -> ChecksumAddress: if addr: return addr else: - raise NameNotFound("Could not find address for name %r" % name) + raise NameNotFound(f"Could not find address for name {name!r}") class StaticENS: diff --git a/web3/_utils/method_formatters.py b/web3/_utils/method_formatters.py index 5105b5f0e0..7a1ff17a7b 100644 --- a/web3/_utils/method_formatters.py +++ b/web3/_utils/method_formatters.py @@ -123,7 +123,7 @@ def to_hexbytes( if isinstance(val, (str, int, bytes)): result = HexBytes(val) else: - raise TypeError("Cannot convert %r to HexBytes" % val) + raise TypeError(f"Cannot convert {val!r} to HexBytes") extra_bytes = len(result) - num_bytes if extra_bytes == 0 or (variable_length and extra_bytes < 0): @@ -132,9 +132,7 @@ def to_hexbytes( return HexBytes(result[extra_bytes:]) else: raise ValueError( - "The value %r is %d bytes, but should be %d" % ( - result, len(result), num_bytes - ) + f"The value {result!r} is {len(result)} bytes, but should be {num_bytes}" ) diff --git a/web3/_utils/module_testing/eth_module.py b/web3/_utils/module_testing/eth_module.py index a2e4b66ea9..f2fa5bee92 100644 --- a/web3/_utils/module_testing/eth_module.py +++ b/web3/_utils/module_testing/eth_module.py @@ -2739,7 +2739,7 @@ def test_eth_getTransactionByHash_contract_creation( ) -> None: transaction = w3.eth.get_transaction(math_contract_deploy_txn_hash) assert is_dict(transaction) - assert transaction['to'] is None, "to field is %r" % transaction['to'] + assert transaction['to'] is None, f"to field is {transaction['to']!r}" def test_eth_getTransactionByBlockHashAndIndex( self, w3: "Web3", block_with_txn: BlockData, mined_txn_hash: HexStr @@ -3140,7 +3140,7 @@ def test_eth_call_old_contract_state( assert default_call_result == 0 if pending_call_result != 1: - raise AssertionError("pending call result was %d instead of 1" % pending_call_result) + raise AssertionError(f"pending call result was {pending_call_result} instead of 1") def test_eth_uninstallFilter_deprecated(self, w3: "Web3") -> None: filter = w3.eth.filter({}) diff --git a/web3/_utils/normalizers.py b/web3/_utils/normalizers.py index fe0b07dd7d..61f5f07414 100644 --- a/web3/_utils/normalizers.py +++ b/web3/_utils/normalizers.py @@ -146,9 +146,8 @@ def abi_bytes_to_hex( num_bytes = abi_type.sub if len(bytes_data) > num_bytes: raise ValueError( - "This value was expected to be at most %d bytes, but instead was %d: %r" % ( - (num_bytes, len(bytes_data), data) - ) + f"This value was expected to be at most {num_bytes} bytes, " + f"but instead was {len(bytes_data)}: {data!r}" ) padded = bytes_data.ljust(num_bytes, b'\0') @@ -204,18 +203,18 @@ def abi_ens_resolver(w3: "Web3", type_str: TypeStr, val: Any) -> Tuple[TypeStr, if type_str == 'address' and is_ens_name(val): if w3 is None: raise InvalidAddress( - "Could not look up name %r because no web3" - " connection available" % (val) + f"Could not look up name {val!r} because no web3" + " connection available" ) elif w3.ens is None: raise InvalidAddress( - "Could not look up name %r because ENS is" - " set to None" % (val) + f"Could not look up name {val!r} because ENS is" + " set to None" ) elif int(w3.net.version) != 1 and not isinstance(w3.ens, StaticENS): raise InvalidAddress( - "Could not look up name %r because web3 is" - " not connected to mainnet" % (val) + f"Could not look up name {val!r} because web3 is" + " not connected to mainnet" ) else: return type_str, validate_name_has_address(w3.ens, val) diff --git a/web3/_utils/rpc_abi.py b/web3/_utils/rpc_abi.py index 87b29e41bb..641497ef42 100644 --- a/web3/_utils/rpc_abi.py +++ b/web3/_utils/rpc_abi.py @@ -241,4 +241,4 @@ def abi_request_formatters( single_dict_formatter = apply_abi_formatters_to_dict(normalizers, abi_types) yield method, apply_formatter_at_index(single_dict_formatter, 0) else: - raise TypeError("ABI definitions must be a list or dictionary, got %r" % abi_types) + raise TypeError(f"ABI definitions must be a list or dictionary, got {abi_types!r}") diff --git a/web3/_utils/transactions.py b/web3/_utils/transactions.py index 8d9ca470df..638cb89d50 100644 --- a/web3/_utils/transactions.py +++ b/web3/_utils/transactions.py @@ -109,7 +109,7 @@ def fill_transaction_defaults(w3: "Web3", transaction: TxParams) -> TxParams: if callable(default_getter): if w3 is None: - raise ValueError("You must specify a '%s' value in the transaction" % key) + raise ValueError(f"You must specify a '{key}' value in the transaction") default_val = default_getter(w3, transaction) else: default_val = default_getter diff --git a/web3/_utils/validation.py b/web3/_utils/validation.py index 65fcb9f1c0..d59e788d61 100644 --- a/web3/_utils/validation.py +++ b/web3/_utils/validation.py @@ -205,5 +205,5 @@ def assert_one_val(*args: Any, **kwargs: Any) -> None: if not has_one_val(*args, **kwargs): raise TypeError( "Exactly one of the passed values can be specified. " - "Instead, values were: %r, %r" % (args, kwargs) + f"Instead, values were: {args!r}, {kwargs!r}" ) diff --git a/web3/auto/infura/endpoints.py b/web3/auto/infura/endpoints.py index c8de5c78d2..8531e3c023 100644 --- a/web3/auto/infura/endpoints.py +++ b/web3/auto/infura/endpoints.py @@ -56,10 +56,10 @@ def build_infura_url(domain: str) -> URI: secret = load_secret() if scheme == WEBSOCKET_SCHEME and secret != '': - return URI("%s://:%s@%s/ws/v3/%s" % (scheme, secret, domain, key)) + return URI(f"{scheme}://:{secret}@{domain}/ws/v3/{key}") elif scheme == WEBSOCKET_SCHEME and secret == '': - return URI("%s://%s/ws/v3/%s" % (scheme, domain, key)) + return URI(f"{scheme}://{domain}/ws/v3/{key}") elif scheme == HTTP_SCHEME: - return URI("%s://%s/v3/%s" % (scheme, domain, key)) + return URI(f"{scheme}://{domain}/v3/{key}") else: - raise ValidationError("Cannot connect to Infura with scheme %r" % scheme) + raise ValidationError(f"Cannot connect to Infura with scheme {scheme!r}") diff --git a/web3/contract.py b/web3/contract.py index 38e98b4152..c20d0e2156 100644 --- a/web3/contract.py +++ b/web3/contract.py @@ -425,7 +425,7 @@ def get_function_by_signature(self, signature: str) -> 'ContractFunction': if ' ' in signature: raise ValueError( 'Function signature should not contain any spaces. ' - 'Found spaces in input: %s' % signature + f'Found spaces in input: {signature}' ) def callable_check(fn_abi: ABIFunction) -> bool: @@ -715,9 +715,9 @@ def __prepared_function(self, *args: Any, **kwargs: Any) -> 'ContractFunction': modifier, modifier_dict = kwargs.popitem() if modifier not in self.ALLOWED_MODIFIERS: raise TypeError( - "The only allowed keyword arguments are: %s" % self.ALLOWED_MODIFIERS) + f"The only allowed keyword arguments are: {self.ALLOWED_MODIFIERS}") else: - raise TypeError("Use up to one keyword argument, one of: %s" % self.ALLOWED_MODIFIERS) + raise TypeError(f"Use up to one keyword argument, one of: {self.ALLOWED_MODIFIERS}") return getattr(self._function(*args), modifier)(modifier_dict) @@ -1099,11 +1099,11 @@ def factory(cls, class_name: str, **kwargs: Any) -> 'ContractFunction': def __repr__(self) -> str: if self.abi: - _repr = '' - return '' % self.fn_name + return f'' class ContractEvent: diff --git a/web3/datastructures.py b/web3/datastructures.py index 79f6cd59ec..27b2e73bc2 100644 --- a/web3/datastructures.py +++ b/web3/datastructures.py @@ -57,7 +57,7 @@ def __len__(self) -> int: return len(self.__dict__) def __repr__(self) -> str: - return self.__class__.__name__ + "(%r)" % self.__dict__ + return self.__class__.__name__ + f"({self.__dict__!r})" def _repr_pretty_(self, builder: Any, cycle: bool) -> None: """ @@ -158,12 +158,8 @@ def inject(self, element: TValue, name: Optional[TKey] = None, raise TypeError("The layer for insertion must be an int.") elif layer != 0 and layer != len(self._queue): raise NotImplementedError( - "You can only insert to the beginning or end of a %s, currently. " - "You tried to insert to %d, but only 0 and %d are permitted. " % ( - type(self), - layer, - len(self._queue), - ) + f"You can only insert to the beginning or end of a {type(self)}, currently. " + f"You tried to insert to {layer}, but only 0 and {len(self._queue)} are permitted. " ) self.add(element, name=name) diff --git a/web3/exceptions.py b/web3/exceptions.py index 3390275151..b7d88d1b59 100644 --- a/web3/exceptions.py +++ b/web3/exceptions.py @@ -52,9 +52,11 @@ class StaleBlockchain(Exception): def __init__(self, block: BlockData, allowable_delay: int) -> None: last_block_date = datetime.datetime.fromtimestamp(block["timestamp"]).strftime('%c') message = ( - "The latest block, #%d, is %d seconds old, but is only allowed to be %d s old. " - "The date of the most recent block is %s. Continue syncing and try again..." % - (block["number"], time.time() - block["timestamp"], allowable_delay, last_block_date) + f"The latest block, #{block['number']}, is " + f"{time.time() - block['timestamp']} seconds old, but is only " + f"allowed to be {allowable_delay} s old. " + f"The date of the most recent block is {last_block_date}. Continue " + "syncing and try again..." ) super().__init__(message, block, allowable_delay) diff --git a/web3/main.py b/web3/main.py index acbac1b9a3..5395895683 100644 --- a/web3/main.py +++ b/web3/main.py @@ -292,12 +292,11 @@ def keccak(primitive: Optional[Primitives] = None, text: Optional[str] = None, return eth_utils_keccak(input_bytes) raise TypeError( - "You called keccak with first arg %r and keywords %r. You must call it with one of " - "these approaches: keccak(text='txt'), keccak(hexstr='0x747874'), " - "keccak(b'\\x74\\x78\\x74'), or keccak(0x747874)." % ( - primitive, - {'text': text, 'hexstr': hexstr} - ) + f"You called keccak with first arg {primitive!r} and keywords " + f"{{'text': {text!r}, 'hexstr': {hexstr!r}}}. You must call it " + "with one of these approaches: keccak(text='txt'), keccak " + "(hexstr='0x747874'), keccak(b'\\x74\\x78\\x74'), or kecca " + "(0x747874)." ) @combomethod diff --git a/web3/manager.py b/web3/manager.py index 1594c4dd6f..db5c6085b4 100644 --- a/web3/manager.py +++ b/web3/manager.py @@ -146,7 +146,7 @@ def _make_request( request_func = self.provider.request_func( self.w3, self.middleware_onion) - self.logger.debug("Making request. Method: %s", method) + self.logger.debug(f"Making request. Method: {method}") return request_func(method, params) async def _coro_make_request( @@ -156,7 +156,7 @@ async def _coro_make_request( request_func = await self.provider.request_func( # type: ignore self.w3, self.middleware_onion) - self.logger.debug("Making request. Method: %s", method) + self.logger.debug(f"Making request. Method: {method}") return await request_func(method, params) @staticmethod diff --git a/web3/middleware/validation.py b/web3/middleware/validation.py index 5fafce175b..23872858ef 100644 --- a/web3/middleware/validation.py +++ b/web3/middleware/validation.py @@ -59,11 +59,8 @@ def _validate_chain_id(web3_chain_id: int, chain_id: int) -> int: return chain_id else: raise ValidationError( - "The transaction declared chain ID %r, " - "but the connected node is on %r" % ( - chain_id, - web3_chain_id, - ) + f"The transaction declared chain ID {chain_id!r}, " + f"but the connected node is on {web3_chain_id!r}" ) @@ -73,13 +70,11 @@ def _check_extradata_length(val: Any) -> Any: result = HexBytes(val) if len(result) > MAX_EXTRADATA_LENGTH: raise ExtraDataLengthError( - "The field extraData is %d bytes, but should be %d. " - "It is quite likely that you are connected to a POA chain. " - "Refer to " + f"The field extraData is {len(result)} bytes, but should be " + f"{MAX_EXTRADATA_LENGTH}. It is quite likely that you are " + "connected to a POA chain. Refer to " "http://web3py.readthedocs.io/en/stable/middleware.html#geth-style-proof-of-authority " - "for more details. The full extraData is: %r" % ( - len(result), MAX_EXTRADATA_LENGTH, result - ) + f"for more details. The full extraData is: {result!r}" ) return val diff --git a/web3/providers/async_rpc.py b/web3/providers/async_rpc.py index d3229c71de..dc80bfdedf 100644 --- a/web3/providers/async_rpc.py +++ b/web3/providers/async_rpc.py @@ -74,8 +74,7 @@ def get_request_headers(self) -> Dict[str, str]: } async def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse: - self.logger.debug("Making request HTTP. URI: %s, Method: %s", - self.endpoint_uri, method) + self.logger.debug(f"Making request HTTP. URI: {self.endpoint_uri}, Method: {method}") request_data = self.encode_rpc_request(method, params) raw_response = await async_make_post_request( self.endpoint_uri, @@ -83,7 +82,6 @@ async def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse: **self.get_request_kwargs() ) response = self.decode_rpc_response(raw_response) - self.logger.debug("Getting response HTTP. URI: %s, " - "Method: %s, Response: %s", - self.endpoint_uri, method, response) + self.logger.debug(f"Getting response HTTP. URI: {self.endpoint_uri}, " + f"Method: {method}, Response: {response}") return response diff --git a/web3/providers/auto.py b/web3/providers/auto.py index 9d351b3aec..9bc49b893b 100644 --- a/web3/providers/auto.py +++ b/web3/providers/auto.py @@ -55,10 +55,8 @@ def load_provider_from_uri( return WebsocketProvider(uri_string) else: raise NotImplementedError( - 'Web3 does not know how to connect to scheme %r in %r' % ( - uri.scheme, - uri_string, - ) + f'Web3 does not know how to connect to scheme {uri.scheme!r} ' + f'in {uri_string!r}' ) diff --git a/web3/providers/ipc.py b/web3/providers/ipc.py index 45faf63b61..a166aabd80 100644 --- a/web3/providers/ipc.py +++ b/web3/providers/ipc.py @@ -52,7 +52,10 @@ def __init__(self, ipc_path: str) -> None: def __enter__(self) -> socket.socket: if not self.ipc_path: - raise FileNotFoundError("cannot connect to IPC socket at path: %r" % self.ipc_path) + raise FileNotFoundError( + "cannot connect to IPC socket at path: " + f"{self.ipc_path!r}" + ) if not self.sock: self.sock = self._open() @@ -231,8 +234,7 @@ def __str__(self) -> str: return f"<{self.__class__.__name__} {self.ipc_path}>" def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse: - self.logger.debug("Making request IPC. Path: %s, Method: %s", - self.ipc_path, method) + self.logger.debug(f"Making request IPC. Path: {self.ipc_path}, Method: {method}") request = self.encode_rpc_request(method, params) with self._lock, self._socket as sock: diff --git a/web3/providers/rpc.py b/web3/providers/rpc.py index 0be744f71d..74c83eac7e 100644 --- a/web3/providers/rpc.py +++ b/web3/providers/rpc.py @@ -82,8 +82,7 @@ def get_request_headers(self) -> Dict[str, str]: } def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse: - self.logger.debug("Making request HTTP. URI: %s, Method: %s", - self.endpoint_uri, method) + self.logger.debug(f"Making request HTTP. URI: {self.endpoint_uri}, Method: {method}") request_data = self.encode_rpc_request(method, params) raw_response = make_post_request( self.endpoint_uri, @@ -91,7 +90,6 @@ def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse: **self.get_request_kwargs() ) response = self.decode_rpc_response(raw_response) - self.logger.debug("Getting response HTTP. URI: %s, " - "Method: %s, Response: %s", - self.endpoint_uri, method, response) + self.logger.debug(f"Getting response HTTP. URI: {self.endpoint_uri}, " + f"Method: {method}, Response: {response}") return response diff --git a/web3/providers/websocket.py b/web3/providers/websocket.py index e999d56d3f..ef486ff49c 100644 --- a/web3/providers/websocket.py +++ b/web3/providers/websocket.py @@ -133,8 +133,8 @@ async def coro_make_request(self, request_data: bytes) -> RPCResponse: ) def make_request(self, method: RPCEndpoint, params: Any) -> RPCResponse: - self.logger.debug("Making request WebSocket. URI: %s, " - "Method: %s", self.endpoint_uri, method) + self.logger.debug(f"Making request WebSocket. URI: {self.endpoint_uri}, " + f"Method: {method}") request_data = self.encode_rpc_request(method, params) future = asyncio.run_coroutine_threadsafe( self.coro_make_request(request_data), diff --git a/web3/tools/pytest_ethereum/linker.py b/web3/tools/pytest_ethereum/linker.py index c0804d0136..d7385ea857 100644 --- a/web3/tools/pytest_ethereum/linker.py +++ b/web3/tools/pytest_ethereum/linker.py @@ -82,7 +82,7 @@ def _deploy( manifest = insert_deployment( package, contract_name, deployment_data, latest_block_uri ) - logger.info("%s deployed." % contract_name) + logger.info(f"{contract_name} deployed.") return Package(manifest, package.w3) @@ -107,8 +107,7 @@ def link(contract: ContractName, linked_type: str, package: Package) -> Package: to_hex(linked_factory.bytecode), ) logger.info( - "%s linked to %s at address %s." - % (contract, linked_type, to_checksum_address(deployment_address)) + f"{contract} linked to {linked_type} at address {to_checksum_address(deployment_address)}." ) return Package(manifest, package.w3) @@ -120,5 +119,5 @@ def run_python(callback_fn: Callable[..., None], package: Package) -> Package: the contracts in the package. """ callback_fn(package) - logger.info("%s python function ran." % callback_fn.__name__) + logger.info(f"{callback_fn.__name__} python function ran.") return package From fb294c3be7c18a8f8bba620d224bdfd4549cca82 Mon Sep 17 00:00:00 2001 From: Harmouch101 Date: Wed, 2 Mar 2022 20:55:30 +0200 Subject: [PATCH 03/15] :memo: add newsfragment! Signed-off-by: Harmouch101 --- newsfragments/2372.misc.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 newsfragments/2372.misc.rst diff --git a/newsfragments/2372.misc.rst b/newsfragments/2372.misc.rst new file mode 100644 index 0000000000..85a5d056e1 --- /dev/null +++ b/newsfragments/2372.misc.rst @@ -0,0 +1 @@ +remove python 2 syntax. \ No newline at end of file From c5b413dafea7876dd049b4d66c807c47a152ddd0 Mon Sep 17 00:00:00 2001 From: Harmouch101 Date: Tue, 8 Mar 2022 18:43:51 +0200 Subject: [PATCH 04/15] :zap: update! Signed-off-by: Harmouch101 --- docs/v4_migration.rst | 4 +- docs/web3.eth.rst | 4 +- ethpm/contract.py | 12 ++--- ethpm/tools/builder.py | 2 +- ethpm/validation/manifest.py | 12 ++--- .../contracts/test_contract_call_interface.py | 6 +-- ...st_contract_method_to_argument_matching.py | 6 +-- .../test_filter_against_transaction_logs.py | 12 ++--- .../middleware/test_fixture_middleware.py | 5 +- .../test_simple_cache_middleware.py | 2 +- .../test_time_based_cache_middleware.py | 2 +- .../middleware/test_transaction_signing.py | 5 +- tests/core/providers/test_ipc_provider.py | 2 +- .../core/providers/test_websocket_provider.py | 4 +- .../test_abi_filtering_by_argument_name.py | 14 ++--- tests/ethpm/_utils/test_contract_utils.py | 10 ++-- tests/ethpm/validation/test_manifest.py | 8 +-- tests/integration/generate_fixtures/common.py | 14 +++-- .../generate_fixtures/go_ethereum.py | 7 +-- tests/integration/go_ethereum/conftest.py | 7 +-- .../go_ethereum/test_goethereum_http.py | 2 +- .../go_ethereum/test_goethereum_ws.py | 2 +- web3/_utils/abi.py | 52 ++++++++----------- web3/_utils/async_transactions.py | 4 +- web3/_utils/blocks.py | 3 +- web3/_utils/caching.py | 8 +-- web3/_utils/contracts.py | 25 ++++----- web3/_utils/datatypes.py | 6 +-- web3/_utils/encoding.py | 20 ++++--- web3/_utils/events.py | 10 ++-- web3/_utils/filters.py | 2 +- web3/_utils/http.py | 5 +- web3/_utils/math.py | 5 +- .../go_ethereum_admin_module.py | 2 +- web3/_utils/rpc_abi.py | 2 +- web3/_utils/threads.py | 2 +- web3/_utils/transactions.py | 6 +-- web3/_utils/validation.py | 17 +++--- web3/contract.py | 23 ++++---- web3/main.py | 2 +- web3/manager.py | 2 +- web3/middleware/filter.py | 8 +-- web3/middleware/signing.py | 2 +- web3/middleware/stalecheck.py | 4 +- web3/providers/async_rpc.py | 2 +- web3/providers/auto.py | 7 ++- web3/providers/eth_tester/defaults.py | 8 ++- web3/providers/ipc.py | 8 +-- web3/providers/rpc.py | 2 +- web3/providers/websocket.py | 8 +-- web3/tools/benchmark/node.py | 2 +- 51 files changed, 180 insertions(+), 209 deletions(-) diff --git a/docs/v4_migration.rst b/docs/v4_migration.rst index 14ca20f2e1..f7c2a4ceed 100644 --- a/docs/v4_migration.rst +++ b/docs/v4_migration.rst @@ -68,7 +68,7 @@ printing out new block hashes as they appear: .. code-block:: python >>> def new_block_callback(block_hash): - ... print "New Block: {0}".format(block_hash) + ... print(f"New Block: {block_hash}") ... >>> new_block_filter = web3.eth.filter('latest') >>> new_block_filter.watch(new_block_callback) @@ -79,7 +79,7 @@ In v4, that same logic: >>> new_block_filter = web3.eth.filter('latest') >>> for block_hash in new_block_filter.get_new_entries(): - ... print("New Block: {}".format(block_hash)) + ... print(f"New Block: {block_hash}") The caller is responsible for polling the results from ``get_new_entries()``. See :ref:`asynchronous_filters` for examples of filter-event handling with web3 v4. diff --git a/docs/web3.eth.rst b/docs/web3.eth.rst index eaa1653782..9fbe9c13ab 100644 --- a/docs/web3.eth.rst +++ b/docs/web3.eth.rst @@ -364,7 +364,7 @@ The following methods are available on the ``web3.eth`` namespace. assert rlp_account == HexaryTrie.get_from_proof( root, trie_key, format_proof_nodes(proof.accountProof) - ), "Failed to verify account proof {}".format(proof.address) + ), f"Failed to verify account proof {proof.address}" for storage_proof in proof.storageProof: trie_key = keccak(pad_bytes(b'\x00', 32, storage_proof.key)) @@ -376,7 +376,7 @@ The following methods are available on the ``web3.eth`` namespace. assert rlp_value == HexaryTrie.get_from_proof( root, trie_key, format_proof_nodes(storage_proof.proof) - ), "Failed to verify storage proof {}".format(storage_proof.key) + ), f"Failed to verify storage proof {storage_proof.key}" return True diff --git a/ethpm/contract.py b/ethpm/contract.py index d9b226d392..e56d6670c9 100644 --- a/ethpm/contract.py +++ b/ethpm/contract.py @@ -56,7 +56,7 @@ def __init__(self, address: bytes, **kwargs: Any) -> None: ) validate_address(address) # type ignored to allow for undefined **kwargs on `Contract` base class __init__ - super(LinkableContract, self).__init__(address=address, **kwargs) # type: ignore + super().__init__(address=address, **kwargs) # type: ignore @classmethod def factory( @@ -69,7 +69,7 @@ def factory( if not is_prelinked_bytecode(to_bytes(hexstr=bytecode), dep_link_refs): needs_bytecode_linking = True kwargs = assoc(kwargs, "needs_bytecode_linking", needs_bytecode_linking) - return super(LinkableContract, cls).factory(w3, class_name, **kwargs) + return super().factory(w3, class_name, **kwargs) @classmethod def constructor(cls, *args: Any, **kwargs: Any) -> ContractConstructor: @@ -77,7 +77,7 @@ def constructor(cls, *args: Any, **kwargs: Any) -> ContractConstructor: raise BytecodeLinkingError( "Contract cannot be deployed until its bytecode is linked." ) - return super(LinkableContract, cls).constructor(*args, **kwargs) + return super().constructor(*args, **kwargs) @classmethod def link_bytecode(cls, attr_dict: Dict[str, str]) -> Type["LinkableContract"]: @@ -111,7 +111,7 @@ def validate_attr_dict(self, attr_dict: Dict[str, str]) -> None: """ Validates that ContractType keys in attr_dict reference existing manifest ContractTypes. """ - attr_dict_names = list(attr_dict.keys()) + attr_dict_names = attr_dict.keys() if not self.unlinked_references and not self.linked_references: raise BytecodeLinkingError( @@ -122,8 +122,8 @@ def validate_attr_dict(self, attr_dict: Dict[str, str]) -> None: linked_refs = self.linked_references or ({},) all_link_refs = unlinked_refs + linked_refs - all_link_names = [ref["name"] for ref in all_link_refs if ref] - if set(attr_dict_names) != set(all_link_names): + all_link_names = {ref["name"] for ref in all_link_refs if ref} + if attr_dict_names != all_link_names: raise BytecodeLinkingError( "All link references must be defined when calling " "`link_bytecode` on a contract factory." diff --git a/ethpm/tools/builder.py b/ethpm/tools/builder.py index 895b790c09..6f38968402 100644 --- a/ethpm/tools/builder.py +++ b/ethpm/tools/builder.py @@ -498,7 +498,7 @@ def normalize_compiler_output(compiler_output: Dict[str, Any]) -> Dict[str, Any] ] paths, names = zip(*paths_and_names) if len(names) != len(set(names)): - duplicates = set([name for name in names if names.count(name) > 1]) + duplicates = {name for name in names if names.count(name) > 1} raise ManifestBuildingError( f"Duplicate contract types: {duplicates} were found in the compiler output." ) diff --git a/ethpm/validation/manifest.py b/ethpm/validation/manifest.py index 6aba1b1034..eb94f86523 100644 --- a/ethpm/validation/manifest.py +++ b/ethpm/validation/manifest.py @@ -66,11 +66,11 @@ def _load_schema_data() -> Dict[str, Any]: def extract_contract_types_from_deployments(deployment_data: List[Any]) -> Set[str]: - contract_types = set( + contract_types = { deployment["contractType"] for chain_deployments in deployment_data for deployment in chain_deployments.values() - ) + } return contract_types @@ -108,11 +108,11 @@ def validate_manifest_deployments(manifest: Dict[str, Any]) -> None: """ Validate that a manifest's deployments contracts reference existing contractTypes. """ - if set(("contractTypes", "deployments")).issubset(manifest): - all_contract_types = list(manifest["contractTypes"].keys()) - all_deployments = list(manifest["deployments"].values()) + if {"contractTypes", "deployments"}.issubset(manifest): + all_contract_types = manifest["contractTypes"].keys() + all_deployments = manifest["deployments"].values() all_deployment_names = extract_contract_types_from_deployments(all_deployments) - missing_contract_types = set(all_deployment_names).difference( + missing_contract_types = all_deployment_names.difference( all_contract_types ) if missing_contract_types: diff --git a/tests/core/contracts/test_contract_call_interface.py b/tests/core/contracts/test_contract_call_interface.py index 4e67c7d1cd..34a754191c 100644 --- a/tests/core/contracts/test_contract_call_interface.py +++ b/tests/core/contracts/test_contract_call_interface.py @@ -602,9 +602,9 @@ def test_returns_data_from_specified_block(w3, math_contract): message_regex = ( r"\nCould not identify the intended function with name `.*`, " - r"positional argument\(s\) of type `.*` and " - r"keyword argument\(s\) of type `.*`." - r"\nFound .* function\(s\) with the name `.*`: .*" + r"positional arguments? of type `.*` and " + r"keyword arguments? of type `.*`." + r"\nFound .* functions? with the name `.*`: .*" ) diagnosis_arg_regex = ( r"\nFunction invocation failed due to improper number of arguments." diff --git a/tests/core/contracts/test_contract_method_to_argument_matching.py b/tests/core/contracts/test_contract_method_to_argument_matching.py index 8f80dbf6c6..d4c051b2a0 100644 --- a/tests/core/contracts/test_contract_method_to_argument_matching.py +++ b/tests/core/contracts/test_contract_method_to_argument_matching.py @@ -150,7 +150,7 @@ def test_finds_function_with_matching_args(w3, arguments, expected_types): abi = Contract._find_matching_fn_abi('a', arguments) assert abi['name'] == 'a' assert len(abi['inputs']) == len(expected_types) - assert set(get_abi_input_types(abi)) == set(expected_types) + assert get_abi_input_types(abi) == expected_types def test_finds_function_with_matching_args_deprecation_warning(w3): @@ -160,7 +160,7 @@ def test_finds_function_with_matching_args_deprecation_warning(w3): abi = Contract._find_matching_fn_abi('a', ['']) assert abi['name'] == 'a' assert len(abi['inputs']) == len(['bytes32']) - assert set(get_abi_input_types(abi)) == set(['bytes32']) + assert get_abi_input_types(abi) == ['bytes32'] def test_error_when_duplicate_match(w3): @@ -193,4 +193,4 @@ def test_strict_finds_function_with_matching_args(w3_strict_abi, arguments, expe abi = Contract._find_matching_fn_abi('a', arguments) assert abi['name'] == 'a' assert len(abi['inputs']) == len(expected_types) - assert set(get_abi_input_types(abi)) == set(expected_types) + assert get_abi_input_types(abi) == expected_types diff --git a/tests/core/filtering/test_filter_against_transaction_logs.py b/tests/core/filtering/test_filter_against_transaction_logs.py index 664ec64b95..d1e63a4e43 100644 --- a/tests/core/filtering/test_filter_against_transaction_logs.py +++ b/tests/core/filtering/test_filter_against_transaction_logs.py @@ -22,8 +22,8 @@ def test_sync_filter_against_log_events(w3_empty, txn_filter = w3.eth.filter({}) - txn_hashes = [] - txn_hashes.append(emitter.functions.logNoArgs(emitter_event_ids.LogNoArguments).transact()) + txn_hashes = set() + txn_hashes.add(emitter.functions.logNoArgs(emitter_event_ids.LogNoArguments).transact()) for txn_hash in txn_hashes: wait_for_transaction(w3, txn_hash) @@ -34,7 +34,7 @@ def test_sync_filter_against_log_events(w3_empty, seen_logs = txn_filter.get_new_entries() - assert set(txn_hashes) == set(log['transactionHash'] for log in seen_logs) + assert txn_hashes == {log['transactionHash'] for log in seen_logs} @pytest.mark.skip(reason="fixture 'w3_empty' not found") @@ -51,9 +51,9 @@ def test_async_filter_against_log_events(w3_empty, txn_filter = w3.eth.filter({}) txn_filter.watch(seen_logs.append) - txn_hashes = [] + txn_hashes = set() - txn_hashes.append(emitter.functions.logNoArgs(emitter_event_ids.LogNoArguments).transact()) + txn_hashes.add(emitter.functions.logNoArgs(emitter_event_ids.LogNoArguments).transact()) for txn_hash in txn_hashes: wait_for_transaction(w3, txn_hash) @@ -64,4 +64,4 @@ def test_async_filter_against_log_events(w3_empty, txn_filter.stop_watching(30) - assert set(txn_hashes) == set(log['transactionHash'] for log in seen_logs) + assert txn_hashes == {log['transactionHash'] for log in seen_logs} diff --git a/tests/core/middleware/test_fixture_middleware.py b/tests/core/middleware/test_fixture_middleware.py index 19ed3816c4..1cb9eb9b7b 100644 --- a/tests/core/middleware/test_fixture_middleware.py +++ b/tests/core/middleware/test_fixture_middleware.py @@ -13,10 +13,7 @@ class DummyProvider(BaseProvider): def make_request(self, method, params): - raise NotImplementedError("Cannot make request for {0}:{1}".format( - method, - params, - )) + raise NotImplementedError(f"Cannot make request for {method}:{params}") @pytest.fixture diff --git a/tests/core/middleware/test_simple_cache_middleware.py b/tests/core/middleware/test_simple_cache_middleware.py index 2f09a10ff9..3cbe96e761 100644 --- a/tests/core/middleware/test_simple_cache_middleware.py +++ b/tests/core/middleware/test_simple_cache_middleware.py @@ -87,7 +87,7 @@ def result_cb(method, params): def test_simple_cache_middleware_does_not_cache_error_responses(w3_base): w3 = w3_base w3.middleware_onion.add(construct_error_generator_middleware({ - 'fake_endpoint': lambda *_: 'msg-{0}'.format(str(uuid.uuid4())), + 'fake_endpoint': lambda *_: f'msg-{uuid.uuid4()}', })) w3.middleware_onion.add(construct_simple_cache_middleware( diff --git a/tests/core/middleware/test_time_based_cache_middleware.py b/tests/core/middleware/test_time_based_cache_middleware.py index 968160171f..d2ee1a9502 100644 --- a/tests/core/middleware/test_time_based_cache_middleware.py +++ b/tests/core/middleware/test_time_based_cache_middleware.py @@ -130,7 +130,7 @@ def test_time_based_cache_middleware_does_not_cache_error_response( counter = itertools.count() def mk_error(method, params): - return "error-number-{0}".format(next(counter)) + return f"error-number-{next(counter)}" w3.middleware_onion.add(construct_error_generator_middleware({ 'fake_endpoint': mk_error, diff --git a/tests/core/middleware/test_transaction_signing.py b/tests/core/middleware/test_transaction_signing.py index bb9d2a7dbb..8d1ccefec8 100644 --- a/tests/core/middleware/test_transaction_signing.py +++ b/tests/core/middleware/test_transaction_signing.py @@ -83,10 +83,7 @@ class DummyProvider(BaseProvider): def make_request(self, method, params): - raise NotImplementedError("Cannot make request for {0}:{1}".format( - method, - params, - )) + raise NotImplementedError(f"Cannot make request for {method}:{params}") @pytest.fixture() diff --git a/tests/core/providers/test_ipc_provider.py b/tests/core/providers/test_ipc_provider.py index 2b1042b007..5f856eed7a 100644 --- a/tests/core/providers/test_ipc_provider.py +++ b/tests/core/providers/test_ipc_provider.py @@ -23,7 +23,7 @@ @pytest.fixture def jsonrpc_ipc_pipe_path(): with tempfile.TemporaryDirectory() as temp_dir: - ipc_path = os.path.join(temp_dir, '{0}.ipc'.format(uuid.uuid4())) + ipc_path = os.path.join(temp_dir, f'{uuid.uuid4()}.ipc') try: yield ipc_path finally: diff --git a/tests/core/providers/test_websocket_provider.py b/tests/core/providers/test_websocket_provider.py index 0cf475d31e..25037b1fa0 100644 --- a/tests/core/providers/test_websocket_provider.py +++ b/tests/core/providers/test_websocket_provider.py @@ -55,7 +55,7 @@ async def empty_server(websocket, path): def w3(open_port, start_websocket_server): # need new event loop as the one used by server is already running event_loop = asyncio.new_event_loop() - endpoint_uri = 'ws://127.0.0.1:{}'.format(open_port) + endpoint_uri = f'ws://127.0.0.1:{open_port}' event_loop.run_until_complete(wait_for_ws(endpoint_uri)) provider = WebsocketProvider(endpoint_uri, websocket_timeout=0.01) return Web3(provider) @@ -68,6 +68,6 @@ def test_websocket_provider_timeout(w3): def test_restricted_websocket_kwargs(): invalid_kwargs = {'uri': 'ws://127.0.0.1:8546'} - re_exc_message = r'.*found: {0}*'.format(set(invalid_kwargs.keys())) + re_exc_message = f'.*found: {set(invalid_kwargs)!r}*' with pytest.raises(ValidationError, match=re_exc_message): WebsocketProvider(websocket_kwargs=invalid_kwargs) diff --git a/tests/core/utilities/test_abi_filtering_by_argument_name.py b/tests/core/utilities/test_abi_filtering_by_argument_name.py index 2baac9e355..a9687fb50f 100644 --- a/tests/core/utilities/test_abi_filtering_by_argument_name.py +++ b/tests/core/utilities/test_abi_filtering_by_argument_name.py @@ -48,14 +48,14 @@ @pytest.mark.parametrize( 'argument_names,expected', ( - ([], ['func_1', 'func_2', 'func_3', 'func_4']), - (['a'], ['func_2', 'func_3', 'func_4']), - (['a', 'c'], ['func_4']), - (['c'], ['func_4']), - (['b'], ['func_3', 'func_4']), + ([], {'func_1', 'func_2', 'func_3', 'func_4'}), + (['a'], {'func_2', 'func_3', 'func_4'}), + (['a', 'c'], {'func_4'}), + (['c'], {'func_4'}), + (['b'], {'func_3', 'func_4'}), ) ) def test_filter_by_arguments_1(argument_names, expected): actual_matches = filter_by_argument_name(argument_names, ABI) - function_names = [match['name'] for match in actual_matches] - assert set(function_names) == set(expected) + function_names = {match['name'] for match in actual_matches} + assert function_names == expected diff --git a/tests/ethpm/_utils/test_contract_utils.py b/tests/ethpm/_utils/test_contract_utils.py index 1ffd417901..a6bd9f00e2 100644 --- a/tests/ethpm/_utils/test_contract_utils.py +++ b/tests/ethpm/_utils/test_contract_utils.py @@ -58,11 +58,11 @@ def test_validate_contract_name_invalidates(name): @pytest.mark.parametrize( "contract_data,expected_kwargs", ( - ({"abi": ""}, ["abi"]), - ({"deploymentBytecode": {"bytecode": ""}}, ["bytecode"]), + ({"abi": ""}, {"abi"}), + ({"deploymentBytecode": {"bytecode": ""}}, {"bytecode"}), ( {"abi": "", "runtimeBytecode": {"bytecode": ""}}, - ["abi", "bytecode_runtime"], + {"abi", "bytecode_runtime"}, ), ( { @@ -74,13 +74,13 @@ def test_validate_contract_name_invalidates(name): ], }, }, - ["abi", "bytecode", "unlinked_references"], + {"abi", "bytecode", "unlinked_references"}, ), ), ) def test_generate_contract_factory_kwargs(contract_data, expected_kwargs): contract_factory = generate_contract_factory_kwargs(contract_data) - assert set(contract_factory.keys()) == set(expected_kwargs) + assert contract_factory.keys() == expected_kwargs def test_validate_w3_instance_validates(w3): diff --git a/tests/ethpm/validation/test_manifest.py b/tests/ethpm/validation/test_manifest.py index c68089f7cc..348e2ff388 100644 --- a/tests/ethpm/validation/test_manifest.py +++ b/tests/ethpm/validation/test_manifest.py @@ -78,17 +78,17 @@ def test_validate_deployments_without_deployment(manifest_with_no_deployments): @pytest.mark.parametrize( "data,expected", ( - ({}, set()), - ([{"some": {"contractType": "one"}}], set(["one"])), + ([], {}), + ([{"some": {"contractType": "one"}}], {"one"}), ( [{"some": {"contractType": "one"}, "other": {"contractType": "two"}}], - set(["one", "two"]), + {"one", "two"}, ), ), ) def test_extract_contract_types_from_deployments(data, expected): actual = extract_contract_types_from_deployments(data) - assert actual == expected + assert actual == set(expected) def test_validate_manifest_version_validates_version_three_string(): diff --git a/tests/integration/generate_fixtures/common.py b/tests/integration/generate_fixtures/common.py index d7d20a8964..a6bc487e6b 100644 --- a/tests/integration/generate_fixtures/common.py +++ b/tests/integration/generate_fixtures/common.py @@ -183,11 +183,8 @@ def get_geth_process(geth_binary, output, errors = proc.communicate() print( "Geth Process Exited:\n" - "stdout:{0}\n\n" - "stderr:{1}\n\n".format( - to_text(output), - to_text(errors), - ) + f"stdout:{to_text(output)}\n\n" + f"stderr:{to_text(errors)}\n\n" ) @@ -235,12 +232,13 @@ def mine_transaction_hash(w3, txn_hash): def deploy_contract(w3, name, factory): + name = name.upper() w3.geth.personal.unlock_account(w3.eth.coinbase, KEYFILE_PW) deploy_txn_hash = factory.constructor().transact({'from': w3.eth.coinbase}) - print('{0}_CONTRACT_DEPLOY_HASH: '.format(name.upper()), deploy_txn_hash) + print(f'{name}_CONTRACT_DEPLOY_HASH: {deploy_txn_hash}') deploy_receipt = mine_transaction_hash(w3, deploy_txn_hash) - print('{0}_CONTRACT_DEPLOY_TRANSACTION_MINED'.format(name.upper())) + print(f'{name}_CONTRACT_DEPLOY_TRANSACTION_MINED') contract_address = deploy_receipt['contractAddress'] assert is_checksum_address(contract_address) - print('{0}_CONTRACT_ADDRESS:'.format(name.upper()), contract_address) + print(f'{name}_CONTRACT_ADDRESS: {contract_address}') return deploy_receipt diff --git a/tests/integration/generate_fixtures/go_ethereum.py b/tests/integration/generate_fixtures/go_ethereum.py index d6d55b7ee2..eadbfb00bc 100644 --- a/tests/integration/generate_fixtures/go_ethereum.py +++ b/tests/integration/generate_fixtures/go_ethereum.py @@ -91,11 +91,8 @@ def get_geth_process(geth_binary, print( "Geth Process Exited:\n" - "stdout:{0}\n\n" - "stderr:{1}\n\n".format( - to_text(output), - to_text(errors), - ) + f"stdout:{to_text(output)}\n\n" + f"stderr:{to_text(errors)}\n\n" ) diff --git a/tests/integration/go_ethereum/conftest.py b/tests/integration/go_ethereum/conftest.py index 0862c47aa1..622e11f106 100644 --- a/tests/integration/go_ethereum/conftest.py +++ b/tests/integration/go_ethereum/conftest.py @@ -121,11 +121,8 @@ def geth_process(geth_binary, datadir, genesis_file, geth_command_arguments): output, errors = proc.communicate() print( "Geth Process Exited:\n" - "stdout:{0}\n\n" - "stderr:{1}\n\n".format( - to_text(output), - to_text(errors), - ) + f"stdout:{to_text(output)}\n\n" + f"stderr:{to_text(errors)}\n\n" ) diff --git a/tests/integration/go_ethereum/test_goethereum_http.py b/tests/integration/go_ethereum/test_goethereum_http.py index 53a624faad..ec209d02e1 100644 --- a/tests/integration/go_ethereum/test_goethereum_http.py +++ b/tests/integration/go_ethereum/test_goethereum_http.py @@ -58,7 +58,7 @@ def rpc_port(): @pytest.fixture(scope="module") def endpoint_uri(rpc_port): - return 'http://localhost:{0}'.format(rpc_port) + return f'http://localhost:{rpc_port}' def _geth_command_arguments(rpc_port, diff --git a/tests/integration/go_ethereum/test_goethereum_ws.py b/tests/integration/go_ethereum/test_goethereum_ws.py index 647681ede8..22367aa570 100644 --- a/tests/integration/go_ethereum/test_goethereum_ws.py +++ b/tests/integration/go_ethereum/test_goethereum_ws.py @@ -27,7 +27,7 @@ def ws_port(): @pytest.fixture(scope="module") def endpoint_uri(ws_port): - return 'ws://localhost:{0}'.format(ws_port) + return f'ws://localhost:{ws_port}' def _geth_command_arguments(ws_port, diff --git a/web3/_utils/abi.py b/web3/_utils/abi.py index 698f4151cd..aef5c6c137 100644 --- a/web3/_utils/abi.py +++ b/web3/_utils/abi.py @@ -290,9 +290,8 @@ def validate(self) -> None: if self.value_bit_size % 8 != 0: raise ValueError( - "Invalid value bit size: {0}. Must be a multiple of 8".format( - self.value_bit_size, - ) + f"Invalid value bit size: {self.value_bit_size}. " + "Must be a multiple of 8" ) if self.value_bit_size > self.data_byte_size * 8: @@ -328,13 +327,13 @@ def validate_value(self, value: Any) -> bytes: # type: ignore self.invalidate_value( value, exc=ValueOutOfBounds, - msg="exceeds total byte size for bytes{} encoding".format(byte_size), + msg=f"exceeds total byte size for bytes{byte_size} encoding", ) elif len(value) < byte_size: self.invalidate_value( value, exc=ValueOutOfBounds, - msg="less than total byte size for bytes{} encoding".format(byte_size), + msg=f"less than total byte size for bytes{byte_size} encoding", ) return value @@ -438,10 +437,8 @@ def merge_args_and_kwargs( # Ensure the function is being applied to the correct number of args if len(args) + len(kwargs) != len(function_abi.get('inputs', [])): raise TypeError( - "Incorrect argument count. Expected '{0}'. Got '{1}'".format( - len(function_abi['inputs']), - len(args) + len(kwargs), - ) + f"Incorrect argument count. Expected '{len(function_abi['inputs'])}" + f". Got '{len(args) + len(kwargs)}'" ) # If no keyword args were given, we don't need to align them @@ -456,10 +453,9 @@ def merge_args_and_kwargs( duplicate_args = kwarg_names.intersection(args_as_kwargs.keys()) if duplicate_args: raise TypeError( - "{fn_name}() got multiple values for argument(s) '{dups}'".format( - fn_name=function_abi['name'], - dups=', '.join(duplicate_args), - ) + f"{function_abi.get('name')}() got multiple values for " + f"argument{'s' if len(duplicate_args) > 1 else ''} " + f"'{', '.join(duplicate_args)}'" ) # Check for unknown args @@ -467,16 +463,14 @@ def merge_args_and_kwargs( if unknown_args: if function_abi.get('name'): raise TypeError( - "{fn_name}() got unexpected keyword argument(s) '{dups}'".format( - fn_name=function_abi.get('name'), - dups=', '.join(unknown_args), - ) + f"{function_abi.get('name')}() got unexpected keyword argument" + f"{'s' if len(unknown_args) > 1 else ''} " + f"{', '.join(unknown_args)}'" ) raise TypeError( - "Type: '{_type}' got unexpected keyword argument(s) '{dups}'".format( - _type=function_abi.get('type'), - dups=', '.join(unknown_args), - ) + f"Type: '{function_abi.get('type')}' got unexpected keyword " + f"argument{'s' if len(unknown_args) > 1 else ''} " + f"{', '.join(unknown_args)}'" ) # Sort args according to their position in the ABI and unzip them from their @@ -545,10 +539,8 @@ def _align_abi_input(arg_abi: ABIFunctionParams, arg: Any) -> Tuple[Any, ...]: if not is_list_like(aligned_arg): raise TypeError( - 'Expected non-string sequence for "{}" component type: got {}'.format( - arg_abi['type'], - aligned_arg, - ), + f'Expected non-string sequence for "{arg_abi.get("type")}" ' + f'component type: got {aligned_arg}' ) # convert NamedTuple to regular tuple @@ -604,9 +596,9 @@ def get_constructor_abi(contract_abi: ABI) -> ABIFunction: INT_SIZES = range(8, 257, 8) BYTES_SIZES = range(1, 33) -UINT_TYPES = ['uint{0}'.format(i) for i in INT_SIZES] -INT_TYPES = ['int{0}'.format(i) for i in INT_SIZES] -BYTES_TYPES = ['bytes{0}'.format(i) for i in BYTES_SIZES] + ['bytes32.byte'] +UINT_TYPES = [f'uint{i}' for i in INT_SIZES] +INT_TYPES = [f'int{i}' for i in INT_SIZES] +BYTES_TYPES = [f'bytes{i}' for i in BYTES_SIZES] + ['bytes32.byte'] STATIC_TYPES = list(itertools.chain( ['address', 'bool'], @@ -694,7 +686,7 @@ def size_of_type(abi_type: TypeStr) -> int: def sub_type_of_array_type(abi_type: TypeStr) -> str: if not is_array_type(abi_type): raise ValueError( - "Cannot parse subtype of nonarray abi-type: {0}".format(abi_type) + f"Cannot parse subtype of nonarray abi-type: {abi_type}" ) return re.sub(END_BRACKETS_OF_ARRAY_TYPE_REGEX, '', abi_type, 1) @@ -703,7 +695,7 @@ def sub_type_of_array_type(abi_type: TypeStr) -> str: def length_of_array_type(abi_type: TypeStr) -> int: if not is_array_type(abi_type): raise ValueError( - "Cannot parse length of nonarray abi-type: {0}".format(abi_type) + f"Cannot parse length of nonarray abi-type: {abi_type}" ) inner_brackets = re.search(END_BRACKETS_OF_ARRAY_TYPE_REGEX, abi_type).group(0).strip("[]") diff --git a/web3/_utils/async_transactions.py b/web3/_utils/async_transactions.py index 116fdf8898..7e6153e047 100644 --- a/web3/_utils/async_transactions.py +++ b/web3/_utils/async_transactions.py @@ -35,8 +35,8 @@ async def get_buffered_gas_estimate( if gas_estimate > gas_limit: raise ValueError( "Contract does not appear to be deployable within the " - "current network gas limits. Estimated: {0}. Current gas " - "limit: {1}".format(gas_estimate, gas_limit) + f"current network gas limits. Estimated: {gas_estimate}. " + f"Current gas limit: {gas_limit}" ) return min(gas_limit, gas_estimate + gas_buffer) diff --git a/web3/_utils/blocks.py b/web3/_utils/blocks.py index 63ff37328c..d05e33c38e 100644 --- a/web3/_utils/blocks.py +++ b/web3/_utils/blocks.py @@ -70,5 +70,6 @@ def select_method_for_block_identifier( return if_number else: raise ValueError( - "Value did not match any of the recognized block identifiers: {0}".format(value) + "Value did not match any of the recognized block identifiers: " + f"{value}" ) diff --git a/web3/_utils/caching.py b/web3/_utils/caching.py index 09cdf25335..ce11235312 100644 --- a/web3/_utils/caching.py +++ b/web3/_utils/caching.py @@ -39,7 +39,7 @@ def generate_cache_key(value: Any) -> str: in value ))) else: - raise TypeError("Cannot generate cache key for value {0} of type {1}".format( - value, - type(value), - )) + raise TypeError( + f"Cannot generate cache key for value {value} of type " + f"{type(value)}" + ) diff --git a/web3/_utils/contracts.py b/web3/_utils/contracts.py index 97a0b1d17c..fbddcf7768 100644 --- a/web3/_utils/contracts.py +++ b/web3/_utils/contracts.py @@ -146,18 +146,15 @@ def find_matching_fn_abi( ) message = ( - "\nCould not identify the intended function with name `{name}`, " - "positional argument(s) of type `{arg_types}` and " - "keyword argument(s) of type `{kwarg_types}`." - "\nFound {num_candidates} function(s) with the name `{name}`: {candidates}" - "{diagnosis}" - ).format( - name=fn_identifier, - arg_types=tuple(map(type, args)), - kwarg_types=valmap(type, kwargs), - num_candidates=len(matching_identifiers), - candidates=matching_function_signatures, - diagnosis=diagnosis, + f"\nCould not identify the intended function with name `" + f"{fn_identifier}`, positional argument" + f"{'s' if len(args) > 1 else ''} of type " + f"`{tuple(map(type, args))}` and keyword argument" + f"{'s' if len(kwargs) > 1 else ''} of type " + f"`{valmap(type, kwargs)}`.\nFound {len(matching_identifiers)} " + f"function{'s' if len(matching_identifiers) > 1 else ''} with " + f"the name `{fn_identifier}`" + f": {matching_function_signatures}{diagnosis}" ) raise ValidationError(message) @@ -171,9 +168,7 @@ def encode_abi( if not check_if_arguments_can_be_encoded(abi, w3.codec, arguments, {}): raise TypeError( "One or more arguments could not be encoded to the necessary " - "ABI type. Expected types are: {0}".format( - ', '.join(argument_types), - ) + f"ABI type. Expected types are: {', '.join(argument_types)}" ) normalizers = [ diff --git a/web3/_utils/datatypes.py b/web3/_utils/datatypes.py index 957de2a22e..110449fd75 100644 --- a/web3/_utils/datatypes.py +++ b/web3/_utils/datatypes.py @@ -18,9 +18,9 @@ def verify_attr(class_name: str, key: str, namespace: Collection[str]) -> None: if key not in namespace: raise AttributeError( - "Property {0} not found on {1} class. " - "`{1}.factory` only accepts keyword arguments which are " - "present on the {1} class".format(key, class_name) + f"Property {key} not found on {class_name} class. " + f"`{class_name}.factory` only accepts keyword arguments which are " + f"present on the {class_name} class" ) diff --git a/web3/_utils/encoding.py b/web3/_utils/encoding.py index 5b9a4a4a56..7482db29fa 100644 --- a/web3/_utils/encoding.py +++ b/web3/_utils/encoding.py @@ -88,7 +88,7 @@ def hex_encode_abi_type(abi_type: TypeStr, value: Any, return to_hex(text=value) else: raise ValueError( - "Unsupported ABI type: {0}".format(abi_type) + f"Unsupported ABI type: {abi_type}" ) @@ -169,9 +169,8 @@ def hexstr_if_str( (primitive, hexstr) = (None, hexstr_or_primitive) if remove_0x_prefix(HexStr(hexstr)) and not is_hex(hexstr): raise ValueError( - "when sending a str, it must be a hex string. Got: {0!r}".format( - hexstr_or_primitive, - ) + "when sending a str, it must be a hex string. Got: " + f"{hexstr_or_primitive!r}" ) else: (primitive, hexstr) = (hexstr_or_primitive, None) @@ -208,10 +207,15 @@ def _friendly_json_encode(self, obj: Dict[Any, Any], except TypeError as full_exception: if hasattr(obj, 'items'): item_errors = '; '.join(self._json_mapping_errors(obj)) - raise TypeError("dict had unencodable value at keys: {{{}}}".format(item_errors)) + raise TypeError( + "dict had unencodable value at keys: " + f"{{{item_errors}}}" + ) elif is_list_like(obj): element_errors = '; '.join(self._json_list_errors(obj)) - raise TypeError("list had unencodable value at index: [{}]".format(element_errors)) + raise TypeError( + f"list had unencodable value at index: [{element_errors}]" + ) else: raise full_exception @@ -220,7 +224,7 @@ def json_decode(self, json_str: str) -> Dict[Any, Any]: decoded = json.loads(json_str) return decoded except json.decoder.JSONDecodeError as exc: - err_msg = 'Could not decode {} because of {}.'.format(repr(json_str), exc) + err_msg = f'Could not decode {json_str!r} because of {exc}.' # Calling code may rely on catching JSONDecodeError to recognize bad json # so we have to re-raise the same type. raise json.decoder.JSONDecodeError(err_msg, exc.doc, exc.pos) @@ -230,7 +234,7 @@ def json_encode(self, obj: Dict[Any, Any], try: return self._friendly_json_encode(obj, cls=cls) except TypeError as exc: - raise TypeError("Could not encode to JSON: {}".format(exc)) + raise TypeError(f"Could not encode to JSON: {exc}") def to_4byte_hex(hex_or_str_or_bytes: Union[HexStr, str, bytes, int]) -> HexStr: diff --git a/web3/_utils/events.py b/web3/_utils/events.py index 9cfe6fb685..78bc60e94b 100644 --- a/web3/_utils/events.py +++ b/web3/_utils/events.py @@ -219,10 +219,10 @@ def get_event_data(abi_codec: ABICodec, event_abi: ABIEvent, log_entry: LogRecei log_topic_names = get_abi_input_names(ABIEvent({'inputs': log_topics_abi})) if len(log_topics) != len(log_topic_types): - raise LogTopicError("Expected {0} log topics. Got {1}".format( - len(log_topic_types), - len(log_topics), - )) + raise LogTopicError( + f"Expected {len(log_topic_types)} log topics. Got " + f"{len(log_topics)}" + ) log_data = hexstr_if_str(to_bytes, log_entry['data']) log_data_abi = exclude_indexed_event_inputs(event_abi) @@ -400,7 +400,7 @@ def filter_params(self) -> FilterParams: def deploy(self, w3: "Web3") -> "LogFilter": if not isinstance(w3, web3.Web3): - raise ValueError("Invalid web3 argument: got: {0}".format(repr(w3))) + raise ValueError(f"Invalid web3 argument: got: {w3!r}") for arg in AttributeDict.values(self.args): arg._immutable = True diff --git a/web3/_utils/filters.py b/web3/_utils/filters.py index b6a77c385f..79d3ae734f 100644 --- a/web3/_utils/filters.py +++ b/web3/_utils/filters.py @@ -97,7 +97,7 @@ def construct_event_filter_params( filter_params['address'] = [address, contract_address] else: raise ValueError( - "Unsupported type for `address` parameter: {0}".format(type(address)) + f"Unsupported type for `address` parameter: {type(address)}" ) elif address: filter_params['address'] = address diff --git a/web3/_utils/http.py b/web3/_utils/http.py index 80c7e31296..e244183877 100644 --- a/web3/_utils/http.py +++ b/web3/_utils/http.py @@ -1,8 +1,5 @@ def construct_user_agent(class_name: str) -> str: from web3 import __version__ as web3_version - user_agent = 'Web3.py/{version}/{class_name}'.format( - version=web3_version, - class_name=class_name, - ) + user_agent = f'Web3.py/{web3_version}/{class_name}' return user_agent diff --git a/web3/_utils/math.py b/web3/_utils/math.py index cb983f235b..96052bb00d 100644 --- a/web3/_utils/math.py +++ b/web3/_utils/math.py @@ -14,9 +14,10 @@ def percentile(values: Optional[Sequence[int]] = None, """ if values in [None, tuple(), []] or len(values) < 1: raise InsufficientData( - "Expected a sequence of at least 1 integers, got {0!r}".format(values)) + f"Expected a sequence of at least 1 integers, got {values!r}" + ) if percentile is None: - raise ValueError("Expected a percentile choice, got {0}".format(percentile)) + raise ValueError(f"Expected a percentile choice, got {percentile}") sorted_values = sorted(values) diff --git a/web3/_utils/module_testing/go_ethereum_admin_module.py b/web3/_utils/module_testing/go_ethereum_admin_module.py index 38505deec7..0fa20285ff 100644 --- a/web3/_utils/module_testing/go_ethereum_admin_module.py +++ b/web3/_utils/module_testing/go_ethereum_admin_module.py @@ -98,7 +98,7 @@ def test_admin_nodeInfo(self, w3: "Web3") -> None: 'protocols': AttributeDict({}) }) # Test that result gives at least the keys that are listed in `expected` - assert not set(expected.keys()).difference(result.keys()) + assert not set(expected).difference(result) class GoEthereumAsyncAdminModuleTest: diff --git a/web3/_utils/rpc_abi.py b/web3/_utils/rpc_abi.py index 641497ef42..4ebf435919 100644 --- a/web3/_utils/rpc_abi.py +++ b/web3/_utils/rpc_abi.py @@ -219,7 +219,7 @@ def apply_abi_formatters_to_dict( abi_dict: Dict[str, Any], data: Dict[Any, Any] ) -> Dict[Any, Any]: - fields = list(set(abi_dict.keys()) & set(data.keys())) + fields = list(abi_dict.keys() & data.keys()) formatted_values = map_abi_data( normalizers, [abi_dict[field] for field in fields], diff --git a/web3/_utils/threads.py b/web3/_utils/threads.py index ba45d8775e..435807dbbe 100644 --- a/web3/_utils/threads.py +++ b/web3/_utils/threads.py @@ -49,7 +49,7 @@ def __exit__( def __str__(self) -> str: if self.seconds is None: return '' - return "{0} seconds".format(self.seconds) + return f"{self.seconds} seconds" @property def expire_at(self) -> int: diff --git a/web3/_utils/transactions.py b/web3/_utils/transactions.py index 638cb89d50..40acdf0780 100644 --- a/web3/_utils/transactions.py +++ b/web3/_utils/transactions.py @@ -137,8 +137,8 @@ def get_buffered_gas_estimate( if gas_estimate > gas_limit: raise ValueError( "Contract does not appear to be deployable within the " - "current network gas limits. Estimated: {0}. Current gas " - "limit: {1}".format(gas_estimate, gas_limit) + f"current network gas limits. Estimated: {gas_estimate}. " + f"Current gas limit: {gas_limit}" ) return min(gas_limit, gas_estimate + gas_buffer) @@ -189,7 +189,7 @@ def extract_valid_transaction_params(transaction_params: TxData) -> TxParams: def assert_valid_transaction_params(transaction_params: TxParams) -> None: for param in transaction_params: if param not in VALID_TRANSACTION_PARAMS: - raise ValueError('{} is not a valid transaction parameter'.format(param)) + raise ValueError(f'{param} is not a valid transaction parameter') def prepare_replacement_transaction( diff --git a/web3/_utils/validation.py b/web3/_utils/validation.py index d59e788d61..d6080e340c 100644 --- a/web3/_utils/validation.py +++ b/web3/_utils/validation.py @@ -87,7 +87,7 @@ def validate_abi(abi: ABI) -> None: if duplicates: raise ValueError( 'Abi contains functions with colliding selectors. ' - 'Functions {0}'.format(_prepare_selector_collision_msg(duplicates)) + f'Functions {_prepare_selector_collision_msg(duplicates)}' ) @@ -96,7 +96,7 @@ def validate_abi_type(abi_type: TypeStr) -> None: Helper function for validating an abi_type """ if not is_recognized_type(abi_type): - raise ValueError("Unrecognized abi_type: {abi_type}".format(abi_type=abi_type)) + raise ValueError(f"Unrecognized abi_type: {abi_type}") def validate_abi_value(abi_type: TypeStr, value: Any) -> None: @@ -110,15 +110,13 @@ def validate_abi_value(abi_type: TypeStr, value: Any) -> None: if specified_length is not None: if specified_length < 1: raise TypeError( - "Invalid abi-type: {abi_type}. Length of fixed sized arrays" - "must be greater than 0." - .format(abi_type=abi_type) + f"Invalid abi-type: {abi_type}. Length of fixed sized " + "arrays must be greater than 0." ) if specified_length != len(value): raise TypeError( "The following array length does not the length specified" - "by the abi-type, {abi_type}: {value}" - .format(abi_type=abi_type, value=value) + f"by the abi-type, {abi_type}: {value}" ) # validate sub_types @@ -150,8 +148,7 @@ def validate_abi_value(abi_type: TypeStr, value: Any) -> None: return raise TypeError( - "The following abi value is not a '{abi_type}': {value}" - .format(abi_type=abi_type, value=value) + f"The following abi value is not a '{abi_type}': {value}" ) @@ -174,7 +171,7 @@ def validate_address(value: Any) -> None: return if not isinstance(value, str): - raise TypeError('Address {} must be provided as a string'.format(value)) + raise TypeError(f'Address {value} must be provided as a string') if not is_hex_address(value): raise InvalidAddress("Address must be 20 bytes, as a hex string with a 0x prefix", value) if not is_checksum_address(value): diff --git a/web3/contract.py b/web3/contract.py index c20d0e2156..0f152ce02a 100644 --- a/web3/contract.py +++ b/web3/contract.py @@ -192,7 +192,7 @@ def __getattr__(self, function_name: str) -> "ContractFunction": ) elif function_name not in self.__dict__['_functions']: raise ABIFunctionNotFound( - "The function '{}' was not found in this contract's abi. ".format(function_name), + f"The function '{function_name}' was not found in this contract's abi. ", "Are you sure you provided the correct contract abi?" ) else: @@ -252,7 +252,7 @@ def __getattr__(self, event_name: str) -> Type['ContractEvent']: ) elif event_name not in self.__dict__['_events']: raise ABIEventFunctionNotFound( - "The event '{}' was not found in this contract's abi. ".format(event_name), + f"The event '{event_name}' was not found in this contract's abi. ", "Are you sure you provided the correct contract abi?" ) else: @@ -585,7 +585,7 @@ def _encode_constructor_data(cls, args: Optional[Any] = None, def mk_collision_prop(fn_name: str) -> Callable[[], None]: def collision_fn() -> NoReturn: - msg = "Namespace collision for function name {0} with ConciseContract API.".format(fn_name) + msg = f"Namespace collision for function name {fn_name} with ConciseContract API." raise AttributeError(msg) collision_fn.__name__ = fn_name return collision_fn @@ -687,10 +687,11 @@ def buildTransaction(self, transaction: Optional[TxParams] = None) -> TxParams: def check_forbidden_keys_in_transaction( transaction: TxParams, forbidden_keys: Optional[Collection[str]] = None ) -> None: - keys_found = set(transaction.keys()) & set(forbidden_keys) + keys_found = transaction.keys() & forbidden_keys if keys_found: raise ValueError( - "Cannot set '{}' field(s) in transaction".format(', '.join(keys_found)) + f"Cannot set '{', '.join(keys_found)}'" + f"field{'s' if len(keys_found) > 1 else ''} in transaction" ) @@ -1412,12 +1413,12 @@ def __getattr__(self, function_name: str) -> Any: "The ABI for this contract contains no function definitions. ", "Are you sure you provided the correct contract ABI?" ) - elif function_name not in set(fn['name'] for fn in self._functions): + elif function_name not in {fn['name'] for fn in self._functions}: functions_available = ', '.join([fn['name'] for fn in self._functions]) raise ABIFunctionNotFound( - "The function '{}' was not found in this contract's ABI. ".format(function_name), + f"The function '{function_name}' was not found in this contract's ABI. ", "Here is a list of all of the function names found: ", - "{}. ".format(functions_available), + f"{functions_available}. ", "Did you mean to call one of those functions?" ) else: @@ -1673,11 +1674,11 @@ def get_function_by_identifier( ) -> ContractFunction: if len(fns) > 1: raise ValueError( - 'Found multiple functions with matching {0}. ' - 'Found: {1!r}'.format(identifier, fns) + f'Found multiple functions with matching {identifier}. ' + f'Found: {fns!r}' ) elif len(fns) == 0: raise ValueError( - 'Could not find any function with matching {0}'.format(identifier) + f'Could not find any function with matching {identifier}' ) return fns[0] diff --git a/web3/main.py b/web3/main.py index 5395895683..666ef18e03 100644 --- a/web3/main.py +++ b/web3/main.py @@ -314,7 +314,7 @@ def solidityKeccak(cls, abi_types: List[TypeStr], values: List[Any]) -> bytes: if len(abi_types) != len(values): raise ValueError( "Length mismatch between provided abi types and values. Got " - "{0} types and {1} values.".format(len(abi_types), len(values)) + f"{len(abi_types)} types and {len(values)} values." ) if isinstance(cls, type): diff --git a/web3/manager.py b/web3/manager.py index db5c6085b4..6cc2b68e44 100644 --- a/web3/manager.py +++ b/web3/manager.py @@ -230,7 +230,7 @@ def receive_blocking(self, request_id: UUID, timeout: Optional[float] = None) -> try: request = self.pending_requests.pop(request_id) except KeyError: - raise KeyError("Request for id:{0} not found".format(request_id)) + raise KeyError(f"Request for id:{request_id} not found") else: response = request.get(timeout=timeout) diff --git a/web3/middleware/filter.py b/web3/middleware/filter.py index 5b2f82a417..dee73d7be3 100644 --- a/web3/middleware/filter.py +++ b/web3/middleware/filter.py @@ -293,15 +293,15 @@ def get_logs(self) -> List[LogReceipt]: "fromBlock": "from_block" } -NEW_FILTER_METHODS = set([ +NEW_FILTER_METHODS = { "eth_newBlockFilter", "eth_newFilter", -]) +} -FILTER_CHANGES_METHODS = set([ +FILTER_CHANGES_METHODS = { "eth_getFilterChanges", "eth_getFilterLogs", -]) +} class RequestBlocks: diff --git a/web3/middleware/signing.py b/web3/middleware/signing.py index 4ed76a9830..b0f121f243 100644 --- a/web3/middleware/signing.py +++ b/web3/middleware/signing.py @@ -93,7 +93,7 @@ def to_account(val: Any) -> LocalAccount: "key must be one of the types: " "eth_keys.datatype.PrivateKey, eth_account.signers.local.LocalAccount, " "or raw private key as a hex string or byte string. " - "Was of type {0}".format(type(val))) + f"Was of type {type(val)}") @to_account.register(LocalAccount) diff --git a/web3/middleware/stalecheck.py b/web3/middleware/stalecheck.py index 73e8158770..e8d98188ad 100644 --- a/web3/middleware/stalecheck.py +++ b/web3/middleware/stalecheck.py @@ -20,9 +20,9 @@ if TYPE_CHECKING: from web3 import Web3 # noqa: F401 -SKIP_STALECHECK_FOR_METHODS = set([ +SKIP_STALECHECK_FOR_METHODS = { 'eth_getBlockByNumber', -]) +} def _isfresh(block: BlockData, allowable_delay: int) -> bool: diff --git a/web3/providers/async_rpc.py b/web3/providers/async_rpc.py index dc80bfdedf..04f27f3b3e 100644 --- a/web3/providers/async_rpc.py +++ b/web3/providers/async_rpc.py @@ -58,7 +58,7 @@ async def cache_async_session(self, session: ClientSession) -> None: await _cache_async_session(self.endpoint_uri, session) def __str__(self) -> str: - return "RPC connection {0}".format(self.endpoint_uri) + return f"RPC connection {self.endpoint_uri}" @to_dict def get_request_kwargs(self) -> Iterable[Tuple[str, Any]]: diff --git a/web3/providers/auto.py b/web3/providers/auto.py index 9bc49b893b..47f3f4e933 100644 --- a/web3/providers/auto.py +++ b/web3/providers/auto.py @@ -103,10 +103,9 @@ def _proxy_request(self, method: RPCEndpoint, params: Any, if provider is None: raise CannotHandleRequest( "Could not discover provider while making request: " - "method:{0}\n" - "params:{1}\n".format( - method, - params)) + f"method:{method}\n" + f"params:{params}\n" + ) return provider.make_request(method, params) diff --git a/web3/providers/eth_tester/defaults.py b/web3/providers/eth_tester/defaults.py index a33d337ef6..5c190217f8 100644 --- a/web3/providers/eth_tester/defaults.py +++ b/web3/providers/eth_tester/defaults.py @@ -102,11 +102,9 @@ def inner(*args: Any, **kwargs: Any) -> TValue: def client_version(eth_tester: "EthereumTester", params: Any) -> str: # TODO: account for the backend that is in use. from eth_tester import __version__ - return "EthereumTester/{version}/{platform}/python{v.major}.{v.minor}.{v.micro}".format( - version=__version__, - v=sys.version_info, - platform=sys.platform, - ) + v = sys.version_info + return f"EthereumTester/{__version__}/{sys.platform}/python{v.major}" \ + f".{v.minor}.{v.micro}" @curry diff --git a/web3/providers/ipc.py b/web3/providers/ipc.py index a166aabd80..1e7250451b 100644 --- a/web3/providers/ipc.py +++ b/web3/providers/ipc.py @@ -153,8 +153,8 @@ def get_default_ipc_path() -> str: # type: ignore else: raise ValueError( - "Unsupported platform '{0}'. Only darwin/linux/win32/freebsd are " - "supported. You must specify the ipc_path".format(sys.platform) + f"Unsupported platform '{sys.platform}'. Only darwin/linux/win32/" + "freebsd are supported. You must specify the ipc_path" ) @@ -202,8 +202,8 @@ def get_dev_ipc_path() -> str: # type: ignore else: raise ValueError( - "Unsupported platform '{0}'. Only darwin/linux/win32/freebsd are " - "supported. You must specify the ipc_path".format(sys.platform) + f"Unsupported platform '{sys.platform}'. Only darwin/linux/win32/" + "freebsd are supported. You must specify the ipc_path" ) diff --git a/web3/providers/rpc.py b/web3/providers/rpc.py index 74c83eac7e..13e3156aa5 100644 --- a/web3/providers/rpc.py +++ b/web3/providers/rpc.py @@ -66,7 +66,7 @@ def __init__( super().__init__() def __str__(self) -> str: - return "RPC connection {0}".format(self.endpoint_uri) + return f"RPC connection {self.endpoint_uri}" @to_dict def get_request_kwargs(self) -> Iterable[Tuple[str, Any]]: diff --git a/web3/providers/websocket.py b/web3/providers/websocket.py index ef486ff49c..0713fa830c 100644 --- a/web3/providers/websocket.py +++ b/web3/providers/websocket.py @@ -103,13 +103,13 @@ def __init__( if websocket_kwargs is None: websocket_kwargs = {} else: - found_restricted_keys = set(websocket_kwargs.keys()).intersection( + found_restricted_keys = set(websocket_kwargs).intersection( RESTRICTED_WEBSOCKET_KWARGS ) if found_restricted_keys: raise ValidationError( - '{0} are not allowed in websocket_kwargs, ' - 'found: {1}'.format(RESTRICTED_WEBSOCKET_KWARGS, found_restricted_keys) + f'{RESTRICTED_WEBSOCKET_KWARGS} are not allowed ' + f'in websocket_kwargs, found: {found_restricted_keys}' ) self.conn = PersistentWebSocket( self.endpoint_uri, websocket_kwargs @@ -117,7 +117,7 @@ def __init__( super().__init__() def __str__(self) -> str: - return "WS connection {0}".format(self.endpoint_uri) + return f"WS connection {self.endpoint_uri}" async def coro_make_request(self, request_data: bytes) -> RPCResponse: async with self.conn as conn: diff --git a/web3/tools/benchmark/node.py b/web3/tools/benchmark/node.py index 86fa3416b7..2cc23d0fe6 100644 --- a/web3/tools/benchmark/node.py +++ b/web3/tools/benchmark/node.py @@ -59,7 +59,7 @@ def _rpc_port(self) -> str: return str(port) def _endpoint_uri(self) -> str: - return "http://localhost:{0}".format(self.rpc_port) + return f"http://localhost:{self.rpc_port}" def _geth_binary(self) -> str: if "GETH_BINARY" in os.environ: From 1adadd48072f0033aaf91b0483264050ff64e96e Mon Sep 17 00:00:00 2001 From: Harmouch101 Date: Thu, 10 Mar 2022 10:03:48 +0200 Subject: [PATCH 05/15] :zap: resolve issues! Signed-off-by: Harmouch101 --- tests/ethpm/validation/test_manifest.py | 4 ++-- web3/_utils/abi.py | 18 +++++++----------- web3/_utils/blocks.py | 3 +-- web3/_utils/caching.py | 3 +-- web3/_utils/contracts.py | 17 +++++++---------- web3/_utils/encoding.py | 6 ++---- web3/_utils/events.py | 3 +-- web3/contract.py | 4 ++-- web3/main.py | 9 ++++----- web3/providers/auto.py | 6 ++---- web3/providers/eth_tester/defaults.py | 3 +-- web3/providers/ipc.py | 3 +-- 12 files changed, 31 insertions(+), 48 deletions(-) diff --git a/tests/ethpm/validation/test_manifest.py b/tests/ethpm/validation/test_manifest.py index 348e2ff388..6633830f09 100644 --- a/tests/ethpm/validation/test_manifest.py +++ b/tests/ethpm/validation/test_manifest.py @@ -78,7 +78,7 @@ def test_validate_deployments_without_deployment(manifest_with_no_deployments): @pytest.mark.parametrize( "data,expected", ( - ([], {}), + ([], set()), ([{"some": {"contractType": "one"}}], {"one"}), ( [{"some": {"contractType": "one"}, "other": {"contractType": "two"}}], @@ -88,7 +88,7 @@ def test_validate_deployments_without_deployment(manifest_with_no_deployments): ) def test_extract_contract_types_from_deployments(data, expected): actual = extract_contract_types_from_deployments(data) - assert actual == set(expected) + assert actual == expected def test_validate_manifest_version_validates_version_three_string(): diff --git a/web3/_utils/abi.py b/web3/_utils/abi.py index aef5c6c137..55935de423 100644 --- a/web3/_utils/abi.py +++ b/web3/_utils/abi.py @@ -290,8 +290,7 @@ def validate(self) -> None: if self.value_bit_size % 8 != 0: raise ValueError( - f"Invalid value bit size: {self.value_bit_size}. " - "Must be a multiple of 8" + f"Invalid value bit size: {self.value_bit_size}. Must be a multiple of 8" ) if self.value_bit_size > self.data_byte_size * 8: @@ -452,25 +451,22 @@ def merge_args_and_kwargs( # Check for duplicate args duplicate_args = kwarg_names.intersection(args_as_kwargs.keys()) if duplicate_args: + s = 's' if len(duplicate_args) > 1 else '' raise TypeError( - f"{function_abi.get('name')}() got multiple values for " - f"argument{'s' if len(duplicate_args) > 1 else ''} " - f"'{', '.join(duplicate_args)}'" + f"{function_abi.get('name')}() got multiple values for argument{s} '{', '.join(duplicate_args)}'" ) # Check for unknown args unknown_args = kwarg_names.difference(sorted_arg_names) if unknown_args: + s = 's' if len(unknown_args) > 1 else '' + unknown_args = ', '.join(unknown_args) if function_abi.get('name'): raise TypeError( - f"{function_abi.get('name')}() got unexpected keyword argument" - f"{'s' if len(unknown_args) > 1 else ''} " - f"{', '.join(unknown_args)}'" + f"{function_abi.get('name')}() got unexpected keyword argument{s} '{unknown_args}'" ) raise TypeError( - f"Type: '{function_abi.get('type')}' got unexpected keyword " - f"argument{'s' if len(unknown_args) > 1 else ''} " - f"{', '.join(unknown_args)}'" + f"Type: '{function_abi.get('type')}' got unexpected keyword argument{s} '{unknown_args}'" ) # Sort args according to their position in the ABI and unzip them from their diff --git a/web3/_utils/blocks.py b/web3/_utils/blocks.py index d05e33c38e..dd6c237c76 100644 --- a/web3/_utils/blocks.py +++ b/web3/_utils/blocks.py @@ -70,6 +70,5 @@ def select_method_for_block_identifier( return if_number else: raise ValueError( - "Value did not match any of the recognized block identifiers: " - f"{value}" + f"Value did not match any of the recognized block identifiers: {value}" ) diff --git a/web3/_utils/caching.py b/web3/_utils/caching.py index ce11235312..ee78b48c16 100644 --- a/web3/_utils/caching.py +++ b/web3/_utils/caching.py @@ -40,6 +40,5 @@ def generate_cache_key(value: Any) -> str: ))) else: raise TypeError( - f"Cannot generate cache key for value {value} of type " - f"{type(value)}" + f"Cannot generate cache key for value {value} of type {type(value)}" ) diff --git a/web3/_utils/contracts.py b/web3/_utils/contracts.py index fbddcf7768..2623e5a11f 100644 --- a/web3/_utils/contracts.py +++ b/web3/_utils/contracts.py @@ -144,17 +144,14 @@ def find_matching_fn_abi( "\nAmbiguous argument encoding. " "Provided arguments can be encoded to multiple functions matching this call." ) - + s_a = 's' if len(args) > 1 else '' + s_k = 's' if len(kwargs) > 1 else '' + s_m = 's' if len(matching_identifiers) > 1 else '' message = ( - f"\nCould not identify the intended function with name `" - f"{fn_identifier}`, positional argument" - f"{'s' if len(args) > 1 else ''} of type " - f"`{tuple(map(type, args))}` and keyword argument" - f"{'s' if len(kwargs) > 1 else ''} of type " - f"`{valmap(type, kwargs)}`.\nFound {len(matching_identifiers)} " - f"function{'s' if len(matching_identifiers) > 1 else ''} with " - f"the name `{fn_identifier}`" - f": {matching_function_signatures}{diagnosis}" + f"\nCould not identify the intended function with name `{fn_identifier}`, positional argument" + f"{s_a} of type `{tuple(map(type, args))}` and keyword argument{s_k} of type " + f"`{valmap(type, kwargs)}`.\nFound {len(matching_identifiers)} function{s_m} with " + f"the name `{fn_identifier}` {matching_function_signatures}{diagnosis}" ) raise ValidationError(message) diff --git a/web3/_utils/encoding.py b/web3/_utils/encoding.py index 7482db29fa..b8adfd88d1 100644 --- a/web3/_utils/encoding.py +++ b/web3/_utils/encoding.py @@ -169,8 +169,7 @@ def hexstr_if_str( (primitive, hexstr) = (None, hexstr_or_primitive) if remove_0x_prefix(HexStr(hexstr)) and not is_hex(hexstr): raise ValueError( - "when sending a str, it must be a hex string. Got: " - f"{hexstr_or_primitive!r}" + f"when sending a str, it must be a hex string. Got: {hexstr_or_primitive!r}" ) else: (primitive, hexstr) = (hexstr_or_primitive, None) @@ -208,8 +207,7 @@ def _friendly_json_encode(self, obj: Dict[Any, Any], if hasattr(obj, 'items'): item_errors = '; '.join(self._json_mapping_errors(obj)) raise TypeError( - "dict had unencodable value at keys: " - f"{{{item_errors}}}" + f"dict had unencodable value at keys: {{{item_errors}}}" ) elif is_list_like(obj): element_errors = '; '.join(self._json_list_errors(obj)) diff --git a/web3/_utils/events.py b/web3/_utils/events.py index 78bc60e94b..dbb1bcbf27 100644 --- a/web3/_utils/events.py +++ b/web3/_utils/events.py @@ -220,8 +220,7 @@ def get_event_data(abi_codec: ABICodec, event_abi: ABIEvent, log_entry: LogRecei if len(log_topics) != len(log_topic_types): raise LogTopicError( - f"Expected {len(log_topic_types)} log topics. Got " - f"{len(log_topics)}" + f"Expected {len(log_topic_types)} log topics. Got {len(log_topics)}" ) log_data = hexstr_if_str(to_bytes, log_entry['data']) diff --git a/web3/contract.py b/web3/contract.py index 0f152ce02a..b88f76c2a2 100644 --- a/web3/contract.py +++ b/web3/contract.py @@ -689,9 +689,9 @@ def check_forbidden_keys_in_transaction( ) -> None: keys_found = transaction.keys() & forbidden_keys if keys_found: + s = 's' if len(keys_found) > 1 else '' raise ValueError( - f"Cannot set '{', '.join(keys_found)}'" - f"field{'s' if len(keys_found) > 1 else ''} in transaction" + f"Cannot set '{', '.join(keys_found)}' field{s} in transaction" ) diff --git a/web3/main.py b/web3/main.py index 666ef18e03..c8cb108922 100644 --- a/web3/main.py +++ b/web3/main.py @@ -292,11 +292,10 @@ def keccak(primitive: Optional[Primitives] = None, text: Optional[str] = None, return eth_utils_keccak(input_bytes) raise TypeError( - f"You called keccak with first arg {primitive!r} and keywords " - f"{{'text': {text!r}, 'hexstr': {hexstr!r}}}. You must call it " - "with one of these approaches: keccak(text='txt'), keccak " - "(hexstr='0x747874'), keccak(b'\\x74\\x78\\x74'), or kecca " - "(0x747874)." + f"You called keccak with first arg {primitive!r} and keywords {{'text': {text!r}, " + f"'hexstr': {hexstr!r}}}. You must call it with one of these approaches: " + "keccak(text='txt'), keccak(hexstr='0x747874'), keccak(b'\\x74\\x78\\x74'), " + "or keccak(0x747874)." ) @combomethod diff --git a/web3/providers/auto.py b/web3/providers/auto.py index 47f3f4e933..362640b588 100644 --- a/web3/providers/auto.py +++ b/web3/providers/auto.py @@ -55,8 +55,7 @@ def load_provider_from_uri( return WebsocketProvider(uri_string) else: raise NotImplementedError( - f'Web3 does not know how to connect to scheme {uri.scheme!r} ' - f'in {uri_string!r}' + f'Web3 does not know how to connect to scheme {uri.scheme!r} in {uri_string!r}' ) @@ -103,8 +102,7 @@ def _proxy_request(self, method: RPCEndpoint, params: Any, if provider is None: raise CannotHandleRequest( "Could not discover provider while making request: " - f"method:{method}\n" - f"params:{params}\n" + f"method:{method}\nparams:{params}\n" ) return provider.make_request(method, params) diff --git a/web3/providers/eth_tester/defaults.py b/web3/providers/eth_tester/defaults.py index 5c190217f8..544da5e863 100644 --- a/web3/providers/eth_tester/defaults.py +++ b/web3/providers/eth_tester/defaults.py @@ -103,8 +103,7 @@ def client_version(eth_tester: "EthereumTester", params: Any) -> str: # TODO: account for the backend that is in use. from eth_tester import __version__ v = sys.version_info - return f"EthereumTester/{__version__}/{sys.platform}/python{v.major}" \ - f".{v.minor}.{v.micro}" + return f"EthereumTester/{__version__}/{sys.platform}/python{v.major}.{v.minor}.{v.micro}" @curry diff --git a/web3/providers/ipc.py b/web3/providers/ipc.py index 1e7250451b..9bb3903b32 100644 --- a/web3/providers/ipc.py +++ b/web3/providers/ipc.py @@ -53,8 +53,7 @@ def __init__(self, ipc_path: str) -> None: def __enter__(self) -> socket.socket: if not self.ipc_path: raise FileNotFoundError( - "cannot connect to IPC socket at path: " - f"{self.ipc_path!r}" + f"cannot connect to IPC socket at path: {self.ipc_path!r}" ) if not self.sock: From 4f560c3eec04b562a401e6784d40e1687ec7d89e Mon Sep 17 00:00:00 2001 From: Harmouch101 Date: Thu, 10 Mar 2022 10:09:39 +0200 Subject: [PATCH 06/15] :memo: update newsfragment! Signed-off-by: Harmouch101 --- newsfragments/2372.misc.rst | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/newsfragments/2372.misc.rst b/newsfragments/2372.misc.rst index 85a5d056e1..bd2a98be31 100644 --- a/newsfragments/2372.misc.rst +++ b/newsfragments/2372.misc.rst @@ -1 +1,6 @@ -remove python 2 syntax. \ No newline at end of file +improve the code base by: +- using `f-strings`. +- removing redundant `set()` calls. +- making error messages more readable in the code. +- removing python2 `u` prefix. +- other cleanups. \ No newline at end of file From 2904d15232219f5b8eba48a30f46660fe2c89cf7 Mon Sep 17 00:00:00 2001 From: Harmouch101 Date: Thu, 10 Mar 2022 10:18:54 +0200 Subject: [PATCH 07/15] :bug: fix lint issues Signed-off-by: Harmouch101 --- web3/_utils/abi.py | 9 +++++---- web3/_utils/contracts.py | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/web3/_utils/abi.py b/web3/_utils/abi.py index 55935de423..6f69ef0309 100644 --- a/web3/_utils/abi.py +++ b/web3/_utils/abi.py @@ -452,21 +452,22 @@ def merge_args_and_kwargs( duplicate_args = kwarg_names.intersection(args_as_kwargs.keys()) if duplicate_args: s = 's' if len(duplicate_args) > 1 else '' + duplicate_arg = ', '.join(duplicate_args) raise TypeError( - f"{function_abi.get('name')}() got multiple values for argument{s} '{', '.join(duplicate_args)}'" + f"{function_abi.get('name')}() got multiple values for argument{s} '{duplicate_arg}'" ) # Check for unknown args unknown_args = kwarg_names.difference(sorted_arg_names) if unknown_args: s = 's' if len(unknown_args) > 1 else '' - unknown_args = ', '.join(unknown_args) + unknown_arg = ', '.join(unknown_args) if function_abi.get('name'): raise TypeError( - f"{function_abi.get('name')}() got unexpected keyword argument{s} '{unknown_args}'" + f"{function_abi.get('name')}() got unexpected keyword argument{s} '{unknown_arg}'" ) raise TypeError( - f"Type: '{function_abi.get('type')}' got unexpected keyword argument{s} '{unknown_args}'" + f"Type: '{function_abi.get('type')}' got unexpected keyword argument{s} '{unknown_arg}'" ) # Sort args according to their position in the ABI and unzip them from their diff --git a/web3/_utils/contracts.py b/web3/_utils/contracts.py index 2623e5a11f..81ce4b191a 100644 --- a/web3/_utils/contracts.py +++ b/web3/_utils/contracts.py @@ -148,8 +148,8 @@ def find_matching_fn_abi( s_k = 's' if len(kwargs) > 1 else '' s_m = 's' if len(matching_identifiers) > 1 else '' message = ( - f"\nCould not identify the intended function with name `{fn_identifier}`, positional argument" - f"{s_a} of type `{tuple(map(type, args))}` and keyword argument{s_k} of type " + f"\nCould not identify the intended function with name `{fn_identifier}`, positional " + f"argument {s_a} of type `{tuple(map(type, args))}` and keyword argument{s_k} of type " f"`{valmap(type, kwargs)}`.\nFound {len(matching_identifiers)} function{s_m} with " f"the name `{fn_identifier}` {matching_function_signatures}{diagnosis}" ) From e9a279f20cc6abae571d562ee99861a1563793c3 Mon Sep 17 00:00:00 2001 From: Harmouch101 Date: Thu, 10 Mar 2022 10:35:04 +0200 Subject: [PATCH 08/15] :zap: fix message error! Signed-off-by: Harmouch101 --- web3/_utils/contracts.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web3/_utils/contracts.py b/web3/_utils/contracts.py index 81ce4b191a..a4ed743ff6 100644 --- a/web3/_utils/contracts.py +++ b/web3/_utils/contracts.py @@ -149,9 +149,9 @@ def find_matching_fn_abi( s_m = 's' if len(matching_identifiers) > 1 else '' message = ( f"\nCould not identify the intended function with name `{fn_identifier}`, positional " - f"argument {s_a} of type `{tuple(map(type, args))}` and keyword argument{s_k} of type " + f"argument{s_a} of type `{tuple(map(type, args))}` and keyword argument{s_k} of type " f"`{valmap(type, kwargs)}`.\nFound {len(matching_identifiers)} function{s_m} with " - f"the name `{fn_identifier}` {matching_function_signatures}{diagnosis}" + f"the name `{fn_identifier}`: {matching_function_signatures}{diagnosis}" ) raise ValidationError(message) From dca17957af20799dfd4cf372b5ce7d7dd838113c Mon Sep 17 00:00:00 2001 From: Harmouch101 Date: Thu, 10 Mar 2022 18:33:31 +0200 Subject: [PATCH 09/15] :rewind: revert changes! Signed-off-by: Harmouch101 --- .../core/contracts/test_contract_call_interface.py | 6 +++--- web3/_utils/abi.py | 13 ++++++------- web3/_utils/contracts.py | 7 ++----- web3/contract.py | 3 +-- 4 files changed, 12 insertions(+), 17 deletions(-) diff --git a/tests/core/contracts/test_contract_call_interface.py b/tests/core/contracts/test_contract_call_interface.py index 34a754191c..4e67c7d1cd 100644 --- a/tests/core/contracts/test_contract_call_interface.py +++ b/tests/core/contracts/test_contract_call_interface.py @@ -602,9 +602,9 @@ def test_returns_data_from_specified_block(w3, math_contract): message_regex = ( r"\nCould not identify the intended function with name `.*`, " - r"positional arguments? of type `.*` and " - r"keyword arguments? of type `.*`." - r"\nFound .* functions? with the name `.*`: .*" + r"positional argument\(s\) of type `.*` and " + r"keyword argument\(s\) of type `.*`." + r"\nFound .* function\(s\) with the name `.*`: .*" ) diagnosis_arg_regex = ( r"\nFunction invocation failed due to improper number of arguments." diff --git a/web3/_utils/abi.py b/web3/_utils/abi.py index 6f69ef0309..791f7bdfd5 100644 --- a/web3/_utils/abi.py +++ b/web3/_utils/abi.py @@ -451,23 +451,22 @@ def merge_args_and_kwargs( # Check for duplicate args duplicate_args = kwarg_names.intersection(args_as_kwargs.keys()) if duplicate_args: - s = 's' if len(duplicate_args) > 1 else '' - duplicate_arg = ', '.join(duplicate_args) raise TypeError( - f"{function_abi.get('name')}() got multiple values for argument{s} '{duplicate_arg}'" + f"{function_abi.get('name')}() got multiple values for argument(s) " + f"'{', '.join(duplicate_args)}'" ) # Check for unknown args unknown_args = kwarg_names.difference(sorted_arg_names) if unknown_args: - s = 's' if len(unknown_args) > 1 else '' - unknown_arg = ', '.join(unknown_args) if function_abi.get('name'): raise TypeError( - f"{function_abi.get('name')}() got unexpected keyword argument{s} '{unknown_arg}'" + f"{function_abi.get('name')}() got unexpected keyword argument(s)" + f" '{', '.join(unknown_args)}'" ) raise TypeError( - f"Type: '{function_abi.get('type')}' got unexpected keyword argument{s} '{unknown_arg}'" + f"Type: '{function_abi.get('type')}' got unexpected keyword argument(s)" + f" '{', '.join(unknown_args)}'" ) # Sort args according to their position in the ABI and unzip them from their diff --git a/web3/_utils/contracts.py b/web3/_utils/contracts.py index a4ed743ff6..dcf66f6304 100644 --- a/web3/_utils/contracts.py +++ b/web3/_utils/contracts.py @@ -144,13 +144,10 @@ def find_matching_fn_abi( "\nAmbiguous argument encoding. " "Provided arguments can be encoded to multiple functions matching this call." ) - s_a = 's' if len(args) > 1 else '' - s_k = 's' if len(kwargs) > 1 else '' - s_m = 's' if len(matching_identifiers) > 1 else '' message = ( f"\nCould not identify the intended function with name `{fn_identifier}`, positional " - f"argument{s_a} of type `{tuple(map(type, args))}` and keyword argument{s_k} of type " - f"`{valmap(type, kwargs)}`.\nFound {len(matching_identifiers)} function{s_m} with " + f"argument(s) of type `{tuple(map(type, args))}` and keyword argument(s) of type " + f"`{valmap(type, kwargs)}`.\nFound {len(matching_identifiers)} function(s) with " f"the name `{fn_identifier}`: {matching_function_signatures}{diagnosis}" ) diff --git a/web3/contract.py b/web3/contract.py index b88f76c2a2..5679e5be86 100644 --- a/web3/contract.py +++ b/web3/contract.py @@ -689,9 +689,8 @@ def check_forbidden_keys_in_transaction( ) -> None: keys_found = transaction.keys() & forbidden_keys if keys_found: - s = 's' if len(keys_found) > 1 else '' raise ValueError( - f"Cannot set '{', '.join(keys_found)}' field{s} in transaction" + f"Cannot set '{', '.join(keys_found)}' field(s) in transaction" ) From c72280af844bd998717fd7638f87692d7b6e0fa8 Mon Sep 17 00:00:00 2001 From: Felipe Selmo Date: Thu, 24 Feb 2022 15:03:20 -0700 Subject: [PATCH 10/15] Minor cleanups in method class. --- web3/method.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/web3/method.py b/web3/method.py index 4f08807e40..dfc88800e9 100644 --- a/web3/method.py +++ b/web3/method.py @@ -61,14 +61,14 @@ def inner(args: Any) -> TReturn: return inner -def default_munger(module: "Module", *args: Any, **kwargs: Any) -> Tuple[()]: +def default_munger(_module: "Module", *args: Any, **kwargs: Any) -> Tuple[()]: if not args and not kwargs: return () else: raise TypeError("Parameters passed to method without parameter mungers defined.") -def default_root_munger(module: "Module", *args: Any) -> List[Any]: +def default_root_munger(_module: "Module", *args: Any) -> List[Any]: return [*args] @@ -115,15 +115,14 @@ def get_balance_root_munger(module, account, block_identifier=None): and the response formatters are applied to the output. """ def __init__( - self, - json_rpc_method: Optional[RPCEndpoint] = None, - mungers: Optional[Sequence[Munger]] = None, - request_formatters: Optional[Callable[..., TReturn]] = None, - result_formatters: Optional[Callable[..., TReturn]] = None, - error_formatters: Optional[Callable[..., TReturn]] = None, - null_result_formatters: Optional[Callable[..., TReturn]] = None, - method_choice_depends_on_args: Optional[Callable[..., RPCEndpoint]] = None, - w3: Optional["Web3"] = None): + self, + json_rpc_method: Optional[RPCEndpoint] = None, + mungers: Optional[Sequence[Munger]] = None, + request_formatters: Optional[Callable[..., TReturn]] = None, + result_formatters: Optional[Callable[..., TReturn]] = None, + null_result_formatters: Optional[Callable[..., TReturn]] = None, + method_choice_depends_on_args: Optional[Callable[..., RPCEndpoint]] = None, + ): self.json_rpc_method = json_rpc_method self.mungers = mungers or [default_munger] @@ -133,8 +132,9 @@ def __init__( self.null_result_formatters = null_result_formatters or get_null_result_formatters self.method_choice_depends_on_args = method_choice_depends_on_args - def __get__(self, obj: Optional["Module"] = None, - obj_type: Optional[Type["Module"]] = None) -> TFunc: + def __get__( + self, obj: Optional["Module"] = None, obj_type: Optional[Type["Module"]] = None + ) -> TFunc: if obj is None: raise TypeError( "Direct calls to methods are not supported. " @@ -204,7 +204,7 @@ def process_params( return request, response_formatters -class DeprecatedMethod(): +class DeprecatedMethod: def __init__(self, method: Method[Callable[..., Any]], old_name: str, new_name: str) -> None: self.method = method self.old_name = old_name From 0e3aefdbdccbef7aac854ab2920cf95692779714 Mon Sep 17 00:00:00 2001 From: Felipe Selmo Date: Thu, 24 Feb 2022 15:05:42 -0700 Subject: [PATCH 11/15] Remove old reference to TestRPCProvider --- web3/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/web3/__init__.py b/web3/__init__.py index 5c8edfacef..8d86766529 100644 --- a/web3/__init__.py +++ b/web3/__init__.py @@ -45,7 +45,6 @@ "HTTPProvider", "IPCProvider", "WebsocketProvider", - "TestRPCProvider", "EthereumTesterProvider", "Account", "AsyncHTTPProvider", From 7762ed9fe15c6bac76c1b8974394b4c4ead2ce13 Mon Sep 17 00:00:00 2001 From: Felipe Selmo Date: Thu, 24 Feb 2022 19:04:29 -0700 Subject: [PATCH 12/15] Add option to attach methods to classes extending from `Module` class * Refactor logic for attaching a `Method` class as a property rather than a method. Instead of implicitly setting `mungers=None`, explicitly set the `is_property` flag on `Method` to `True`. This also facilitates attaching new methods and properties to modules. * Fix up some tests in test_method.py that were falsely passing to actually test correctly. Add tests for new `is_property` flag for the `Method` class. * Create `test_module.py` and add tests for `attach_methods()` --- newsfragments/2383.breaking-change.rst | 1 + tests/core/method-class/test_method.py | 44 ++++++++++----- tests/core/module-class/test_module.py | 75 ++++++++++++++++++++++++++ web3/_utils/admin.py | 10 ++-- web3/_utils/miner.py | 6 +-- web3/_utils/personal.py | 4 +- web3/_utils/txpool.py | 6 +-- web3/eth.py | 22 ++++---- web3/method.py | 35 +++++++----- web3/module.py | 12 +++++ web3/parity.py | 6 +-- 11 files changed, 170 insertions(+), 51 deletions(-) create mode 100644 newsfragments/2383.breaking-change.rst create mode 100644 tests/core/module-class/test_module.py diff --git a/newsfragments/2383.breaking-change.rst b/newsfragments/2383.breaking-change.rst new file mode 100644 index 0000000000..a53354bbc7 --- /dev/null +++ b/newsfragments/2383.breaking-change.rst @@ -0,0 +1 @@ +Add ``attach_methods()`` to ``Module`` class to facilitate attaching methods to modules. \ No newline at end of file diff --git a/tests/core/method-class/test_method.py b/tests/core/method-class/test_method.py index 28b7f953aa..44e132f4b6 100644 --- a/tests/core/method-class/test_method.py +++ b/tests/core/method-class/test_method.py @@ -32,7 +32,7 @@ def test_method_accepts_callable_for_selector(): def test_method_selector_fn_accepts_str(): method = Method( - mungers=None, + is_property=True, json_rpc_method='eth_method', ) assert method.method_selector_fn() == 'eth_method' @@ -77,7 +77,7 @@ def test_input_munger_parameter_passthrough_matching_arity(): mungers=[lambda m, z, y: ['success']], json_rpc_method='eth_method', ) - method.input_munger(object(), ['first', 'second'], {}) == 'success' + assert method.input_munger(object(), ['first', 'second'], {}) == ['success'] def test_input_munger_parameter_passthrough_mismatch_arity(): @@ -94,16 +94,15 @@ def test_input_munger_falsy_config_result_in_default_munger(): mungers=[], json_rpc_method='eth_method', ) - method.input_munger(object(), [], {}) == [] + assert method.input_munger(object(), [], {}) == [] -def test_default_input_munger_with_input_parameters_exception(): +def test_default_input_munger_with_input_parameters(): method = Method( mungers=[], json_rpc_method='eth_method', ) - with pytest.raises(TypeError): - method.input_munger(object(), [1], {}) + assert method.input_munger(object(), [1], {}) == [1] @pytest.mark.parametrize( @@ -125,7 +124,7 @@ def test_default_input_munger_with_input_parameters_exception(): }, ['unexpected_argument'], {}, - TypeError, + IndexError, 2 ), ( @@ -184,6 +183,15 @@ def test_default_input_munger_with_input_parameters_exception(): ('eth_getBalance', ('0x0000000000000000000000000000000000000000', '0x3')), 2, ), + ( + { + 'json_rpc_method': 'eth_getBalance', + }, + ('0x0000000000000000000000000000000000000000', 3), + {}, + ('eth_getBalance', ('0x0000000000000000000000000000000000000000', '0x3')), + 2, + ), ( { 'mungers': [ @@ -206,7 +214,17 @@ def test_default_input_munger_with_input_parameters_exception(): {}, ('eth_chainId', ()), 2, - ) + ), + ( + { + 'is_property': True, + 'json_rpc_method': 'eth_chainId', + }, + [], + {}, + ('eth_chainId', ()), + 2, + ), ), ids=[ 'raises-error-no-rpc-method', @@ -215,9 +233,11 @@ def test_default_input_munger_with_input_parameters_exception(): 'test-rpc-method-as-callable', 'test-arg-munger', 'test-munger-wrong-length-arg', - 'test-request-formatters', + 'test-request-formatters-default-root-munger-explicit', + 'test-request-formatters-default-root-munger-implicit', 'test-mungers-and-request-formatters', 'test-response-formatters', + 'test-set-as-property-default-munger-implicit', ] ) def test_process_params( @@ -230,7 +250,7 @@ def test_process_params( if isclass(expected_request_result) and issubclass(expected_request_result, Exception): with pytest.raises(expected_request_result): method = Method(**method_config) - request_params, output_formatter = method.process_params(object(), *args, **kwargs) + method.process_params(object(), *args, **kwargs) else: method = Method(**method_config) request_params, output_formatter = method.process_params(object(), *args, **kwargs) @@ -248,8 +268,8 @@ class Success(Exception): pass -def return_exception_raising_formatter(method): - def formatter(params): +def return_exception_raising_formatter(_method): + def formatter(_params): raise Success() return compose(formatter) diff --git a/tests/core/module-class/test_module.py b/tests/core/module-class/test_module.py new file mode 100644 index 0000000000..3654a7df8d --- /dev/null +++ b/tests/core/module-class/test_module.py @@ -0,0 +1,75 @@ +import pytest + +from web3 import ( + EthereumTesterProvider, + Web3, +) +from web3.method import ( + Method, +) + + +@pytest.fixture +def web3_with_external_modules(module1, module2, module3): + return Web3( + EthereumTesterProvider(), + external_modules={ + 'module1': module1, + 'module2': (module2, { + 'submodule1': module3, + }), + } + ) + + +def test_attach_methods_to_module(web3_with_external_modules): + w3 = web3_with_external_modules + + w3.module1.attach_methods({ + # set `property1` on `module1` with `eth_chainId` RPC endpoint + 'property1': Method('eth_chainId', is_property=True), + # set `method1` on `module1` with `eth_getBalance` RPC endpoint + 'method1': Method('eth_getBalance'), + }) + + assert w3.eth.chain_id == 61 + assert w3.module1.property1 == 61 + + coinbase = w3.eth.coinbase + assert w3.eth.get_balance(coinbase, 'latest') == 1000000000000000000000000 + assert w3.module1.method1(coinbase, 'latest') == 1000000000000000000000000 + + w3.module2.submodule1.attach_methods({ + # set `method2` on `module2.submodule1` with `eth_blockNumber` RPC endpoint + 'method2': Method('eth_blockNumber', is_property=True) + }) + + assert w3.eth.block_number == 0 + assert w3.module2.submodule1.method2 == 0 + + w3.eth.attach_methods({'get_block2': Method('eth_getBlockByNumber')}) + + assert w3.eth.get_block('latest')['number'] == 0 + assert w3.eth.get_block('pending')['number'] == 1 + + assert w3.eth.get_block2('latest')['number'] == 0 + assert w3.eth.get_block2('pending')['number'] == 1 + + +def test_attach_methods_with_mungers(web3_with_external_modules): + w3 = web3_with_external_modules + + w3.module1.attach_methods({ + 'method1': Method('eth_getBlockByNumber', mungers=[ + lambda _method, block_id, f, _z: (block_id, f), + lambda _m, block_id, _f: (block_id - 1,), + ]), + }) + + assert w3.eth.get_block(0)['baseFeePerGas'] == 1000000000 + assert w3.eth.get_block(1)['baseFeePerGas'] == 875000000 + + # `method1` should take a higher block number than `eth_getBlockByNumber` due to mungers and no + # other params should matter + assert w3.module1.method1(1, False, '_is_never_used_')['baseFeePerGas'] == 1000000000 + assert w3.module1.method1(2, '_will_be_overridden_', None)['baseFeePerGas'] == 875000000 diff --git a/web3/_utils/admin.py b/web3/_utils/admin.py index 2505451d96..86e946d550 100644 --- a/web3/_utils/admin.py +++ b/web3/_utils/admin.py @@ -40,19 +40,19 @@ def admin_start_params_munger( datadir: Method[Callable[[], str]] = Method( RPC.admin_datadir, - mungers=None, + is_property=True, ) node_info: Method[Callable[[], NodeInfo]] = Method( RPC.admin_nodeInfo, - mungers=None, + is_property=True, ) peers: Method[Callable[[], List[Peer]]] = Method( RPC.admin_peers, - mungers=None, + is_property=True, ) @@ -77,13 +77,13 @@ def __call__( stop_rpc: Method[Callable[[], bool]] = Method( RPC.admin_stopRPC, - mungers=None, + is_property=True, ) stop_ws: Method[Callable[[], bool]] = Method( RPC.admin_stopWS, - mungers=None, + is_property=True, ) # diff --git a/web3/_utils/miner.py b/web3/_utils/miner.py index 873f296d95..f39c99953e 100644 --- a/web3/_utils/miner.py +++ b/web3/_utils/miner.py @@ -51,19 +51,19 @@ stop: Method[Callable[[], bool]] = Method( RPC.miner_stop, - mungers=None, + is_property=True, ) start_auto_dag: Method[Callable[[], bool]] = Method( RPC.miner_startAutoDag, - mungers=None, + is_property=True, ) stop_auto_dag: Method[Callable[[], bool]] = Method( RPC.miner_stopAutoDag, - mungers=None, + is_property=True, ) diff --git a/web3/_utils/personal.py b/web3/_utils/personal.py index 2bb2eda77d..d9690cc44a 100644 --- a/web3/_utils/personal.py +++ b/web3/_utils/personal.py @@ -44,13 +44,13 @@ list_accounts: Method[Callable[[], List[ChecksumAddress]]] = Method( RPC.personal_listAccounts, - mungers=None, + is_property=True, ) list_wallets: Method[Callable[[], List[GethWallet]]] = Method( RPC.personal_listWallets, - mungers=None, + is_property=True, ) diff --git a/web3/_utils/txpool.py b/web3/_utils/txpool.py index 052f8b35ca..7af244b52b 100644 --- a/web3/_utils/txpool.py +++ b/web3/_utils/txpool.py @@ -16,17 +16,17 @@ content: Method[Callable[[], TxPoolContent]] = Method( RPC.txpool_content, - mungers=None, + is_property=True, ) inspect: Method[Callable[[], TxPoolInspect]] = Method( RPC.txpool_inspect, - mungers=None, + is_property=True, ) status: Method[Callable[[], TxPoolStatus]] = Method( RPC.txpool_status, - mungers=None, + is_property=True, ) diff --git a/web3/eth.py b/web3/eth.py index ef1c134911..5c84b14a6c 100644 --- a/web3/eth.py +++ b/web3/eth.py @@ -119,7 +119,7 @@ class BaseEth(Module): _gas_price: Method[Callable[[], Wei]] = Method( RPC.eth_gasPrice, - mungers=None, + is_property=True, ) @property @@ -244,7 +244,7 @@ def estimate_gas_munger( _max_priority_fee: Method[Callable[..., Wei]] = Method( RPC.eth_maxPriorityFeePerGas, - mungers=None, + is_property=True, ) def get_block_munger( @@ -267,12 +267,12 @@ def get_block_munger( get_block_number: Method[Callable[[], BlockNumber]] = Method( RPC.eth_blockNumber, - mungers=None, + is_property=True, ) get_coinbase: Method[Callable[[], ChecksumAddress]] = Method( RPC.eth_coinbase, - mungers=None, + is_property=True, ) def block_id_munger( @@ -315,27 +315,27 @@ def call_munger( _get_accounts: Method[Callable[[], Tuple[ChecksumAddress]]] = Method( RPC.eth_accounts, - mungers=None, + is_property=True, ) _get_hashrate: Method[Callable[[], int]] = Method( RPC.eth_hashrate, - mungers=None, + is_property=True, ) _chain_id: Method[Callable[[], int]] = Method( RPC.eth_chainId, - mungers=None, + is_property=True, ) _is_mining: Method[Callable[[], bool]] = Method( RPC.eth_mining, - mungers=None, + is_property=True, ) _is_syncing: Method[Callable[[], Union[SyncStatus, bool]]] = Method( RPC.eth_syncing, - mungers=None, + is_property=True, ) _get_transaction_receipt: Method[Callable[[_Hash32], TxReceipt]] = Method( @@ -565,7 +565,7 @@ def icapNamereg(self) -> NoReturn: _protocol_version: Method[Callable[[], str]] = Method( RPC.eth_protocolVersion, - mungers=None, + is_property=True, ) @property @@ -983,7 +983,7 @@ def getCompilers(self) -> NoReturn: get_work: Method[Callable[[], List[HexBytes]]] = Method( RPC.eth_getWork, - mungers=None, + is_property=True, ) @deprecated_for("generate_gas_price") diff --git a/web3/method.py b/web3/method.py index dfc88800e9..ce94e75915 100644 --- a/web3/method.py +++ b/web3/method.py @@ -61,6 +61,14 @@ def inner(args: Any) -> TReturn: return inner +def _set_mungers(mungers: Optional[Sequence[Munger]], is_property: bool) -> Sequence[Any]: + return ( + mungers if mungers + else [default_munger] if is_property + else [default_root_munger] + ) + + def default_munger(_module: "Module", *args: Any, **kwargs: Any) -> Tuple[()]: if not args and not kwargs: return () @@ -81,7 +89,7 @@ class Method(Generic[TFunc]): Calls to the Method go through these steps: 1. input munging - includes normalization, parameter checking, early parameter - formatting. Any processing on the input parameters that need to happen before + formatting. Any processing on the input parameters that need to happen before json_rpc method string selection occurs. A note about mungers: The first (root) munger should reflect the desired @@ -122,15 +130,16 @@ def __init__( result_formatters: Optional[Callable[..., TReturn]] = None, null_result_formatters: Optional[Callable[..., TReturn]] = None, method_choice_depends_on_args: Optional[Callable[..., RPCEndpoint]] = None, + is_property: bool = False, ): - self.json_rpc_method = json_rpc_method - self.mungers = mungers or [default_munger] + self.mungers = _set_mungers(mungers, is_property) self.request_formatters = request_formatters or get_request_formatters self.result_formatters = result_formatters or get_result_formatters self.error_formatters = get_error_formatters self.null_result_formatters = null_result_formatters or get_null_result_formatters self.method_choice_depends_on_args = method_choice_depends_on_args + self.is_property = is_property def __get__( self, obj: Optional["Module"] = None, obj_type: Optional[Type["Module"]] = None @@ -167,8 +176,8 @@ def input_munger( root_munger = next(mungers_iter) munged_inputs = pipe( root_munger(module, *args, **kwargs), - *map(lambda m: _munger_star_apply(functools.partial(m, module)), mungers_iter)) - + *map(lambda m: _munger_star_apply(functools.partial(m, module)), mungers_iter) + ) return munged_inputs def process_params( @@ -194,13 +203,15 @@ def process_params( params = [] method = self.method_selector_fn() - response_formatters = (self.result_formatters(method, module), - self.error_formatters(method), - self.null_result_formatters(method),) - - request = (method, - _apply_request_formatters(params, self.request_formatters(method))) - + response_formatters = ( + self.result_formatters(method, module), + self.error_formatters(method), + self.null_result_formatters(method), + ) + request = ( + method, + _apply_request_formatters(params, self.request_formatters(method)) + ) return request, response_formatters diff --git a/web3/module.py b/web3/module.py index 49fdfb9209..de3343e5a8 100644 --- a/web3/module.py +++ b/web3/module.py @@ -3,6 +3,7 @@ Any, Callable, Coroutine, + Dict, TypeVar, Union, ) @@ -91,3 +92,14 @@ def __init__(self, w3: "Web3") -> None: self.retrieve_caller_fn = retrieve_blocking_method_call_fn(w3, self) self.w3 = w3 self.codec: ABICodec = w3.codec + + def attach_methods( + self, + methods: Dict[str, Method[Callable[..., Any]]], + ) -> None: + for method_name, method_class in methods.items(): + klass = ( + method_class.__get__(obj=self)() if method_class.is_property else + method_class.__get__(obj=self) + ) + setattr(self, method_name, klass) diff --git a/web3/parity.py b/web3/parity.py index 84bcaa5c44..4b47a40b21 100644 --- a/web3/parity.py +++ b/web3/parity.py @@ -94,7 +94,7 @@ class Parity(Module): enode: Method[Callable[[], str]] = Method( RPC.parity_enode, - mungers=None, + is_property=True, ) """ property default_block """ @@ -141,7 +141,7 @@ def list_storage_keys_munger( net_peers: Method[Callable[[], ParityNetPeers]] = Method( RPC.parity_netPeers, - mungers=None + is_property=True ) add_reserved_peer: Method[Callable[[EnodeURI], bool]] = Method( @@ -217,7 +217,7 @@ def trace_transactions_munger( mode: Method[Callable[[], ParityMode]] = Method( RPC.parity_mode, - mungers=None + is_property=True ) # Deprecated Methods From 061fb6693e4a2c16a91c88c9b11df65227225902 Mon Sep 17 00:00:00 2001 From: Felipe Selmo Date: Thu, 10 Mar 2022 09:18:39 -0300 Subject: [PATCH 13/15] More tests for method class and cleanup from PR #2383 comments --- tests/core/method-class/test_method.py | 99 ++++++++++++++++++++------ tests/core/module-class/test_module.py | 19 ++--- web3/method.py | 16 +++-- 3 files changed, 99 insertions(+), 35 deletions(-) diff --git a/tests/core/method-class/test_method.py b/tests/core/method-class/test_method.py index 44e132f4b6..9e0bb64e0d 100644 --- a/tests/core/method-class/test_method.py +++ b/tests/core/method-class/test_method.py @@ -3,6 +3,9 @@ ) import pytest +from eth_utils import ( + ValidationError, +) from eth_utils.toolz import ( compose, ) @@ -69,7 +72,7 @@ def test_get_formatters_non_falsy_config_retrieval(): first_formatter = (method.request_formatters(method_name).first,) all_other_formatters = method.request_formatters(method_name).funcs assert len(first_formatter + all_other_formatters) == 2 - # assert method.request_formatters('eth_nonmatching') == 'nonmatch' + assert (method.request_formatters('eth_getBalance').first,) == first_formatter def test_input_munger_parameter_passthrough_matching_arity(): @@ -89,9 +92,17 @@ def test_input_munger_parameter_passthrough_mismatch_arity(): method.input_munger(object(), ['first', 'second', 'third'], {}) -def test_input_munger_falsy_config_result_in_default_munger(): +def test_default_input_munger_with_no_input_parameters(): method = Method( - mungers=[], + json_rpc_method='eth_method', + ) + assert method.input_munger(object(), [], {}) == [] + + +@pytest.mark.parametrize('empty', ([], (), None), ids=['empty-list', 'empty-tuple', 'None']) +def test_empty_input_munger_with_no_input_parameters(empty): + method = Method( + mungers=empty, json_rpc_method='eth_method', ) assert method.input_munger(object(), [], {}) == [] @@ -99,14 +110,69 @@ def test_input_munger_falsy_config_result_in_default_munger(): def test_default_input_munger_with_input_parameters(): method = Method( - mungers=[], json_rpc_method='eth_method', ) assert method.input_munger(object(), [1], {}) == [1] +@pytest.mark.parametrize('empty', ([], (), None), ids=['empty-list', 'empty-tuple', 'None']) +def test_empty_input_mungers_with_input_parameters(empty): + method = Method( + mungers=empty, + json_rpc_method='eth_method', + ) + assert method.input_munger(object(), [1], {}) == [1] + + +def test_default_munger_for_property_with_no_input_parameters(): + method = Method( + is_property=True, + json_rpc_method='eth_method', + ) + assert method.input_munger(object(), [], {}) == () + + +@pytest.mark.parametrize('empty', ([], (), None), ids=['empty-list', 'empty-tuple', 'None']) +def test_empty_mungers_for_property_with_no_input_parameters(empty): + method = Method( + is_property=True, + mungers=empty, + json_rpc_method='eth_method', + ) + assert method.input_munger(object(), [], {}) == () + + +def test_default_munger_for_property_with_input_parameters_raises_ValidationError(): + method = Method( + is_property=True, + json_rpc_method='eth_method', + ) + with pytest.raises(ValidationError, match='Parameters cannot be passed to a property'): + method.input_munger(object(), [1], {}) + + +@pytest.mark.parametrize('empty', ([], (), None), ids=['empty-list', 'empty-tuple', 'None']) +def test_empty_mungers_for_property_with_input_parameters_raises_ValidationError(empty): + method = Method( + is_property=True, + mungers=empty, + json_rpc_method='eth_method', + ) + with pytest.raises(ValidationError, match='Parameters cannot be passed to a property'): + method.input_munger(object(), [1], {}) + + +def test_property_with_mungers_raises_ValidationError(): + with pytest.raises(ValidationError, match='Mungers cannot be used with a property'): + Method( + is_property=True, + mungers=[lambda m, z, y: 'success'], + json_rpc_method='eth_method', + ) + + @pytest.mark.parametrize( - "method_config,args,kwargs,expected_request_result,expected_result_formatters_len", + "method_config,args,kwargs,expected_request_result", ( ( { @@ -115,17 +181,15 @@ def test_default_input_munger_with_input_parameters(): [], {}, ValueError, - 2 ), ( { 'mungers': [], 'json_rpc_method': 'eth_getBalance', }, - ['unexpected_argument'], + ['only_the_first_argument_but_expects_two'], {}, IndexError, - 2 ), ( { @@ -135,7 +199,6 @@ def test_default_input_munger_with_input_parameters(): ['0x0000000000000000000000000000000000000000', 3], {}, ('eth_getBalance', (('0x' + '00' * 20), "0x3")), - 2 ), ( { @@ -145,7 +208,6 @@ def test_default_input_munger_with_input_parameters(): ['0x0000000000000000000000000000000000000000', 3], {}, ('eth_getBalance', (('0x' + '00' * 20), "0x3")), - 2 ), ( { @@ -158,7 +220,6 @@ def test_default_input_munger_with_input_parameters(): [1, 2, 3, ('0x' + '00' * 20)], {}, ('eth_getBalance', (('0x' + '00' * 20), "1")), - 2, ), ( { @@ -171,7 +232,6 @@ def test_default_input_munger_with_input_parameters(): [1, 2, 3, 4], {}, TypeError, - 2, ), ( { @@ -181,7 +241,6 @@ def test_default_input_munger_with_input_parameters(): ('0x0000000000000000000000000000000000000000', 3), {}, ('eth_getBalance', ('0x0000000000000000000000000000000000000000', '0x3')), - 2, ), ( { @@ -190,7 +249,6 @@ def test_default_input_munger_with_input_parameters(): ('0x0000000000000000000000000000000000000000', 3), {}, ('eth_getBalance', ('0x0000000000000000000000000000000000000000', '0x3')), - 2, ), ( { @@ -203,7 +261,6 @@ def test_default_input_munger_with_input_parameters(): [('0x' + '00' * 20), 1, 2, 3], {}, ('eth_getBalance', (('0x' + '00' * 20), '1')), - 2, ), ( { @@ -213,7 +270,6 @@ def test_default_input_munger_with_input_parameters(): [], {}, ('eth_chainId', ()), - 2, ), ( { @@ -223,16 +279,15 @@ def test_default_input_munger_with_input_parameters(): [], {}, ('eth_chainId', ()), - 2, ), ), ids=[ 'raises-error-no-rpc-method', - 'test-unexpected-arg', + 'test-missing-argument', 'test-rpc-method-as-string', 'test-rpc-method-as-callable', 'test-arg-munger', - 'test-munger-wrong-length-arg', + 'test-munger-too-many-args', 'test-request-formatters-default-root-munger-explicit', 'test-request-formatters-default-root-munger-implicit', 'test-mungers-and-request-formatters', @@ -245,7 +300,7 @@ def test_process_params( args, kwargs, expected_request_result, - expected_result_formatters_len): +): if isclass(expected_request_result) and issubclass(expected_request_result, Exception): with pytest.raises(expected_request_result): @@ -257,7 +312,9 @@ def test_process_params( assert request_params == expected_request_result first_formatter = (output_formatter[0].first,) all_other_formatters = output_formatter[0].funcs - assert len(first_formatter + all_other_formatters) == expected_result_formatters_len + + # the expected result formatters length is 2 + assert len(first_formatter + all_other_formatters) == 2 def keywords(module, keyword_one, keyword_two): diff --git a/tests/core/module-class/test_module.py b/tests/core/module-class/test_module.py index 3654a7df8d..ec67fa8d46 100644 --- a/tests/core/module-class/test_module.py +++ b/tests/core/module-class/test_module.py @@ -59,17 +59,20 @@ def test_attach_methods_to_module(web3_with_external_modules): def test_attach_methods_with_mungers(web3_with_external_modules): w3 = web3_with_external_modules + # `method1` uses `eth_getBlockByNumber` but makes use of unique mungers w3.module1.attach_methods({ 'method1': Method('eth_getBlockByNumber', mungers=[ - lambda _method, block_id, f, _z: (block_id, f), - lambda _m, block_id, _f: (block_id - 1,), + lambda _method, block_id, full_transactions: (block_id, full_transactions), + # take the user-provided `block_id` and subtract 1 + lambda _method, block_id, full_transactions: (block_id - 1, full_transactions), ]), }) - assert w3.eth.get_block(0)['baseFeePerGas'] == 1000000000 - assert w3.eth.get_block(1)['baseFeePerGas'] == 875000000 + assert w3.eth.get_block(0, False)['baseFeePerGas'] == 1000000000 + assert w3.eth.get_block(1, False)['baseFeePerGas'] == 875000000 - # `method1` should take a higher block number than `eth_getBlockByNumber` due to mungers and no - # other params should matter - assert w3.module1.method1(1, False, '_is_never_used_')['baseFeePerGas'] == 1000000000 - assert w3.module1.method1(2, '_will_be_overridden_', None)['baseFeePerGas'] == 875000000 + # Testing the mungers work: + # `method1` also calls 'eth_getBlockByNumber' but subtracts 1 from the user-provided `block_id` + # due to the second munger. So, `0` from above is a `1` here and `1` is `2`. + assert w3.module1.method1(1, False)['baseFeePerGas'] == 1000000000 + assert w3.module1.method1(2, False)['baseFeePerGas'] == 875000000 diff --git a/web3/method.py b/web3/method.py index ce94e75915..9e0e6b822f 100644 --- a/web3/method.py +++ b/web3/method.py @@ -15,6 +15,9 @@ ) import warnings +from eth_utils import ( + ValidationError, +) from eth_utils.curried import ( to_tuple, ) @@ -62,6 +65,9 @@ def inner(args: Any) -> TReturn: def _set_mungers(mungers: Optional[Sequence[Munger]], is_property: bool) -> Sequence[Any]: + if is_property and mungers: + raise ValidationError("Mungers cannot be used with a property.") + return ( mungers if mungers else [default_munger] if is_property @@ -70,10 +76,9 @@ def _set_mungers(mungers: Optional[Sequence[Munger]], is_property: bool) -> Sequ def default_munger(_module: "Module", *args: Any, **kwargs: Any) -> Tuple[()]: - if not args and not kwargs: - return () - else: - raise TypeError("Parameters passed to method without parameter mungers defined.") + if args or kwargs: + raise ValidationError("Parameters cannot be passed to a property.") + return () def default_root_munger(_module: "Module", *args: Any) -> List[Any]: @@ -136,7 +141,6 @@ def __init__( self.mungers = _set_mungers(mungers, is_property) self.request_formatters = request_formatters or get_request_formatters self.result_formatters = result_formatters or get_result_formatters - self.error_formatters = get_error_formatters self.null_result_formatters = null_result_formatters or get_null_result_formatters self.method_choice_depends_on_args = method_choice_depends_on_args self.is_property = is_property @@ -205,7 +209,7 @@ def process_params( method = self.method_selector_fn() response_formatters = ( self.result_formatters(method, module), - self.error_formatters(method), + get_error_formatters(method), self.null_result_formatters(method), ) request = ( From 9ba6fec2b13a62302318035edeb8b97797d1ec1f Mon Sep 17 00:00:00 2001 From: Mahmoud Harmouch Date: Fri, 11 Mar 2022 23:32:41 +0200 Subject: [PATCH 14/15] bump docker base image & rm parity env var! (#2369) * :zap: bump docker base image and parity version! Signed-off-by: Harmouch101 * :memo: add newsfragment! Signed-off-by: Harmouch101 * :zap: rm parity tests from docker. Signed-off-by: Harmouch101 * :memo: update newsfragment. Signed-off-by: Harmouch101 --- Dockerfile | 3 +-- docker-compose.yml | 2 -- newsfragments/2369.misc.rst | 1 + 3 files changed, 2 insertions(+), 4 deletions(-) create mode 100644 newsfragments/2369.misc.rst diff --git a/Dockerfile b/Dockerfile index 912a2961e2..8fe1aabb26 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,6 @@ -FROM python:3.6 +FROM python:3.9 # Set up code directory -RUN mkdir -p /usr/src/app WORKDIR /usr/src/app # Install Linux dependencies diff --git a/docker-compose.yml b/docker-compose.yml index c130de201a..2ffdfaede2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,8 +3,6 @@ services: sandbox: build: context: . - environment: - PARITY_VERSION: v2.3.5 volumes: - .:/code command: tail -f /dev/null diff --git a/newsfragments/2369.misc.rst b/newsfragments/2369.misc.rst new file mode 100644 index 0000000000..cede2588df --- /dev/null +++ b/newsfragments/2369.misc.rst @@ -0,0 +1 @@ +bump docker base image to ``3.9`` and remove parity tests from docker. \ No newline at end of file From ca662c879e95e9e184d9dfdb78855492e1d7db23 Mon Sep 17 00:00:00 2001 From: kclowes Date: Mon, 14 Mar 2022 10:55:43 -0600 Subject: [PATCH 15/15] Add async chain_id setter (#2376) * Add async chain_id setter * Add newsfragment for async chain_id --- newsfragments/2376.bugfix.rst | 1 + tests/core/eth-module/test_eth_properties.py | 29 ++++++++++++++++++++ web3/eth.py | 9 +++++- 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 newsfragments/2376.bugfix.rst diff --git a/newsfragments/2376.bugfix.rst b/newsfragments/2376.bugfix.rst new file mode 100644 index 0000000000..8518a5427e --- /dev/null +++ b/newsfragments/2376.bugfix.rst @@ -0,0 +1 @@ +Add async default_chain_id and chain_id setter diff --git a/tests/core/eth-module/test_eth_properties.py b/tests/core/eth-module/test_eth_properties.py index 596222b62a..c24070eb60 100644 --- a/tests/core/eth-module/test_eth_properties.py +++ b/tests/core/eth-module/test_eth_properties.py @@ -1,5 +1,23 @@ import pytest +from web3 import Web3 +from web3.eth import ( + AsyncEth, +) +from web3.providers.eth_tester.main import ( + AsyncEthereumTesterProvider, +) + + +@pytest.fixture +def async_w3(): + return Web3( + AsyncEthereumTesterProvider(), + middlewares=[], + modules={ + 'eth': (AsyncEth,), + }) + def test_eth_protocol_version(w3): with pytest.warns(DeprecationWarning): @@ -28,3 +46,14 @@ def test_set_chain_id(w3): w3.eth.chain_id = None assert w3.eth.chain_id == 61 + + +@pytest.mark.asyncio +async def test_async_set_chain_id(async_w3): + assert await async_w3.eth.chain_id == 61 + + async_w3.eth.chain_id = 72 + assert await async_w3.eth.chain_id == 72 + + async_w3.eth.chain_id = None + assert await async_w3.eth.chain_id == 61 diff --git a/web3/eth.py b/web3/eth.py index 5c84b14a6c..54c88a41d1 100644 --- a/web3/eth.py +++ b/web3/eth.py @@ -358,7 +358,14 @@ async def block_number(self) -> BlockNumber: @property async def chain_id(self) -> int: - return await self._chain_id() # type: ignore + if self._default_chain_id is None: + return await self._chain_id() # type: ignore + else: + return self._default_chain_id + + @chain_id.setter + def chain_id(self, value: int) -> None: + self._default_chain_id = value @property async def coinbase(self) -> ChecksumAddress: