From 835e1a0a4ea5fc162f4dc7d2bc3ea356f502ac3a Mon Sep 17 00:00:00 2001 From: antazoey Date: Fri, 20 Dec 2024 06:10:57 +0700 Subject: [PATCH] fix: issue with Block transactions when `block.hash` was `None` (#2426) Signed-off-by: fudancoder Co-authored-by: fudancoder <171416994+fudancoder@users.noreply.github.com> --- src/ape/api/providers.py | 11 ++++++++++- tests/functional/test_block.py | 23 +++++++++++++++++++++++ tests/integration/cli/test_run.py | 5 ++++- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/ape/api/providers.py b/src/ape/api/providers.py index 559646f70e..d11bab831b 100644 --- a/src/ape/api/providers.py +++ b/src/ape/api/providers.py @@ -16,6 +16,7 @@ from subprocess import DEVNULL, PIPE, Popen from typing import TYPE_CHECKING, Any, Optional, Union, cast +from eth_utils import to_hex from pydantic import Field, computed_field, field_serializer, model_validator from ape.api.networks import NetworkAPI @@ -95,7 +96,11 @@ class BlockAPI(BaseInterfaceModel): @log_instead_of_fail(default="") def __repr__(self) -> str: - return super().__repr__() + repr_str = f"{self.__class__.__name__} number={self.number}" + if hash := self.hash: + repr_str = f"{repr_str} hash={to_hex(hash)}" + + return f"<{repr_str}>" @property def datetime(self) -> datetime.datetime: @@ -146,6 +151,10 @@ def transactions(self) -> list[TransactionAPI]: """ All transactions in a block. """ + if self.hash is None: + # Unable to query transactions. + return [] + try: query = BlockTransactionQuery(columns=["*"], block_id=self.hash) return cast(list[TransactionAPI], list(self.query_manager.query(query))) diff --git a/tests/functional/test_block.py b/tests/functional/test_block.py index 75a059472d..d5912ab0a9 100644 --- a/tests/functional/test_block.py +++ b/tests/functional/test_block.py @@ -18,6 +18,18 @@ def test_block(eth_tester_provider, vyper_contract_instance): assert actual.number == data["number"] +def test_repr(block): + actual = repr(block) + expected = f"" + assert actual == expected + + # Show it works when there is no hash. + block.hash = None + actual = repr(block) + expected = f"" + assert actual == expected + + @pytest.mark.parametrize("mode", ("json", "python")) def test_model_dump(block, mode): actual = block.model_dump(mode=mode) @@ -116,3 +128,14 @@ def test_model_validate_web3_block(): data = BlockData(number=123, timestamp=123, gasLimit=123, gasUsed=100) # type: ignore actual = Block.model_validate(data) assert actual.number == 123 + + +def test_transactions(block): + actual = block.transactions + expected: list = [] + assert actual == expected + + # Ensure still works when hash is None (was a bug where this crashed). + block.hash = None + block.__dict__.pop("transactions", None) # Ensure not cached. + assert block.transactions == [] diff --git a/tests/integration/cli/test_run.py b/tests/integration/cli/test_run.py index 937bb1ba2e..fd1058c4a8 100644 --- a/tests/integration/cli/test_run.py +++ b/tests/integration/cli/test_run.py @@ -121,7 +121,10 @@ def test_run_interactive(scripts_runner, integ_project): scripts = [integ_project.scripts_folder / f"{s}.py" for s in error_names] # Show that the variable namespace from the script is available in the console. - user_input = "local_variable\nape.chain.provider.mine()\nape.chain.blocks.head\nexit\n" + user_input = ( + "local_variable\nape.chain.provider.mine()\n" + "print(f'timestamp={ape.chain.blocks.head.timestamp}')\nexit\n" + ) result = scripts_runner.invoke("--interactive", scripts[0].stem, input=user_input) assert result.exit_code == 0, result.output