Skip to content

Commit 6e0e0c0

Browse files
authored
Merge pull request #133 from psqlpy-python/support_dbapi
Added DBAPI exceptions and connect method to single connection creation
2 parents 2e4b134 + 0a484a8 commit 6e0e0c0

14 files changed

+326
-94
lines changed

docs/components/connection_pool.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -125,15 +125,15 @@ db_pool: Final = ConnectionPool(
125125
```py
126126
from typing import Final
127127

128-
from psqlpy import connect
128+
from psqlpy import connect_pool
129129

130130

131-
db_pool: Final = connect(
131+
db_pool: Final = connect_pool(
132132
dsn="postgres://postgres:postgres@localhost:5432/postgres",
133133
max_db_pool_size=10,
134134
)
135135
```
136-
`connect` function has the same parameters as `ConnectionPool`.
136+
`connect_pool` function has the same parameters as `ConnectionPool`.
137137

138138
### Use Connection Pool as context manager
139139
```py

pyproject.toml

+4
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ ignore = [
9696
"D103", # Missing docstring in public function
9797
"S311", # Standard pseudo-random generators are not suitable for security/cryptographic purposes
9898
]
99+
"python/psqlpy/_internal/exceptions.pyi" = [
100+
"D205",
101+
"RUF002",
102+
]
99103
"./psqlpy-stress/psqlpy_stress/migrations/env.py" = ["INP001"]
100104
"examples/*" = ["INP001"]
101105

python/psqlpy/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
TargetSessionAttrs,
1818
Transaction,
1919
connect,
20+
connect_pool,
2021
)
2122

2223
__all__ = [
@@ -38,4 +39,5 @@
3839
"TargetSessionAttrs",
3940
"Transaction",
4041
"connect",
42+
"connect_pool",
4143
]

python/psqlpy/_internal/__init__.pyi

+29-1
Original file line numberDiff line numberDiff line change
@@ -886,6 +886,34 @@ class Transaction:
886886
number of inserted rows;
887887
"""
888888

889+
async def connect(
890+
dsn: str | None = None,
891+
username: str | None = None,
892+
password: str | None = None,
893+
host: str | None = None,
894+
hosts: list[str] | None = None,
895+
port: int | None = None,
896+
ports: list[int] | None = None,
897+
db_name: str | None = None,
898+
target_session_attrs: TargetSessionAttrs | None = None,
899+
options: str | None = None,
900+
application_name: str | None = None,
901+
connect_timeout_sec: int | None = None,
902+
connect_timeout_nanosec: int | None = None,
903+
tcp_user_timeout_sec: int | None = None,
904+
tcp_user_timeout_nanosec: int | None = None,
905+
keepalives: bool | None = None,
906+
keepalives_idle_sec: int | None = None,
907+
keepalives_idle_nanosec: int | None = None,
908+
keepalives_interval_sec: int | None = None,
909+
keepalives_interval_nanosec: int | None = None,
910+
keepalives_retries: int | None = None,
911+
load_balance_hosts: LoadBalanceHosts | None = None,
912+
ssl_mode: SslMode | None = None,
913+
ca_file: str | None = None,
914+
) -> Connection:
915+
"""Create new standalone connection."""
916+
889917
class Connection:
890918
"""Connection from Database Connection Pool.
891919
@@ -1336,7 +1364,7 @@ class ConnectionPool:
13361364
def close(self: Self) -> None:
13371365
"""Close the connection pool."""
13381366

1339-
def connect(
1367+
def connect_pool(
13401368
dsn: str | None = None,
13411369
username: str | None = None,
13421370
password: str | None = None,

python/psqlpy/_internal/exceptions.pyi

+72-13
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,68 @@
1-
class RustPSQLDriverPyBaseError(Exception):
2-
"""Base PSQL-Rust-Engine exception."""
1+
class WarningError(Exception):
2+
"""
3+
Exception raised for important warnings
4+
like data truncations while inserting, etc.
5+
"""
6+
7+
class Error(Exception):
8+
"""
9+
Exception that is the base class of all other error exceptions.
310
4-
class BaseConnectionPoolError(RustPSQLDriverPyBaseError):
11+
You can use this to catch all errors with one single except statement.
12+
"""
13+
14+
class InterfaceError(Error):
15+
"""
16+
Exception raised for errors that are related to the
17+
database interface rather than the database itself.
18+
"""
19+
20+
class DatabaseError(Error):
21+
"""Exception raised for errors that are related to the database."""
22+
23+
class DataError(DatabaseError):
24+
"""
25+
Exception raised for errors that are due to problems with
26+
the processed data like division by zero, numeric value out of range, etc.
27+
"""
28+
29+
class OperationalError(DatabaseError):
30+
"""
31+
Exception raised for errors that are related to the database’s operation
32+
and not necessarily under the control of the programmer,
33+
e.g. an unexpected disconnect occurs, the data source name is not found,
34+
a transaction could not be processed, a memory allocation error
35+
occurred during processing, etc.
36+
"""
37+
38+
class IntegrityError(DatabaseError):
39+
"""
40+
Exception raised when the relational integrity of the
41+
database is affected, e.g. a foreign key check fails.
42+
"""
43+
44+
class InternalError(DatabaseError):
45+
"""
46+
Exception raised when the database encounters an internal error,
47+
e.g. the cursor is not valid anymore, the transaction is out of sync, etc.
48+
"""
49+
50+
class ProgrammingError(DatabaseError):
51+
"""
52+
Exception raised for programming errors, e.g. table not found or
53+
already exists, syntax error in the SQL statement,
54+
wrong number of parameters specified, etc.
55+
"""
56+
57+
class NotSupportedError(DatabaseError):
58+
"""
59+
Exception raised in case a method or database API was used which
60+
is not supported by the database, e.g. requesting a .rollback()
61+
on a connection that does not support transaction
62+
or has transactions turned off.
63+
"""
64+
65+
class BaseConnectionPoolError(InterfaceError):
566
"""Base error for all Connection Pool errors."""
667

768
class ConnectionPoolBuildError(BaseConnectionPoolError):
@@ -13,7 +74,7 @@ class ConnectionPoolConfigurationError(BaseConnectionPoolError):
1374
class ConnectionPoolExecuteError(BaseConnectionPoolError):
1475
"""Error in connection pool execution."""
1576

16-
class BaseConnectionError(RustPSQLDriverPyBaseError):
77+
class BaseConnectionError(InterfaceError):
1778
"""Base error for Connection errors."""
1879

1980
class ConnectionExecuteError(BaseConnectionError):
@@ -22,7 +83,7 @@ class ConnectionExecuteError(BaseConnectionError):
2283
class ConnectionClosedError(BaseConnectionError):
2384
"""Error if underlying connection is already closed."""
2485

25-
class BaseTransactionError(RustPSQLDriverPyBaseError):
86+
class BaseTransactionError(InterfaceError):
2687
"""Base error for all transaction errors."""
2788

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

46-
class BaseCursorError(RustPSQLDriverPyBaseError):
107+
class BaseCursorError(InterfaceError):
47108
"""Base error for Cursor errors."""
48109

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

61-
class UUIDValueConvertError(RustPSQLDriverPyBaseError):
122+
class UUIDValueConvertError(DataError):
62123
"""Error if it's impossible to convert py string UUID into rust UUID."""
63124

64-
class MacAddrConversionError(RustPSQLDriverPyBaseError):
125+
class MacAddrConversionError(DataError):
65126
"""Error if cannot convert MacAddr string value to rust type."""
66127

67-
class RustToPyValueMappingError(RustPSQLDriverPyBaseError):
128+
class RustToPyValueMappingError(DataError):
68129
"""Error if it is not possible to covert rust type to python.
69130
70131
You can get it if you database contains data type that it not
71132
supported by this library.
72-
73-
It's better to handle this exception.
74133
"""
75134

76-
class PyToRustValueMappingError(RustPSQLDriverPyBaseError):
135+
class PyToRustValueMappingError(DataError):
77136
"""Error if it is not possible to covert python type to rust.
78137
79138
You can get this exception when executing queries with parameters.
80139
So, if there are no parameters for the query, don't handle this error.
81140
"""
82141

83-
class BaseListenerError(RustPSQLDriverPyBaseError):
142+
class BaseListenerError(InterfaceError):
84143
"""Base error for all Listener errors."""
85144

86145
class ListenerStartError(BaseListenerError):

python/psqlpy/exceptions.py

+20-2
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,20 @@
1313
CursorCloseError,
1414
CursorFetchError,
1515
CursorStartError,
16+
DatabaseError,
17+
DataError,
18+
Error,
19+
IntegrityError,
20+
InterfaceError,
21+
InternalError,
1622
ListenerCallbackError,
1723
ListenerClosedError,
1824
ListenerStartError,
1925
MacAddrConversionError,
26+
NotSupportedError,
27+
OperationalError,
28+
ProgrammingError,
2029
PyToRustValueMappingError,
21-
RustPSQLDriverPyBaseError,
2230
RustToPyValueMappingError,
2331
TransactionBeginError,
2432
TransactionClosedError,
@@ -27,6 +35,7 @@
2735
TransactionRollbackError,
2836
TransactionSavepointError,
2937
UUIDValueConvertError,
38+
WarningError,
3039
)
3140

3241
__all__ = [
@@ -44,12 +53,20 @@
4453
"CursorClosedError",
4554
"CursorFetchError",
4655
"CursorStartError",
56+
"DataError",
57+
"DatabaseError",
58+
"Error",
59+
"IntegrityError",
60+
"InterfaceError",
61+
"InternalError",
4762
"ListenerCallbackError",
4863
"ListenerClosedError",
4964
"ListenerStartError",
5065
"MacAddrConversionError",
66+
"NotSupportedError",
67+
"OperationalError",
68+
"ProgrammingError",
5169
"PyToRustValueMappingError",
52-
"RustPSQLDriverPyBaseError",
5370
"RustToPyValueMappingError",
5471
"TransactionBeginError",
5572
"TransactionClosedError",
@@ -58,4 +75,5 @@
5875
"TransactionRollbackError",
5976
"TransactionSavepointError",
6077
"UUIDValueConvertError",
78+
"WarningError",
6179
]

python/tests/test_connection_pool.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,19 @@
55
ConnRecyclingMethod,
66
LoadBalanceHosts,
77
TargetSessionAttrs,
8-
connect,
8+
connect_pool,
99
)
1010
from psqlpy.exceptions import (
1111
ConnectionPoolConfigurationError,
12-
RustPSQLDriverPyBaseError,
12+
InterfaceError,
1313
)
1414

1515
pytestmark = pytest.mark.anyio
1616

1717

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

@@ -106,7 +106,7 @@ async def test_pool_target_session_attrs(
106106
)
107107

108108
if target_session_attrs == TargetSessionAttrs.ReadOnly:
109-
with pytest.raises(expected_exception=RustPSQLDriverPyBaseError):
109+
with pytest.raises(expected_exception=InterfaceError):
110110
await pg_pool.connection()
111111
else:
112112
conn = await pg_pool.connection()
@@ -143,7 +143,7 @@ async def test_close_connection_pool() -> None:
143143

144144
pg_pool.close()
145145

146-
with pytest.raises(expected_exception=RustPSQLDriverPyBaseError):
146+
with pytest.raises(expected_exception=InterfaceError):
147147
await pg_pool.connection()
148148

149149

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

159-
with pytest.raises(expected_exception=RustPSQLDriverPyBaseError):
159+
with pytest.raises(expected_exception=InterfaceError):
160160
await pg_pool.connection()

python/tests/test_transaction.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
SynchronousCommit,
1212
)
1313
from psqlpy.exceptions import (
14-
RustPSQLDriverPyBaseError,
14+
InterfaceError,
1515
TransactionBeginError,
1616
TransactionExecuteError,
1717
TransactionSavepointError,
@@ -50,7 +50,7 @@ async def test_transaction_init_parameters(
5050
f"INSERT INTO {table_name} VALUES ($1, $2)",
5151
parameters=[100, "test_name"],
5252
)
53-
except RustPSQLDriverPyBaseError:
53+
except InterfaceError:
5454
assert read_variant is ReadVariant.ReadOnly
5555
else:
5656
assert read_variant is not ReadVariant.ReadOnly
@@ -287,7 +287,7 @@ async def test_transaction_fetch_row_more_than_one_row(
287287
) -> None:
288288
connection = await psql_pool.connection()
289289
async with connection.transaction() as transaction:
290-
with pytest.raises(RustPSQLDriverPyBaseError):
290+
with pytest.raises(InterfaceError):
291291
await transaction.fetch_row(
292292
f"SELECT * FROM {table_name}",
293293
[],
@@ -313,7 +313,7 @@ async def test_transaction_fetch_val_more_than_one_row(
313313
) -> None:
314314
connection = await psql_pool.connection()
315315
async with connection.transaction() as transaction:
316-
with pytest.raises(RustPSQLDriverPyBaseError):
316+
with pytest.raises(InterfaceError):
317317
await transaction.fetch_row(
318318
f"SELECT * FROM {table_name}",
319319
[],

0 commit comments

Comments
 (0)