sqlspec 0.1.1__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of sqlspec might be problematic. Click here for more details.

Files changed (42) hide show
  1. sqlspec/__metadata__.py +1 -1
  2. sqlspec/_serialization.py +1 -1
  3. sqlspec/_typing.py +112 -0
  4. sqlspec/adapters/adbc/__init__.py +0 -0
  5. sqlspec/adapters/adbc/config.py +54 -0
  6. sqlspec/adapters/aiosqlite/__init__.py +3 -0
  7. sqlspec/adapters/aiosqlite/config.py +112 -0
  8. sqlspec/adapters/asyncmy/__init__.py +3 -0
  9. sqlspec/adapters/asyncmy/config.py +194 -0
  10. sqlspec/adapters/asyncpg/__init__.py +0 -0
  11. sqlspec/adapters/asyncpg/config.py +155 -0
  12. sqlspec/adapters/duckdb/__init__.py +0 -0
  13. sqlspec/adapters/duckdb/config.py +101 -0
  14. sqlspec/adapters/oracledb/__init__.py +13 -0
  15. sqlspec/adapters/oracledb/config/__init__.py +9 -0
  16. sqlspec/adapters/oracledb/config/_asyncio.py +98 -0
  17. sqlspec/adapters/oracledb/config/_common.py +151 -0
  18. sqlspec/adapters/oracledb/config/_sync.py +102 -0
  19. sqlspec/adapters/psycopg/__init__.py +0 -0
  20. sqlspec/adapters/psycopg/config/__init__.py +9 -0
  21. sqlspec/adapters/psycopg/config/_async.py +84 -0
  22. sqlspec/adapters/psycopg/config/_common.py +72 -0
  23. sqlspec/adapters/psycopg/config/_sync.py +84 -0
  24. sqlspec/adapters/sqlite/__init__.py +0 -0
  25. sqlspec/adapters/sqlite/config.py +109 -0
  26. sqlspec/config.py +16 -0
  27. sqlspec/exceptions.py +29 -0
  28. sqlspec/extensions/__init__.py +0 -0
  29. sqlspec/extensions/litestar/__init__.py +0 -0
  30. sqlspec/extensions/litestar/plugin.py +34 -0
  31. sqlspec/filters.py +33 -28
  32. sqlspec/typing.py +287 -0
  33. sqlspec/utils/dataclass.py +11 -3
  34. sqlspec/{types → utils}/empty.py +1 -1
  35. sqlspec-0.3.0.dist-info/METADATA +84 -0
  36. sqlspec-0.3.0.dist-info/RECORD +42 -0
  37. {sqlspec-0.1.1.dist-info → sqlspec-0.3.0.dist-info}/WHEEL +1 -1
  38. sqlspec-0.3.0.dist-info/licenses/NOTICE +29 -0
  39. sqlspec/types/protocols.py +0 -117
  40. sqlspec-0.1.1.dist-info/METADATA +0 -25
  41. sqlspec-0.1.1.dist-info/RECORD +0 -14
  42. /sqlspec/{types → adapters}/__init__.py +0 -0
sqlspec/__metadata__.py CHANGED
@@ -4,7 +4,7 @@ from __future__ import annotations
4
4
 
5
5
  from importlib.metadata import PackageNotFoundError, metadata, version
6
6
 
7
- __all__ = ["__version__", "__project__"]
7
+ __all__ = ["__project__", "__version__"]
8
8
 
9
9
  try:
10
10
  __version__ = version("sqlspec")
sqlspec/_serialization.py CHANGED
@@ -14,7 +14,7 @@ try:
14
14
  except ImportError:
15
15
  try:
16
16
  from orjson import dumps as _encode_json # pyright: ignore[reportMissingImports]
17
- from orjson import loads as decode_json # type: ignore[no-redef,assignment]
17
+ from orjson import loads as decode_json # type: ignore[no-redef]
18
18
 
19
19
  def encode_json(data: Any) -> str:
20
20
  return _encode_json(data).decode("utf-8") # type: ignore[no-any-return]
sqlspec/_typing.py ADDED
@@ -0,0 +1,112 @@
1
+ """This is a simple wrapper around a few important classes in each library.
2
+
3
+ This is used to ensure compatibility when one or more of the libraries are installed.
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import (
9
+ Any,
10
+ ClassVar,
11
+ Protocol,
12
+ cast,
13
+ runtime_checkable,
14
+ )
15
+
16
+ from typing_extensions import TypeVar, dataclass_transform
17
+
18
+ T = TypeVar("T")
19
+ T_co = TypeVar("T_co", covariant=True)
20
+
21
+
22
+ try:
23
+ from pydantic import BaseModel, FailFast, TypeAdapter
24
+
25
+ PYDANTIC_INSTALLED = True
26
+ except ImportError:
27
+
28
+ @runtime_checkable
29
+ class BaseModel(Protocol): # type: ignore[no-redef]
30
+ """Placeholder Implementation"""
31
+
32
+ model_fields: ClassVar[dict[str, Any]]
33
+
34
+ def model_dump(self, *args: Any, **kwargs: Any) -> dict[str, Any]:
35
+ """Placeholder"""
36
+ return {}
37
+
38
+ @runtime_checkable
39
+ class TypeAdapter(Protocol[T_co]): # type: ignore[no-redef]
40
+ """Placeholder Implementation"""
41
+
42
+ def __init__(
43
+ self,
44
+ type: Any, # noqa: A002
45
+ *,
46
+ config: Any | None = None,
47
+ _parent_depth: int = 2,
48
+ module: str | None = None,
49
+ ) -> None:
50
+ """Init"""
51
+
52
+ def validate_python(
53
+ self,
54
+ object: Any, # noqa: A002
55
+ /,
56
+ *,
57
+ strict: bool | None = None,
58
+ from_attributes: bool | None = None,
59
+ context: dict[str, Any] | None = None,
60
+ ) -> T_co:
61
+ """Stub"""
62
+ return cast("T_co", object)
63
+
64
+ @runtime_checkable
65
+ class FailFast(Protocol): # type: ignore[no-redef]
66
+ """Placeholder Implementation for FailFast"""
67
+
68
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
69
+ """Init"""
70
+
71
+ PYDANTIC_INSTALLED = False # pyright: ignore[reportConstantRedefinition]
72
+
73
+ try:
74
+ from msgspec import (
75
+ UNSET,
76
+ Struct,
77
+ UnsetType, # pyright: ignore[reportAssignmentType]
78
+ convert,
79
+ )
80
+
81
+ MSGSPEC_INSTALLED: bool = True
82
+ except ImportError:
83
+ import enum
84
+
85
+ @dataclass_transform()
86
+ @runtime_checkable
87
+ class Struct(Protocol): # type: ignore[no-redef]
88
+ """Placeholder Implementation"""
89
+
90
+ __struct_fields__: ClassVar[tuple[str, ...]]
91
+
92
+ def convert(*args: Any, **kwargs: Any) -> Any: # type: ignore[no-redef]
93
+ """Placeholder implementation"""
94
+ return {}
95
+
96
+ class UnsetType(enum.Enum): # type: ignore[no-redef]
97
+ UNSET = "UNSET"
98
+
99
+ UNSET = UnsetType.UNSET # pyright: ignore[reportConstantRedefinition]
100
+ MSGSPEC_INSTALLED = False # pyright: ignore[reportConstantRedefinition]
101
+
102
+ __all__ = (
103
+ "MSGSPEC_INSTALLED",
104
+ "PYDANTIC_INSTALLED",
105
+ "UNSET",
106
+ "BaseModel",
107
+ "FailFast",
108
+ "Struct",
109
+ "TypeAdapter",
110
+ "UnsetType",
111
+ "convert",
112
+ )
File without changes
@@ -0,0 +1,54 @@
1
+ from __future__ import annotations
2
+
3
+ from contextlib import contextmanager
4
+ from dataclasses import dataclass
5
+ from typing import TYPE_CHECKING, TypeVar
6
+
7
+ from sqlspec.config import GenericDatabaseConfig
8
+ from sqlspec.utils.empty import Empty
9
+
10
+ if TYPE_CHECKING:
11
+ from collections.abc import Generator
12
+ from typing import Any
13
+
14
+ from adbc_driver_manager.dbapi import Connection, Cursor
15
+
16
+ from sqlspec.utils.empty import EmptyType
17
+
18
+ __all__ = ("AdbcDatabaseConfig",)
19
+
20
+ ConnectionT = TypeVar("ConnectionT", bound="Connection")
21
+ CursorT = TypeVar("CursorT", bound="Cursor")
22
+
23
+
24
+ @dataclass
25
+ class AdbcDatabaseConfig(GenericDatabaseConfig):
26
+ """Configuration for ADBC connections.
27
+
28
+ This class provides configuration options for ADBC database connections using the
29
+ ADBC Driver Manager.([1](https://arrow.apache.org/adbc/current/python/api/adbc_driver_manager.html))
30
+ """
31
+
32
+ uri: str | EmptyType = Empty
33
+ """Database URI"""
34
+ driver_name: str | EmptyType = Empty
35
+ """Name of the ADBC driver to use"""
36
+ db_kwargs: dict[str, Any] | None = None
37
+ """Additional database-specific connection parameters"""
38
+
39
+ @property
40
+ def connection_params(self) -> dict[str, Any]:
41
+ """Return the connection parameters as a dict."""
42
+ return {
43
+ k: v
44
+ for k, v in {"uri": self.uri, "driver": self.driver_name, **(self.db_kwargs or {})}.items()
45
+ if v is not Empty
46
+ }
47
+
48
+ @contextmanager
49
+ def provide_connection(self, *args: Any, **kwargs: Any) -> Generator[Connection, None, None]:
50
+ """Create and provide a database connection."""
51
+ from adbc_driver_manager.dbapi import connect
52
+
53
+ with connect(**self.connection_params) as connection:
54
+ yield connection
@@ -0,0 +1,3 @@
1
+ from .config import AiosqliteConfig
2
+
3
+ __all__ = ("AiosqliteConfig",)
@@ -0,0 +1,112 @@
1
+ from __future__ import annotations
2
+
3
+ from contextlib import asynccontextmanager
4
+ from dataclasses import dataclass
5
+ from typing import TYPE_CHECKING, Any
6
+
7
+ from sqlspec.config import GenericDatabaseConfig
8
+ from sqlspec.exceptions import ImproperConfigurationError
9
+ from sqlspec.utils.dataclass import simple_asdict
10
+ from sqlspec.utils.empty import Empty, EmptyType
11
+
12
+ if TYPE_CHECKING:
13
+ from collections.abc import AsyncGenerator
14
+ from sqlite3 import Connection as SQLite3Connection
15
+ from typing import Literal
16
+
17
+ from aiosqlite import Connection
18
+
19
+ __all__ = ("AiosqliteConfig",)
20
+
21
+
22
+ @dataclass
23
+ class AiosqliteConfig(GenericDatabaseConfig):
24
+ """Configuration for Aiosqlite database connections.
25
+
26
+ This class provides configuration options for Aiosqlite database connections, wrapping all parameters
27
+ available to aiosqlite.connect().
28
+
29
+ For details see: https://github.com/omnilib/aiosqlite/blob/main/aiosqlite/__init__.pyi
30
+ """
31
+
32
+ database: str
33
+ """The path to the database file to be opened. Pass ":memory:" to open a connection to a database that resides in RAM instead of on disk."""
34
+
35
+ timeout: float | EmptyType = Empty
36
+ """How many seconds the connection should wait before raising an OperationalError when a table is locked. If another thread or process has acquired a shared lock, a wait for the specified timeout occurs."""
37
+
38
+ detect_types: int | EmptyType = Empty
39
+ """Control whether and how data types are detected. It can be 0 (default) or a combination of PARSE_DECLTYPES and PARSE_COLNAMES."""
40
+
41
+ isolation_level: Literal["DEFERRED", "IMMEDIATE", "EXCLUSIVE"] | None | EmptyType = Empty
42
+ """The isolation_level of the connection. This can be None for autocommit mode or one of "DEFERRED", "IMMEDIATE" or "EXCLUSIVE"."""
43
+
44
+ check_same_thread: bool | EmptyType = Empty
45
+ """If True (default), ProgrammingError is raised if the database connection is used by a thread other than the one that created it. If False, the connection may be shared across multiple threads."""
46
+
47
+ factory: type[SQLite3Connection] | EmptyType = Empty
48
+ """A custom Connection class factory. If given, must be a callable that returns a Connection instance."""
49
+
50
+ cached_statements: int | EmptyType = Empty
51
+ """The number of statements that SQLite will cache for this connection. The default is 128."""
52
+
53
+ uri: bool | EmptyType = Empty
54
+ """If set to True, database is interpreted as a URI with supported options."""
55
+
56
+ @property
57
+ def connection_config_dict(self) -> dict[str, Any]:
58
+ """Return the connection configuration as a dict.
59
+
60
+ Returns:
61
+ A string keyed dict of config kwargs for the aiosqlite.connect() function.
62
+ """
63
+ return simple_asdict(self, exclude_empty=True, convert_nested=False)
64
+
65
+ async def create_connection(self) -> Connection:
66
+ """Create and return a new database connection.
67
+
68
+ Returns:
69
+ A new Aiosqlite connection instance.
70
+
71
+ Raises:
72
+ ImproperConfigurationError: If the connection could not be established.
73
+ """
74
+ import aiosqlite
75
+
76
+ try:
77
+ return await aiosqlite.connect(**self.connection_config_dict)
78
+ except Exception as e:
79
+ msg = f"Could not configure the Aiosqlite connection. Error: {e!s}"
80
+ raise ImproperConfigurationError(msg) from e
81
+
82
+ @asynccontextmanager
83
+ async def lifespan(self, *args: Any, **kwargs: Any) -> AsyncGenerator[None, None]:
84
+ """Manage the lifecycle of a database connection.
85
+
86
+ Yields:
87
+ None
88
+
89
+ Raises:
90
+ ImproperConfigurationError: If the connection could not be established.
91
+ """
92
+ connection = await self.create_connection()
93
+ try:
94
+ yield
95
+ finally:
96
+ await connection.close()
97
+
98
+ @asynccontextmanager
99
+ async def provide_connection(self, *args: Any, **kwargs: Any) -> AsyncGenerator[Connection, None]:
100
+ """Create and provide a database connection.
101
+
102
+ Yields:
103
+ An Aiosqlite connection instance.
104
+
105
+ Raises:
106
+ ImproperConfigurationError: If the connection could not be established.
107
+ """
108
+ connection = await self.create_connection()
109
+ try:
110
+ yield connection
111
+ finally:
112
+ await connection.close()
@@ -0,0 +1,3 @@
1
+ from .config import AsyncMyConfig, AsyncmyPoolConfig
2
+
3
+ __all__ = ("AsyncMyConfig", "AsyncmyPoolConfig")
@@ -0,0 +1,194 @@
1
+ from __future__ import annotations
2
+
3
+ from contextlib import asynccontextmanager
4
+ from dataclasses import dataclass
5
+ from typing import TYPE_CHECKING, TypeVar
6
+
7
+ from sqlspec.exceptions import ImproperConfigurationError
8
+ from sqlspec.utils.dataclass import simple_asdict
9
+ from sqlspec.utils.empty import Empty, EmptyType
10
+
11
+ if TYPE_CHECKING:
12
+ from collections.abc import AsyncGenerator
13
+ from typing import Any
14
+
15
+ from asyncmy.connection import Connection
16
+ from asyncmy.cursors import Cursor, DictCursor
17
+ from asyncmy.pool import Pool
18
+
19
+ __all__ = (
20
+ "AsyncMyConfig",
21
+ "AsyncmyPoolConfig",
22
+ )
23
+
24
+
25
+ T = TypeVar("T")
26
+
27
+
28
+ @dataclass
29
+ class AsyncmyPoolConfig:
30
+ """Configuration for Asyncmy's connection pool.
31
+
32
+ This class provides configuration options for Asyncmy database connection pools.
33
+
34
+ For details see: https://github.com/long2ice/asyncmy
35
+ """
36
+
37
+ host: str | EmptyType = Empty
38
+ """Host where the database server is located."""
39
+
40
+ user: str | EmptyType = Empty
41
+ """The username used to authenticate with the database."""
42
+
43
+ password: str | EmptyType = Empty
44
+ """The password used to authenticate with the database."""
45
+
46
+ database: str | EmptyType = Empty
47
+ """The database name to use."""
48
+
49
+ port: int | EmptyType = Empty
50
+ """The TCP/IP port of the MySQL server. Must be an integer."""
51
+
52
+ unix_socket: str | EmptyType = Empty
53
+ """The location of the Unix socket file."""
54
+
55
+ charset: str | EmptyType = Empty
56
+ """The character set to use for the connection."""
57
+
58
+ connect_timeout: float | EmptyType = Empty
59
+ """Timeout before throwing an error when connecting."""
60
+
61
+ read_default_file: str | EmptyType = Empty
62
+ """MySQL configuration file to read."""
63
+
64
+ read_default_group: str | EmptyType = Empty
65
+ """Group to read from the configuration file."""
66
+
67
+ autocommit: bool | EmptyType = Empty
68
+ """If True, autocommit mode will be enabled."""
69
+
70
+ local_infile: bool | EmptyType = Empty
71
+ """If True, enables LOAD LOCAL INFILE."""
72
+
73
+ ssl: dict[str, Any] | bool | EmptyType = Empty
74
+ """If present, a dictionary of SSL connection parameters, or just True."""
75
+
76
+ sql_mode: str | EmptyType = Empty
77
+ """Default SQL_MODE to use."""
78
+
79
+ init_command: str | EmptyType = Empty
80
+ """Initial SQL statement to execute once connected."""
81
+
82
+ cursor_class: type[Cursor] | type[DictCursor] | EmptyType = Empty
83
+ """Custom cursor class to use."""
84
+
85
+ minsize: int | EmptyType = Empty
86
+ """Minimum number of connections to keep in the pool."""
87
+
88
+ maxsize: int | EmptyType = Empty
89
+ """Maximum number of connections allowed in the pool."""
90
+
91
+ echo: bool | EmptyType = Empty
92
+ """If True, logging will be enabled for all SQL statements."""
93
+
94
+ pool_recycle: int | EmptyType = Empty
95
+ """Number of seconds after which a connection is recycled."""
96
+
97
+ @property
98
+ def pool_config_dict(self) -> dict[str, Any]:
99
+ """Return the pool configuration as a dict.
100
+
101
+ Returns:
102
+ A string keyed dict of config kwargs for the Asyncmy create_pool function.
103
+ """
104
+ return simple_asdict(self, exclude_empty=True, convert_nested=False)
105
+
106
+
107
+ @dataclass
108
+ class AsyncMyConfig:
109
+ """Asyncmy Configuration."""
110
+
111
+ pool_config: AsyncmyPoolConfig | None = None
112
+ """Asyncmy Pool configuration"""
113
+
114
+ pool_instance: Pool | None = None
115
+ """Optional pool to use.
116
+
117
+ If set, the plugin will use the provided pool rather than instantiate one.
118
+ """
119
+
120
+ @property
121
+ def pool_config_dict(self) -> dict[str, Any]:
122
+ """Return the pool configuration as a dict.
123
+
124
+ Returns:
125
+ A string keyed dict of config kwargs for the Asyncmy create_pool function.
126
+ """
127
+ if self.pool_config:
128
+ return simple_asdict(self.pool_config, exclude_empty=True, convert_nested=False)
129
+ msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
130
+ raise ImproperConfigurationError(msg)
131
+
132
+ async def create_pool(self) -> Pool:
133
+ """Return a pool. If none exists yet, create one.
134
+
135
+ Returns:
136
+ Getter that returns the pool instance used by the plugin.
137
+
138
+ Raises:
139
+ ImproperConfigurationError: If the pool could not be created.
140
+ """
141
+ if self.pool_instance is not None:
142
+ return self.pool_instance
143
+
144
+ if self.pool_config is None:
145
+ msg = "One of 'pool_config' or 'pool_instance' must be provided."
146
+ raise ImproperConfigurationError(msg)
147
+
148
+ try:
149
+ import asyncmy
150
+
151
+ self.pool_instance = await asyncmy.create_pool(**self.pool_config_dict)
152
+ return self.pool_instance
153
+ except Exception as e:
154
+ msg = f"Could not configure the Asyncmy pool. Error: {e!s}"
155
+ raise ImproperConfigurationError(msg) from e
156
+
157
+ async def provide_pool(self, *args: Any, **kwargs: Any) -> Pool:
158
+ """Create a pool instance.
159
+
160
+ Returns:
161
+ A Pool instance.
162
+ """
163
+ return await self.create_pool()
164
+
165
+ @asynccontextmanager
166
+ async def lifespan(self, *args: Any, **kwargs: Any) -> AsyncGenerator[None, None]:
167
+ """Manage the lifecycle of a database connection pool.
168
+
169
+ Yields:
170
+ None
171
+
172
+ Raises:
173
+ ImproperConfigurationError: If the pool could not be established.
174
+ """
175
+ pool = await self.create_pool()
176
+ try:
177
+ yield
178
+ finally:
179
+ pool.close()
180
+ await pool.wait_closed()
181
+
182
+ @asynccontextmanager
183
+ async def provide_connection(self, *args: Any, **kwargs: Any) -> AsyncGenerator[Connection, None]:
184
+ """Create and provide a database connection.
185
+
186
+ Yields:
187
+ An Asyncmy connection instance.
188
+
189
+ Raises:
190
+ ImproperConfigurationError: If the connection could not be established.
191
+ """
192
+ pool = await self.provide_pool(*args, **kwargs)
193
+ async with pool.acquire() as connection:
194
+ yield connection
File without changes
@@ -0,0 +1,155 @@
1
+ from __future__ import annotations
2
+
3
+ from contextlib import asynccontextmanager
4
+ from dataclasses import dataclass
5
+ from typing import TYPE_CHECKING, TypeVar
6
+
7
+ from asyncpg import Record
8
+ from asyncpg import create_pool as asyncpg_create_pool
9
+
10
+ from sqlspec._serialization import decode_json, encode_json
11
+ from sqlspec.config import GenericDatabaseConfig, GenericPoolConfig
12
+ from sqlspec.exceptions import ImproperConfigurationError
13
+ from sqlspec.utils.dataclass import simple_asdict
14
+ from sqlspec.utils.empty import Empty, EmptyType
15
+
16
+ if TYPE_CHECKING:
17
+ from asyncio import AbstractEventLoop
18
+ from collections.abc import AsyncGenerator, Awaitable, Callable, Coroutine
19
+ from typing import Any
20
+
21
+ from asyncpg.connection import Connection
22
+ from asyncpg.pool import Pool, PoolConnectionProxy
23
+
24
+ __all__ = (
25
+ "AsyncPgConfig",
26
+ "AsyncPgPoolConfig",
27
+ )
28
+
29
+
30
+ T = TypeVar("T")
31
+
32
+
33
+ @dataclass
34
+ class AsyncPgPoolConfig(GenericPoolConfig):
35
+ """Configuration for Asyncpg's :class:`Pool <asyncpg.pool.Pool>`.
36
+
37
+ For details see: https://magicstack.github.io/asyncpg/current/api/index.html#connection-pools
38
+ """
39
+
40
+ dsn: str
41
+ """Connection arguments specified using as a single string in the following format: ``postgres://user:pass@host:port/database?option=value``
42
+ """
43
+ connect_kwargs: dict[Any, Any] | None | EmptyType = Empty
44
+ """A dictionary of arguments which will be passed directly to the ``connect()`` method as keyword arguments.
45
+ """
46
+ connection_class: type[Connection] | None | EmptyType = Empty
47
+ """The class to use for connections. Must be a subclass of Connection
48
+ """
49
+ record_class: type[Record] | EmptyType = Empty
50
+ """If specified, the class to use for records returned by queries on the connections in this pool. Must be a subclass of Record."""
51
+
52
+ min_size: int | EmptyType = Empty
53
+ """The number of connections to keep open inside the connection pool."""
54
+ max_size: int | EmptyType = Empty
55
+ """The number of connections to allow in connection pool “overflow”, that is connections that can be opened above
56
+ and beyond the pool_size setting, which defaults to 10."""
57
+
58
+ max_queries: int | EmptyType = Empty
59
+ """Number of queries after a connection is closed and replaced with a new connection.
60
+ """
61
+ max_inactive_connection_lifetime: float | EmptyType = Empty
62
+ """Number of seconds after which inactive connections in the pool will be closed. Pass 0 to disable this mechanism."""
63
+
64
+ setup: Coroutine[None, type[Connection], Any] | EmptyType = Empty
65
+ """A coroutine to prepare a connection right before it is returned from Pool.acquire(). An example use case would be to automatically set up notifications listeners for all connections of a pool."""
66
+ init: Coroutine[None, type[Connection], Any] | EmptyType = Empty
67
+ """A coroutine to prepare a connection right before it is returned from Pool.acquire(). An example use case would be to automatically set up notifications listeners for all connections of a pool."""
68
+
69
+ loop: AbstractEventLoop | EmptyType = Empty
70
+ """An asyncio event loop instance. If None, the default event loop will be used."""
71
+
72
+
73
+ @dataclass
74
+ class AsyncPgConfig(GenericDatabaseConfig):
75
+ """Asyncpg Configuration."""
76
+
77
+ pool_config: AsyncPgPoolConfig | None = None
78
+ """Asyncpg Pool configuration"""
79
+ json_deserializer: Callable[[str], Any] = decode_json
80
+ """For dialects that support the :class:`JSON <sqlalchemy.types.JSON>` datatype, this is a Python callable that will
81
+ convert a JSON string to a Python object. By default, this is set to SQLSpec's
82
+ :attr:`decode_json() <sqlspec._serialization.decode_json>` function."""
83
+ json_serializer: Callable[[Any], str] = encode_json
84
+ """For dialects that support the JSON datatype, this is a Python callable that will render a given object as JSON.
85
+ By default, SQLSpec's :attr:`encode_json() <sqlspec._serialization.encode_json>` is used."""
86
+ pool_instance: Pool | None = None
87
+ """Optional pool to use.
88
+
89
+ If set, the plugin will use the provided pool rather than instantiate one.
90
+ """
91
+
92
+ @property
93
+ def pool_config_dict(self) -> dict[str, Any]:
94
+ """Return the pool configuration as a dict.
95
+
96
+ Returns:
97
+ A string keyed dict of config kwargs for the Asyncpg :func:`create_pool <asyncpg.pool.create_pool>`
98
+ function.
99
+ """
100
+ if self.pool_config:
101
+ return simple_asdict(self.pool_config, exclude_empty=True, convert_nested=False)
102
+ msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
103
+ raise ImproperConfigurationError(msg)
104
+
105
+ async def create_pool(self) -> Pool:
106
+ """Return a pool. If none exists yet, create one.
107
+
108
+ Returns:
109
+ Getter that returns the pool instance used by the plugin.
110
+ """
111
+ if self.pool_instance is not None:
112
+ return self.pool_instance
113
+
114
+ if self.pool_config is None:
115
+ msg = "One of 'pool_config' or 'pool_instance' must be provided."
116
+ raise ImproperConfigurationError(msg)
117
+
118
+ pool_config = self.pool_config_dict
119
+ self.pool_instance = await asyncpg_create_pool(**pool_config)
120
+ if self.pool_instance is None:
121
+ msg = "Could not configure the 'pool_instance'. Please check your configuration."
122
+ raise ImproperConfigurationError(
123
+ msg,
124
+ )
125
+ return self.pool_instance
126
+
127
+ @asynccontextmanager
128
+ async def lifespan(self, *args: Any, **kwargs) -> AsyncGenerator[None, None]:
129
+ db_pool = await self.create_pool()
130
+ try:
131
+ yield
132
+ finally:
133
+ db_pool.terminate()
134
+ await db_pool.close()
135
+
136
+ def provide_pool(self, *args: Any, **kwargs) -> Awaitable[Pool]:
137
+ """Create a pool instance.
138
+
139
+ Returns:
140
+ A Pool instance.
141
+ """
142
+ return self.create_pool()
143
+
144
+ @asynccontextmanager
145
+ async def provide_connection(
146
+ self, *args: Any, **kwargs: Any
147
+ ) -> AsyncGenerator[Connection | PoolConnectionProxy, None]:
148
+ """Create a connection instance.
149
+
150
+ Returns:
151
+ A connection instance.
152
+ """
153
+ db_pool = await self.provide_pool(*args, **kwargs)
154
+ async with db_pool.acquire() as connection:
155
+ yield connection
File without changes