Skip to content

Getting the underlying exception in copy() #593

@bluetech

Description

@bluetech

Problem

When an exception is raised from the user python code during copy(), the traceback looks like this:

Traceback (most recent call last):
    <snipped>
requests.exceptions.ConnectionError: ('Connection aborted.', RemoteDisconnected('Remote end closed connection without response'))

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "venv/lib/python3.11/site-packages/psycopg/cursor.py", line 900, in copy
    yield copy
  File "myfile.py", line 86, in myfunc
    raise MyException() from e
MyException

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "myotherfile.py", line 1251, in myotherfunc
    with cursor.copy(sql.SQL('''
  File "/usr/lib/python3.11/contextlib.py", line 155, in __exit__
    self.gen.throw(typ, value, traceback)
  File "venv/lib/python3.11/site-packages/psycopg/cursor.py", line 902, in copy
    raise ex.with_traceback(None)
psycopg.errors.QueryCanceled: COPY from stdin failed: error from Python: MyException - 

If I want to inspect the underlying exception, I currently need to check __context__. But __context__ should be reserved for unintended bugs in error handling code. This makes it hard to distinguish real underlying errors from exceptions unintentionally raised in psycopg's exception handling code.

Suggested solution

Change to explicit chaining, that is raise QueryCanceled() from underlying_exc instead of raise without from in the exception handling context.

This will change the traceback message to say "The above exception was the direct cause of the following exception". Also will change __context__ to None and make __cause__ have the exception instead.

There are linters that can help with detecting this more holistically, for example flake8-bugbear B904, though I didn't check if it will detect this case.

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