I have various functions which attempt to insert into a PostgreSQL database. In an attempt to remove redundancy for common errors that I was occurring I have created a context manager.
Solution
from contextlib import contextmanager
import psycopg2
class DomainSpecificException(Exception):
...
@contextmanager
def raise_on_insert(table_name: str) -> None:
try:
yield
except psycopg2.errors.InFailedSqlTransaction:
raise DomainSpecificException(
description=(
f'Failed to insert into: {table_name} because InFailedSqlTransaction, this is usually a sign that the upstream connection has spoiled.'
)
)
except psycopg2.errors.NotNullViolation:
raise DomainSpecificException(
description=(
f'Failed to insert into: {table_name} because NotNullViolation for column.'
)
)
except psycopg2.errors.UniqueViolation:
raise DomainSpecificException(
description=(
f'Failed to insert into: {table_name} because UniqueViolation for column'
),
)
except psycopg2.errors.ForeignKeyViolation:
raise DomainSpecificException(
description=(
f'Failed to insert into: {table_name} because ForeignKeyViolation for column'
)
)
Usage
with raise_on_insert(table_name='table_1'):
... # Insert into db
Edit
I have improved the code as follows:
from contextlib import contextmanager
from psycopg2.errors import InFailedSqlTransaction, NotNullViolation, UniqueViolation, ForeignKeyViolation
INSERT_EXCEPTIONS: tuple[Exception, ...] = (
InFailedSqlTransaction,
NotNullViolation,
UniqueViolation,
ForeignKeyViolation
)
class DomainSpecificException(Exception):
pass
@contextmanager
def raise_on_insert(table_name: str) -> None:
try:
yield
except INSERT_EXCEPTIONS as err:
raise DomainSpecificException(description=f'Failed to insert into: {table_name} because {type(err).__name__}')