#python #exception #python-requests #web3
Вопрос:
Я пытаюсь написать некоторую пользовательскую обработку исключений, но продолжаю сталкиваться с проблемами с ошибками «Ошибка типа: перехват классов, которые не наследуются от BaseException, запрещен». У меня есть базовый класс исключений под названием NodeError, который наследуется от Exception. Оттуда у меня есть несколько пользовательских исключений, которые наследуются от NodeError.
Модуль web3 использует модуль запросов для связи с узлом. Мой тест постоянно пытается получить количество tx от узла, и пока он это делает, я пытаюсь имитировать отключение, отключив свою сетевую карту. Я пытаюсь перехватывать запросы.исключения.Ошибка подключения в get_tx_count() и создаю свое собственное исключение. Кажется, что он правильно попал в пользовательское исключение NodeConnectionError на основе трассировки стека, но затем получает другое исключение и жалуется на перехват классов, которые не наследуются от BaseException.
Не знаю, почему он думает, что я не наследую исключение BaseException, но у меня такое чувство, что это связано с тем, чтобы сначала перехватить исключение запросов.
Трассировка стека:
Traceback (most recent call last):
File "C:Python39libsite-packagesurllib3connection.py", line 169, in _new_conn
conn = connection.create_connection(
File "C:Python39libsite-packagesurllib3utilconnection.py", line 73, in create_connection
for res in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM):
File "C:Python39libsocket.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:Python39libsite-packagesurllib3connectionpool.py", line 699, in urlopen
httplib_response = self._make_request(
File "C:Python39libsite-packagesurllib3connectionpool.py", line 382, in _make_request
self._validate_conn(conn)
File "C:Python39libsite-packagesurllib3connectionpool.py", line 1010, in _validate_conn
conn.connect()
File "C:Python39libsite-packagesurllib3connection.py", line 353, in connect
conn = self._new_conn()
File "C:Python39libsite-packagesurllib3connection.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:Python39libsite-packagesrequestsadapters.py", line 439, in send
resp = conn.urlopen(
File "C:Python39libsite-packagesurllib3connectionpool.py", line 755, in urlopen
retries = retries.increment(
File "C:Python39libsite-packagesurllib3utilretry.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:textest.py", line 87, in get_tx_count
nonce = w3.eth.get_transaction_count(address)
File "C:Python39libsite-packagesweb3module.py", line 57, in caller
result = w3.manager.request_blocking(method_str,
File "C:Python39libsite-packagesweb3manager.py", line 186, in request_blocking
response = self._make_request(method, params)
File "C:Python39libsite-packagesweb3manager.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:Python39libsite-packagesweb3middlewareformatting.py", line 76, in apply_formatters
response = make_request(method, params)
File "C:Python39libsite-packagesweb3middlewaregas_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:Python39libsite-packagesweb3middlewareformatting.py", line 74, in apply_formatters
response = make_request(method, formatted_params)
File "C:Python39libsite-packagesweb3middlewareattrdict.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:Python39libsite-packagesweb3middlewareformatting.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:Python39libsite-packagesweb3middlewareformatting.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:Python39libsite-packagesweb3middlewareformatting.py", line 74, in apply_formatters
response = make_request(method, formatted_params)
File "C:Python39libsite-packagesweb3middlewarebuffered_gas_estimate.py", line 40, in middleware
return make_request(method, params)
File "C:Python39libsite-packagesweb3middlewareexception_retry_request.py", line 105, in middleware
return make_request(method, params)
File "C:Python39libsite-packagesweb3providersrpc.py", line 88, in make_request
raw_response = make_post_request(
File "C:Python39libsite-packagesweb3_utilsrequest.py", line 48, in make_post_request
response = session.post(endpoint_uri, data=data, *args, **kwargs) # type: ignore
File "C:Python39libsite-packagesrequestssessions.py", line 590, in post
return self.request('POST', url, data=data, json=json, **kwargs)
File "C:Python39libsite-packagesrequestssessions.py", line 542, in request
resp = self.send(prep, **send_kwargs)
File "C:Python39libsite-packagesrequestssessions.py", line 655, in send
r = adapter.send(request, **kwargs)
File "C:Python39libsite-packagesrequestsadapters.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:textest.py", line 114, in main
print(get_tx_count(w3, address))
File "C:textest.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:textest.py", line 140, in <module>
main()
File "C:textest.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()
Комментарии:
1. синтаксис таков
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.
При указании параметров класса создается экземпляр класса, который вычисляется как экземпляр, а не как тип.
Комментарии:
1. Сообщение об ошибке довольно вводит в заблуждение. Я нахожу весьма удивительным, что нечто подобное
try: 1/0; except 42 as e: pass
производит точно такой же результат. Почему в сообщении об ошибке просто не указано, что параметр не является классом ?2. Это сработало. Я неправильно думал об исключении. Повышение-это то, где я хотел передать любые аргументы, исключение-это просто перехват исключения. Если я хочу использовать какой-либо из аргументов, я просто добавляю часть «как e», и я могу использовать там все, что угодно. Спасибо!
3. @ekhumoro сообщение об ошибке не вводит в заблуждение ИМО, но когда используется параметр, все (
NodeNotConnected('abc')
) является экземпляром класса, а не самим классом4. Да, это явно вводит в заблуждение, потому что ошибка типа должна указывать, что параметр не является классом , как я уже говорил ранее.
5. Это тоже было бы неплохо