Skip to content

Circular reference in Exceptions causes rich traceback formatting to fail and crash the program #31

@chovanecadam

Description

@chovanecadam

The following code runs on every exception in AsyncZabbixAPI:

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.py
import 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions