Thanks to visit codestin.com
Credit goes to github.com

Skip to content

standardize async dbapi exceptions with access pattern #8047

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
zzzeek opened this issue May 23, 2022 Discussed in #8023 · 9 comments
Open

standardize async dbapi exceptions with access pattern #8047

zzzeek opened this issue May 23, 2022 Discussed in #8023 · 9 comments
Labels
asyncio engine engines, connections, transactions, isolation levels, execution options feature PRs (with tests!) welcome a fix or feature which is appropriate to be implemented by volunteers
Milestone

Comments

@zzzeek
Copy link
Member

zzzeek commented May 23, 2022

Discussed in #8023

I think we should try to hit this in terms of all the async dialects all of which have to do what we are doing here, which would be that we make a common mixin / base for all the async-adapted DBAPIs. for asyncpg it would be used here:

class Error(Exception):
pass

    from ..exc import EmulatedDBAPIException

    class Error(EmulatedDBAPIException):
        pass

that way people can look at EmulatedDBAPIException to see the fields, which would be like:

class EmulatedDBAPIException:
    """an exception class used to adapt non-DBAPI database driver exceptions
    into DBAPI exceptions"""

    message: str
    """string message indicating the error"""

    orig: Exception
    """the original driver-level exception object"""

that looks kind of boring so we can also add an accessor on

class DBAPIError(StatementError):
called "driver_exception". This would work similarly to how we have "dbapi_connection" / "driver_connection" on Connection.

that way someone that knows asyncpg can receive the top level SQLAlchemy DBAPIException and just refer to .driver_exception to get all the asyncpg fields.

@zzzeek zzzeek added engine engines, connections, transactions, isolation levels, execution options use case not really a feature or a bug; can be support for new DB features or user use cases not anticipated asyncio labels May 23, 2022
@zzzeek zzzeek added this to the 2.0 milestone May 23, 2022
@CaselIT
Copy link
Member

CaselIT commented May 23, 2022

makes sense. this would also work for sync driver, only "dbapi_expection" / "driver_exception" would point to the same object

@zzzeek
Copy link
Member Author

zzzeek commented Aug 3, 2022

if someone wants to work on this sooner, it can be 2.0, but for now not an urgent need here

@zzzeek zzzeek added feature and removed use case not really a feature or a bug; can be support for new DB features or user use cases not anticipated labels Aug 3, 2022
@zzzeek zzzeek modified the milestones: 2.0, 2.1 Aug 3, 2022
@CaselIT CaselIT added the PRs (with tests!) welcome a fix or feature which is appropriate to be implemented by volunteers label May 27, 2023
@bhperry
Copy link

bhperry commented Aug 7, 2024

This would be great. Currently in order to catch nowait lock errors from asyncpg I am doing string search for "LockNotAvailable" in the error message. Would much prefer to have access to the actual asyncpg exception.

@DeoLeung
Copy link

DeoLeung commented Aug 8, 2024

looking forward to get the origin exception as well
currently I can do e.orig.pgcode and match to the origin exception

@bhperry
Copy link

bhperry commented Aug 8, 2024

Is pgcode an int code?

@DeoLeung
Copy link

DeoLeung commented Aug 9, 2024

nope, but looking at the asyncpg.exception, it seems ok to do .startswith('42') to match to SyntaxOrAcceessError, same of other Exceptions :)

@jmehnle
Copy link
Contributor

jmehnle commented Apr 29, 2025

I believe I'm running into this issue. I'm running SQLAlchemy 2.0 on top of asyncpg, and when some database statement returns, e.g., a not_null_violation PostgreSQL error, SQLAlchemy raises a sqlalchemy.exc.IntegrityError exception. However, when I try to inspect that exception's orig attribute, what I see is an exception that, by its __str__ value, pretends to be a asyncpg.exceptions.NotNullViolationError exception (as I would expect) but in reality is some obscure custom exception class with the following inheritance chain:

  • sqlalchemy.dialects.postgresql.asyncpg.AsyncAdapt_asyncpg_dbapi.IntegrityError
  • sqlalchemy.dialects.postgresql.asyncpg.AsyncAdapt_asyncpg_dbapi.DatabaseError
  • sqlalchemy.dialects.postgresql.asyncpg.AsyncAdapt_asyncpg_dbapi.Error
  • Exception
  • BaseException

I think https://docs.sqlalchemy.org/en/20/core/exceptions.html#sqlalchemy.exc.DBAPIError makes an attempt at explaining this, but unfortunately that explanation went completely past my head.

SQLAlchemy's IntegrityError exception is too general as I need to distinguish between not_null_violation, unique_violation, and other types of PostgreSQL errors that map to IntegrityError. How can I inspect the original exception without resorting to string matching? What is the reason that StatementError.orig doesn't actually expose the original exception but rather some chimera version of it?

@DeoLeung
Copy link

    except ProgrammingError as e:
      if e.orig.pgcode == UndefinedTableError.sqlstate:

hmmm, still string matching but a bit better workaround :)

@CaselIT
Copy link
Member

CaselIT commented Apr 30, 2025

How can I inspect the original exception without resorting to string matching? What is the reason that StatementError.orig doesn't actually expose the original exception but rather some chimera version of it?

That was done to respect the pep249 exception hierarchy. The original exception is the cause of the custom pep249 excenption, since that's raised using raise new_exc from asyncpg_exc

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
asyncio engine engines, connections, transactions, isolation levels, execution options feature PRs (with tests!) welcome a fix or feature which is appropriate to be implemented by volunteers
Projects
None yet
Development

No branches or pull requests

5 participants