自定义异常 - 捕获不继承自 BaseException 的类
Posted
技术标签:
【中文标题】自定义异常 - 捕获不继承自 BaseException 的类【英文标题】:Custom exceptions - catching classes that do not inherit from BaseException 【发布时间】:2021-11-14 08:41:55 【问题描述】:我正在尝试编写一些自定义异常处理,但一直遇到“TypeError:不允许捕获不从 BaseException 继承的类”错误的问题。我有一个名为 NodeError 的基本异常类,它继承自 Exception。从那里,我有几个继承自 NodeError 的自定义异常。
web3 模块使用 requests 模块与节点通信。我的测试不断尝试从节点获取 tx 计数,在此过程中,我尝试通过禁用我的 NIC 来模拟中断。我尝试在 get_tx_count() 中捕获 requests.exceptions.ConnectionError 并引发我自己的异常。它似乎根据堆栈跟踪正确地命中了 NodeConnectionError 自定义异常,但随后又得到了另一个异常并抱怨捕获了不从 BaseException 继承的类。
不知道为什么它认为我没有从 BaseException 继承,但我觉得这与首先捕获请求异常有关。
堆栈跟踪:
Traceback (most recent call last):
File "C:\Python39\lib\site-packages\urllib3\connection.py", line 169, in _new_conn
conn = connection.create_connection(
File "C:\Python39\lib\site-packages\urllib3\util\connection.py", line 73, in create_connection
for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
File "C:\Python39\lib\socket.py", line 953, in getaddrinfo
for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno 11001] getaddrinfo failed
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Python39\lib\site-packages\urllib3\connectionpool.py", line 699, in urlopen
httplib_response = self._make_request(
File "C:\Python39\lib\site-packages\urllib3\connectionpool.py", line 382, in _make_request
self._validate_conn(conn)
File "C:\Python39\lib\site-packages\urllib3\connectionpool.py", line 1010, in _validate_conn
conn.connect()
File "C:\Python39\lib\site-packages\urllib3\connection.py", line 353, in connect
conn = self._new_conn()
File "C:\Python39\lib\site-packages\urllib3\connection.py", line 181, in _new_conn
raise NewConnectionError(
urllib3.exceptions.NewConnectionError: <urllib3.connection.HTTPSConnection object at 0x000001413971AB50>: Failed to establish a new connection: [Errno 11001] getaddrinfo failed
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Python39\lib\site-packages\requests\adapters.py", line 439, in send
resp = conn.urlopen(
File "C:\Python39\lib\site-packages\urllib3\connectionpool.py", line 755, in urlopen
retries = retries.increment(
File "C:\Python39\lib\site-packages\urllib3\util\retry.py", line 574, in increment
raise MaxRetryError(_pool, url, error or ResponseError(cause))
urllib3.exceptions.MaxRetryError: HTTPSConnectionPool(host='matic-mainnet-full-rpc.bwarelabs.com', port=443): Max retries exceeded with url: / (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x000001413971AB50>: Failed to establish a new connection: [Errno 11001] getaddrinfo failed'))
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\t\extest.py", line 87, in get_tx_count
nonce = w3.eth.get_transaction_count(address)
File "C:\Python39\lib\site-packages\web3\module.py", line 57, in caller
result = w3.manager.request_blocking(method_str,
File "C:\Python39\lib\site-packages\web3\manager.py", line 186, in request_blocking
response = self._make_request(method, params)
File "C:\Python39\lib\site-packages\web3\manager.py", line 147, in _make_request
return request_func(method, params)
File "cytoolz/functoolz.pyx", line 250, in cytoolz.functoolz.curry.__call__
return self.func(*args, **kwargs)
File "C:\Python39\lib\site-packages\web3\middleware\formatting.py", line 76, in apply_formatters
response = make_request(method, params)
File "C:\Python39\lib\site-packages\web3\middleware\gas_price_strategy.py", line 84, in middleware
return make_request(method, params)
File "cytoolz/functoolz.pyx", line 250, in cytoolz.functoolz.curry.__call__
return self.func(*args, **kwargs)
File "C:\Python39\lib\site-packages\web3\middleware\formatting.py", line 74, in apply_formatters
response = make_request(method, formatted_params)
File "C:\Python39\lib\site-packages\web3\middleware\attrdict.py", line 33, in middleware
response = make_request(method, params)
File "cytoolz/functoolz.pyx", line 250, in cytoolz.functoolz.curry.__call__
return self.func(*args, **kwargs)
File "C:\Python39\lib\site-packages\web3\middleware\formatting.py", line 74, in apply_formatters
response = make_request(method, formatted_params)
File "cytoolz/functoolz.pyx", line 250, in cytoolz.functoolz.curry.__call__
return self.func(*args, **kwargs)
File "C:\Python39\lib\site-packages\web3\middleware\formatting.py", line 76, in apply_formatters
response = make_request(method, params)
File "cytoolz/functoolz.pyx", line 250, in cytoolz.functoolz.curry.__call__
return self.func(*args, **kwargs)
File "C:\Python39\lib\site-packages\web3\middleware\formatting.py", line 74, in apply_formatters
response = make_request(method, formatted_params)
File "C:\Python39\lib\site-packages\web3\middleware\buffered_gas_estimate.py", line 40, in middleware
return make_request(method, params)
File "C:\Python39\lib\site-packages\web3\middleware\exception_retry_request.py", line 105, in middleware
return make_request(method, params)
File "C:\Python39\lib\site-packages\web3\providers\rpc.py", line 88, in make_request
raw_response = make_post_request(
File "C:\Python39\lib\site-packages\web3\_utils\request.py", line 48, in make_post_request
response = session.post(endpoint_uri, data=data, *args, **kwargs) # type: ignore
File "C:\Python39\lib\site-packages\requests\sessions.py", line 590, in post
return self.request('POST', url, data=data, json=json, **kwargs)
File "C:\Python39\lib\site-packages\requests\sessions.py", line 542, in request
resp = self.send(prep, **send_kwargs)
File "C:\Python39\lib\site-packages\requests\sessions.py", line 655, in send
r = adapter.send(request, **kwargs)
File "C:\Python39\lib\site-packages\requests\adapters.py", line 516, in send
raise ConnectionError(e, request=request)
requests.exceptions.ConnectionError: HTTPSConnectionPool(host='matic-mainnet-full-rpc.bwarelabs.com', port=443): Max retries exceeded with url: / (Caused by NewConnectionError('<urllib3.connection.HTTPSConnection object at 0x000001413971AB50>: Failed to establish a new connection: [Errno 11001] getaddrinfo failed'))
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:\t\extest.py", line 114, in main
print(get_tx_count(w3, address))
File "C:\t\extest.py", line 90, in get_tx_count
raise NodeConnectionError(w3) from e
__main__.NodeConnectionError: An error occurred with the node at A web3 connection error occurred talking to https://matic-mainnet-full-rpc.bwarelabs.com:443..
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\t\extest.py", line 140, in <module>
main()
File "C:\t\extest.py", line 116, in main
except NodeNotConnected(w3.provider.endpoint_uri, w3):
TypeError: catching classes that do not inherit from BaseException is not allowed
测试代码:
from web3 import Web3
from time import sleep
from requests.exceptions import ConnectionError, ConnectTimeout, HTTPError
import sys
class NodeError(Exception):
"""Base exception for node errors"""
def __init__(self, url, msg=None):
if msg is None:
msg = f"An error occurred with the node at url."
super().__init__(msg)
self.url = url
class NodeNotConnected(NodeError):
"""web3 could not connect to a node"""
def __init__(self, url, w3=None):
msg = f"A web3 connection could not be made to URL url."
super().__init__(url, msg=msg)
self.w3 = w3
class NodeConnectionError(NodeError):
"""A web3 error occurred communicating with a node"""
def __init__(self, w3):
msg = (
f"A web3 connection error occurred talking to "
f"w3.provider.endpoint_uri."
)
super().__init__(msg)
self.w3 = w3
class NodeTooManyRequests(NodeError):
def __init__(self, w3):
msg = (
f"Too many requests made to w3.provider.endpoint_uri. Try a "
f"different node."
)
super().__init__(msg)
self.w3 = w3
class NodeNoAvailableNodes(NodeError):
def __init__(self):
msg = f"Unable to connect to any nodes."
super().__init__(msg)
class NodeInternalError(NodeError):
def __init__(self):
msg = "The node had an internal error."
super().__init__(msg)
def lib_connect_to_node(url):
"""Emulates library connect to node function"""
try:
w3 = Web3(
Web3.HTTPProvider(
url,
request_kwargs="timeout": 5
)
)
if not w3.isConnected():
raise NodeNotConnected(url, w3)
except Exception as e:
raise NodeNotConnected(url) from e
else:
return w3
def connect_to_node(urls, node_retries):
while node_retries >= 0:
try:
w3 = lib_connect_to_node(urls[0])
except NodeNotConnected(urls[0]) as e:
if node_retries == 0:
raise NodeNoAvailableNodes from e
node_retries -= 1
urls = get_next_node(urls)
print('Trying another node')
sleep(1)
continue
else:
return w3
def get_next_node(urls):
urls.append(urls.pop(urls.index(urls[0])))
return urls
def get_tx_count(w3, address):
try:
nonce = w3.eth.get_transaction_count(address)
except (ConnectionError, ConnectTimeout) as e:
print("A requests.exceptions.ConnectionError occurred.")
raise NodeConnectionError(w3) from e
except HTTPError as e:
if e.code == 429:
raise NodeTooManyRequests from e
else:
return nonce
def main():
urls = [
'https://matic-mainnet-full-rpc.bwarelabs.com:443',
'https://matic-mainnet.chainstacklabs.com:443',
'https://rpc-mainnet.maticvigil.com:443',
'https://rpc-mainnet.matic.network:443'
]
node_retries = 3
address = '0xe18A0D121057B002BaFb90aD5F1AB951594A61E8'
try:
w3 = connect_to_node(urls, node_retries)
except NodeNoAvailableNodes as e:
print(e)
sys.exit()
while True:
try:
print(get_tx_count(w3, address))
sleep(0.05)
except NodeNotConnected(w3.provider.endpoint_uri, w3):
urls = get_next_node(urls)
try:
w3 = connect_to_node(urls, node_retries)
except NodeNoAvailableNodes as e:
print(e)
sys.exit()
except NodeConnectionError(w3) as e:
urls = get_next_node(urls)
try:
w3 = connect_to_node(urls, node_retries)
except NodeNoAvailableNodes as e:
print(e)
sys.exit()
except NodeTooManyRequests(w3) as e:
print('Too many requests')
urls = get_next_node(urls)
try:
w3 = connect_to_node(urls, node_retries)
except NodeNoAvailableNodes as e:
print(e)
sys.exit()
if __name__ == '__main__':
main()
【问题讨论】:
语法是except NodeTooManyRequests
,而不是except NodeTooManyRequests(...)
。你需要一个类,而不是那个类的实例
【参考方案1】:
您应该能够捕获任何异常,即使它不直接从 BaseException 继承。
但是,从except
子句中删除类参数,并且只保留类名:
try:
raise NodeNotConnected('abc')
except NodeNotConnected as e:
print(e)
结果:
A web3 connection could not be made to URL abc.
当您指定类参数时,将实例化一个类,其计算结果为实例而不是类型。
【讨论】:
错误信息相当具有误导性。我发现像try: 1/0; except 42 as e: pass
这样的东西产生完全相同的输出非常令人惊讶。为什么错误消息没有简单说明参数不是类?
这行得通。我在想异常错误。 raise 是我想传递任何参数的地方,except 只是捕获异常。如果我想使用任何参数,我只需添加“as e”部分,我可以在其中使用任何东西。谢谢!
@ekhumoro 错误信息不会误导 IMO,但是当使用参数时,整个事情(NodeNotConnected('abc')
)是一个类的实例,而不是类本身
是的 - 所以它显然 是 误导,因为类型错误应该说明参数 不是类,正如我之前所说的。
那也不错以上是关于自定义异常 - 捕获不继承自 BaseException 的类的主要内容,如果未能解决你的问题,请参考以下文章
java中Debug调试异常的概念异常体系处理异常的关键字try/catch/finally/throw/throws多个异常使用捕获并处理的方式继承关系中处理异常自定义异常类
java中Debug调试异常的概念异常体系处理异常的关键字try/catch/finally/throw/throws多个异常使用捕获并处理的方式继承关系中处理异常自定义异常类