-
Notifications
You must be signed in to change notification settings - Fork 32
Description
The following code runs on every exception in AsyncZabbixAPI:
python-zabbix-utils/zabbix_utils/aioapi.py
Lines 183 to 185 in 8d57619
| async def __exception(self, exc) -> None: | |
| await self.__aclose_session() | |
| raise exc from exc |
The statement raise exc from exc creates a new Exception whose __cause__ attribute is set to itself. This is unnecessary, because the __context__ is already set and the statement does not add any new information to the traceback (see this page in docs.python.org). It also causes infinite loop and crashes the program when rich exception formatting is enabled.
Some exception formatters, including Rich traverse the exception and pretty-print them with values of local variables. If the exception references itself as the cause, Rich enters an infinite loop, eats all available memory, and eventually is killed by the OS.
Here is a minimal example. Notice the identity check assert e is e.__cause__. The program will stall.
python -m venv venv
source venv/bin/activate
pip install rich 'zabbix_utils[async]'
python3 main.pyimport asyncio
import zabbix_utils
from rich.console import Console
server = "http://localhost:8081/"
token = "changeme"
console = Console()
async def main() -> None:
try:
api = zabbix_utils.AsyncZabbixAPI(url=server)
await api.login(token=token)
await api.check_auth()
except Exception as e:
assert e is e.__cause__
console.print_exception(show_locals=True)
asyncio.run(main())I created a pull request to address the issue.