Skip to content

Added DBAPI exceptions and connect method to single connection creation #133

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

Merged
merged 2 commits into from
May 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/components/connection_pool.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,15 +125,15 @@ db_pool: Final = ConnectionPool(
```py
from typing import Final

from psqlpy import connect
from psqlpy import connect_pool


db_pool: Final = connect(
db_pool: Final = connect_pool(
dsn="postgres://postgres:postgres@localhost:5432/postgres",
max_db_pool_size=10,
)
```
`connect` function has the same parameters as `ConnectionPool`.
`connect_pool` function has the same parameters as `ConnectionPool`.

### Use Connection Pool as context manager
```py
Expand Down
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ ignore = [
"D103", # Missing docstring in public function
"S311", # Standard pseudo-random generators are not suitable for security/cryptographic purposes
]
"python/psqlpy/_internal/exceptions.pyi" = [
"D205",
"RUF002",
]
"./psqlpy-stress/psqlpy_stress/migrations/env.py" = ["INP001"]
"examples/*" = ["INP001"]

Expand Down
2 changes: 2 additions & 0 deletions python/psqlpy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
TargetSessionAttrs,
Transaction,
connect,
connect_pool,
)

__all__ = [
Expand All @@ -38,4 +39,5 @@
"TargetSessionAttrs",
"Transaction",
"connect",
"connect_pool",
]
30 changes: 29 additions & 1 deletion python/psqlpy/_internal/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,34 @@ class Transaction:
number of inserted rows;
"""

async def connect(
dsn: str | None = None,
username: str | None = None,
password: str | None = None,
host: str | None = None,
hosts: list[str] | None = None,
port: int | None = None,
ports: list[int] | None = None,
db_name: str | None = None,
target_session_attrs: TargetSessionAttrs | None = None,
options: str | None = None,
application_name: str | None = None,
connect_timeout_sec: int | None = None,
connect_timeout_nanosec: int | None = None,
tcp_user_timeout_sec: int | None = None,
tcp_user_timeout_nanosec: int | None = None,
keepalives: bool | None = None,
keepalives_idle_sec: int | None = None,
keepalives_idle_nanosec: int | None = None,
keepalives_interval_sec: int | None = None,
keepalives_interval_nanosec: int | None = None,
keepalives_retries: int | None = None,
load_balance_hosts: LoadBalanceHosts | None = None,
ssl_mode: SslMode | None = None,
ca_file: str | None = None,
) -> Connection:
"""Create new standalone connection."""

class Connection:
"""Connection from Database Connection Pool.

Expand Down Expand Up @@ -1336,7 +1364,7 @@ class ConnectionPool:
def close(self: Self) -> None:
"""Close the connection pool."""

def connect(
def connect_pool(
dsn: str | None = None,
username: str | None = None,
password: str | None = None,
Expand Down
85 changes: 72 additions & 13 deletions python/psqlpy/_internal/exceptions.pyi
Original file line number Diff line number Diff line change
@@ -1,7 +1,68 @@
class RustPSQLDriverPyBaseError(Exception):
"""Base PSQL-Rust-Engine exception."""
class WarningError(Exception):
"""
Exception raised for important warnings
like data truncations while inserting, etc.
"""

class Error(Exception):
"""
Exception that is the base class of all other error exceptions.

class BaseConnectionPoolError(RustPSQLDriverPyBaseError):
You can use this to catch all errors with one single except statement.
"""

class InterfaceError(Error):
"""
Exception raised for errors that are related to the
database interface rather than the database itself.
"""

class DatabaseError(Error):
"""Exception raised for errors that are related to the database."""

class DataError(DatabaseError):
"""
Exception raised for errors that are due to problems with
the processed data like division by zero, numeric value out of range, etc.
"""

class OperationalError(DatabaseError):
"""
Exception raised for errors that are related to the database’s operation
and not necessarily under the control of the programmer,
e.g. an unexpected disconnect occurs, the data source name is not found,
a transaction could not be processed, a memory allocation error
occurred during processing, etc.
"""

class IntegrityError(DatabaseError):
"""
Exception raised when the relational integrity of the
database is affected, e.g. a foreign key check fails.
"""

class InternalError(DatabaseError):
"""
Exception raised when the database encounters an internal error,
e.g. the cursor is not valid anymore, the transaction is out of sync, etc.
"""

class ProgrammingError(DatabaseError):
"""
Exception raised for programming errors, e.g. table not found or
already exists, syntax error in the SQL statement,
wrong number of parameters specified, etc.
"""

class NotSupportedError(DatabaseError):
"""
Exception raised in case a method or database API was used which
is not supported by the database, e.g. requesting a .rollback()
on a connection that does not support transaction
or has transactions turned off.
"""

class BaseConnectionPoolError(InterfaceError):
"""Base error for all Connection Pool errors."""

class ConnectionPoolBuildError(BaseConnectionPoolError):
Expand All @@ -13,7 +74,7 @@ class ConnectionPoolConfigurationError(BaseConnectionPoolError):
class ConnectionPoolExecuteError(BaseConnectionPoolError):
"""Error in connection pool execution."""

class BaseConnectionError(RustPSQLDriverPyBaseError):
class BaseConnectionError(InterfaceError):
"""Base error for Connection errors."""

class ConnectionExecuteError(BaseConnectionError):
Expand All @@ -22,7 +83,7 @@ class ConnectionExecuteError(BaseConnectionError):
class ConnectionClosedError(BaseConnectionError):
"""Error if underlying connection is already closed."""

class BaseTransactionError(RustPSQLDriverPyBaseError):
class BaseTransactionError(InterfaceError):
"""Base error for all transaction errors."""

class TransactionBeginError(BaseTransactionError):
Expand All @@ -43,7 +104,7 @@ class TransactionExecuteError(BaseTransactionError):
class TransactionClosedError(BaseTransactionError):
"""Error if underlying connection is already closed."""

class BaseCursorError(RustPSQLDriverPyBaseError):
class BaseCursorError(InterfaceError):
"""Base error for Cursor errors."""

class CursorStartError(BaseCursorError):
Expand All @@ -58,29 +119,27 @@ class CursorFetchError(BaseCursorError):
class CursorClosedError(BaseCursorError):
"""Error if underlying connection is already closed."""

class UUIDValueConvertError(RustPSQLDriverPyBaseError):
class UUIDValueConvertError(DataError):
"""Error if it's impossible to convert py string UUID into rust UUID."""

class MacAddrConversionError(RustPSQLDriverPyBaseError):
class MacAddrConversionError(DataError):
"""Error if cannot convert MacAddr string value to rust type."""

class RustToPyValueMappingError(RustPSQLDriverPyBaseError):
class RustToPyValueMappingError(DataError):
"""Error if it is not possible to covert rust type to python.

You can get it if you database contains data type that it not
supported by this library.

It's better to handle this exception.
"""

class PyToRustValueMappingError(RustPSQLDriverPyBaseError):
class PyToRustValueMappingError(DataError):
"""Error if it is not possible to covert python type to rust.

You can get this exception when executing queries with parameters.
So, if there are no parameters for the query, don't handle this error.
"""

class BaseListenerError(RustPSQLDriverPyBaseError):
class BaseListenerError(InterfaceError):
"""Base error for all Listener errors."""

class ListenerStartError(BaseListenerError):
Expand Down
22 changes: 20 additions & 2 deletions python/psqlpy/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,20 @@
CursorCloseError,
CursorFetchError,
CursorStartError,
DatabaseError,
DataError,
Error,
IntegrityError,
InterfaceError,
InternalError,
ListenerCallbackError,
ListenerClosedError,
ListenerStartError,
MacAddrConversionError,
NotSupportedError,
OperationalError,
ProgrammingError,
PyToRustValueMappingError,
RustPSQLDriverPyBaseError,
RustToPyValueMappingError,
TransactionBeginError,
TransactionClosedError,
Expand All @@ -27,6 +35,7 @@
TransactionRollbackError,
TransactionSavepointError,
UUIDValueConvertError,
WarningError,
)

__all__ = [
Expand All @@ -44,12 +53,20 @@
"CursorClosedError",
"CursorFetchError",
"CursorStartError",
"DataError",
"DatabaseError",
"Error",
"IntegrityError",
"InterfaceError",
"InternalError",
"ListenerCallbackError",
"ListenerClosedError",
"ListenerStartError",
"MacAddrConversionError",
"NotSupportedError",
"OperationalError",
"ProgrammingError",
"PyToRustValueMappingError",
"RustPSQLDriverPyBaseError",
"RustToPyValueMappingError",
"TransactionBeginError",
"TransactionClosedError",
Expand All @@ -58,4 +75,5 @@
"TransactionRollbackError",
"TransactionSavepointError",
"UUIDValueConvertError",
"WarningError",
]
12 changes: 6 additions & 6 deletions python/tests/test_connection_pool.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,19 @@
ConnRecyclingMethod,
LoadBalanceHosts,
TargetSessionAttrs,
connect,
connect_pool,
)
from psqlpy.exceptions import (
ConnectionPoolConfigurationError,
RustPSQLDriverPyBaseError,
InterfaceError,
)

pytestmark = pytest.mark.anyio


async def test_connect_func() -> None:
"""Test that connect function makes new connection pool."""
pg_pool = connect(
pg_pool = connect_pool(
dsn="postgres://postgres:postgres@localhost:5432/psqlpy_test",
)

Expand Down Expand Up @@ -106,7 +106,7 @@ async def test_pool_target_session_attrs(
)

if target_session_attrs == TargetSessionAttrs.ReadOnly:
with pytest.raises(expected_exception=RustPSQLDriverPyBaseError):
with pytest.raises(expected_exception=InterfaceError):
await pg_pool.connection()
else:
conn = await pg_pool.connection()
Expand Down Expand Up @@ -143,7 +143,7 @@ async def test_close_connection_pool() -> None:

pg_pool.close()

with pytest.raises(expected_exception=RustPSQLDriverPyBaseError):
with pytest.raises(expected_exception=InterfaceError):
await pg_pool.connection()


Expand All @@ -156,5 +156,5 @@ async def test_connection_pool_as_context_manager() -> None:
res = await conn.execute("SELECT 1")
assert res.result()

with pytest.raises(expected_exception=RustPSQLDriverPyBaseError):
with pytest.raises(expected_exception=InterfaceError):
await pg_pool.connection()
8 changes: 4 additions & 4 deletions python/tests/test_transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
SynchronousCommit,
)
from psqlpy.exceptions import (
RustPSQLDriverPyBaseError,
InterfaceError,
TransactionBeginError,
TransactionExecuteError,
TransactionSavepointError,
Expand Down Expand Up @@ -50,7 +50,7 @@ async def test_transaction_init_parameters(
f"INSERT INTO {table_name} VALUES ($1, $2)",
parameters=[100, "test_name"],
)
except RustPSQLDriverPyBaseError:
except InterfaceError:
assert read_variant is ReadVariant.ReadOnly
else:
assert read_variant is not ReadVariant.ReadOnly
Expand Down Expand Up @@ -287,7 +287,7 @@ async def test_transaction_fetch_row_more_than_one_row(
) -> None:
connection = await psql_pool.connection()
async with connection.transaction() as transaction:
with pytest.raises(RustPSQLDriverPyBaseError):
with pytest.raises(InterfaceError):
await transaction.fetch_row(
f"SELECT * FROM {table_name}",
[],
Expand All @@ -313,7 +313,7 @@ async def test_transaction_fetch_val_more_than_one_row(
) -> None:
connection = await psql_pool.connection()
async with connection.transaction() as transaction:
with pytest.raises(RustPSQLDriverPyBaseError):
with pytest.raises(InterfaceError):
await transaction.fetch_row(
f"SELECT * FROM {table_name}",
[],
Expand Down
Loading
Loading