学习Solidity课程运行pytest遇TypeError:int()无法转换非字符串类型
问题场景
我正在学习Patrick的Solidity课程,复制了课程中的测试代码:
def test_only_owner_can_withdraw(): if network.show_active() not in LOCAL_BLOCKCHAIN_ENVIRONMENTS: pytest.skip("only for local testing") fund_me = deploy_fund_me() bad_actor = accounts.add() with pytest.raises(exceptions.VirtualMachineError): fund_me.withdraw({"from": bad_actor})
其他pytest测试均可正常运行,但执行该测试时出现错误:pytest: TypeError: int() can't convert non-string with explicit base,错误发生在brownie库的_get_trace方法中,具体代码位置及上下文如下:
def _get_trace(self) -> None: """Retrieves the stack trace via debug_traceTransaction and finds the return value, revert message and event logs in the trace. """ # check if trace has already been retrieved, or the tx warrants it if self._raw_trace is not None: return self._raw_trace = [] if self.input == "0x" and self.gas_used == 21000: self._modified_state = False self._trace = [] return if not web3.supports_traces: raise RPCRequestError("Node client does not support `debug_traceTransaction`") try: trace = web3.provider.make_request( # type: ignore "debug_traceTransaction", (self.txid, {"disableStorage": CONFIG.mode != "console"}) ) except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e: msg = f"Encountered a {type(e).__name__} while requesting " msg += "`debug_traceTransaction`. The local RPC client has likely crashed." if CONFIG.argv["coverage"]: msg += " If the error persists, add the `skip_coverage` marker to this test." raise RPCRequestError(msg) from None if "error" in trace: self._modified_state = None self._trace_exc = RPCRequestError(trace["error"]["message"]) raise self._trace_exc self._raw_trace = trace = trace["result"]["structLogs"] if not trace: self._modified_state = False return # different nodes return slightly different formats. its really fun to handle # geth/nethermind returns unprefixed and with 0-padding for stack and memory # erigon returns 0x-prefixed and without padding (but their memory values are like geth) fix_stack = False for step in trace: if not step["stack"]: continue check = step["stack"][0] if not isinstance(check, str): break if check.startswith("0x"): fix_stack = True > c:\users\xxxx\appdata\local\programs\python\python310\lib\site-packages\brownie\network\transaction.py(678)_get_trace() -> step["pc"] = int(step["pc"], 16) (Pdb)
错误原因
这个错误的核心是:Brownie在处理debug_traceTransaction返回的交易追踪数据时,尝试将step["pc"](程序计数器)以16进制转换为整数,但step["pc"]本身已经是整数类型,不是字符串,导致int()函数触发类型错误。
出现该问题的常见诱因是你使用的本地节点(如Ganache、Hardhat节点)返回的structLogs中,pc字段是数字而非字符串,而旧版本Brownie未兼容这种格式差异。
解决方案
方案1:升级Brownie到最新版本
新版本Brownie已修复不同节点返回格式的兼容问题,执行升级命令:
pip install --upgrade eth-brownie
方案2:临时修改Brownie源码(应急用,不推荐)
若暂时无法升级,找到报错的transaction.py文件(路径示例:c:\users\xxxx\appdata\local\programs\python\python310\lib\site-packages\brownie\network\transaction.py),修改第678行代码:
将原代码:
step["pc"] = int(step["pc"], 16)
替换为:
if isinstance(step["pc"], str): step["pc"] = int(step["pc"], 16) else: step["pc"] = int(step["pc"])
这样可兼容字符串和数字类型的pc值。
方案3:切换本地节点类型
若升级Brownie后问题仍存在,尝试切换本地节点(如从Ganache换成Hardhat节点,或反之)。不同节点返回的追踪数据格式可能不同,部分节点返回的pc是字符串,可被旧版本Brownie正确处理。
内容的提问来源于stack exchange,提问作者MrPotatoHeadDev

