sqlspec 0.3.0__py3-none-any.whl → 0.4.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.

sqlspec/_typing.py CHANGED
@@ -5,15 +5,26 @@ This is used to ensure compatibility when one or more of the libraries are insta
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
+ from enum import Enum
8
9
  from typing import (
9
10
  Any,
10
11
  ClassVar,
12
+ Final,
11
13
  Protocol,
14
+ Union,
12
15
  cast,
13
16
  runtime_checkable,
14
17
  )
15
18
 
16
- from typing_extensions import TypeVar, dataclass_transform
19
+ from typing_extensions import Literal, TypeVar, dataclass_transform
20
+
21
+
22
+ @runtime_checkable
23
+ class DataclassProtocol(Protocol):
24
+ """Protocol for instance checking dataclasses."""
25
+
26
+ __dataclass_fields__: ClassVar[dict[str, Any]]
27
+
17
28
 
18
29
  T = TypeVar("T")
19
30
  T_co = TypeVar("T_co", covariant=True)
@@ -99,11 +110,26 @@ except ImportError:
99
110
  UNSET = UnsetType.UNSET # pyright: ignore[reportConstantRedefinition]
100
111
  MSGSPEC_INSTALLED = False # pyright: ignore[reportConstantRedefinition]
101
112
 
113
+
114
+ class EmptyEnum(Enum):
115
+ """A sentinel enum used as placeholder."""
116
+
117
+ EMPTY = 0
118
+
119
+
120
+ EmptyType = Union[Literal[EmptyEnum.EMPTY], UnsetType]
121
+ Empty: Final = EmptyEnum.EMPTY
122
+
123
+
102
124
  __all__ = (
103
125
  "MSGSPEC_INSTALLED",
104
126
  "PYDANTIC_INSTALLED",
105
127
  "UNSET",
106
128
  "BaseModel",
129
+ "DataclassProtocol",
130
+ "Empty",
131
+ "EmptyEnum",
132
+ "EmptyType",
107
133
  "FailFast",
108
134
  "Struct",
109
135
  "TypeAdapter",
@@ -5,7 +5,7 @@ from dataclasses import dataclass
5
5
  from typing import TYPE_CHECKING, TypeVar
6
6
 
7
7
  from sqlspec.config import GenericDatabaseConfig
8
- from sqlspec.utils.empty import Empty
8
+ from sqlspec.typing import Empty, EmptyType
9
9
 
10
10
  if TYPE_CHECKING:
11
11
  from collections.abc import Generator
@@ -13,8 +13,6 @@ if TYPE_CHECKING:
13
13
 
14
14
  from adbc_driver_manager.dbapi import Connection, Cursor
15
15
 
16
- from sqlspec.utils.empty import EmptyType
17
-
18
16
  __all__ = ("AdbcDatabaseConfig",)
19
17
 
20
18
  ConnectionT = TypeVar("ConnectionT", bound="Connection")
@@ -6,8 +6,7 @@ from typing import TYPE_CHECKING, Any
6
6
 
7
7
  from sqlspec.config import GenericDatabaseConfig
8
8
  from sqlspec.exceptions import ImproperConfigurationError
9
- from sqlspec.utils.dataclass import simple_asdict
10
- from sqlspec.utils.empty import Empty, EmptyType
9
+ from sqlspec.typing import Empty, EmptyType, dataclass_to_dict
11
10
 
12
11
  if TYPE_CHECKING:
13
12
  from collections.abc import AsyncGenerator
@@ -60,7 +59,7 @@ class AiosqliteConfig(GenericDatabaseConfig):
60
59
  Returns:
61
60
  A string keyed dict of config kwargs for the aiosqlite.connect() function.
62
61
  """
63
- return simple_asdict(self, exclude_empty=True, convert_nested=False)
62
+ return dataclass_to_dict(self, exclude_empty=True, convert_nested=False)
64
63
 
65
64
  async def create_connection(self) -> Connection:
66
65
  """Create and return a new database connection.
@@ -79,22 +78,6 @@ class AiosqliteConfig(GenericDatabaseConfig):
79
78
  msg = f"Could not configure the Aiosqlite connection. Error: {e!s}"
80
79
  raise ImproperConfigurationError(msg) from e
81
80
 
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
81
  @asynccontextmanager
99
82
  async def provide_connection(self, *args: Any, **kwargs: Any) -> AsyncGenerator[Connection, None]:
100
83
  """Create and provide a database connection.
@@ -5,8 +5,7 @@ from dataclasses import dataclass
5
5
  from typing import TYPE_CHECKING, TypeVar
6
6
 
7
7
  from sqlspec.exceptions import ImproperConfigurationError
8
- from sqlspec.utils.dataclass import simple_asdict
9
- from sqlspec.utils.empty import Empty, EmptyType
8
+ from sqlspec.typing import Empty, EmptyType, dataclass_to_dict
10
9
 
11
10
  if TYPE_CHECKING:
12
11
  from collections.abc import AsyncGenerator
@@ -101,7 +100,7 @@ class AsyncmyPoolConfig:
101
100
  Returns:
102
101
  A string keyed dict of config kwargs for the Asyncmy create_pool function.
103
102
  """
104
- return simple_asdict(self, exclude_empty=True, convert_nested=False)
103
+ return dataclass_to_dict(self, exclude_empty=True, convert_nested=False)
105
104
 
106
105
 
107
106
  @dataclass
@@ -125,7 +124,7 @@ class AsyncMyConfig:
125
124
  A string keyed dict of config kwargs for the Asyncmy create_pool function.
126
125
  """
127
126
  if self.pool_config:
128
- return simple_asdict(self.pool_config, exclude_empty=True, convert_nested=False)
127
+ return dataclass_to_dict(self.pool_config, exclude_empty=True, convert_nested=False)
129
128
  msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
130
129
  raise ImproperConfigurationError(msg)
131
130
 
@@ -162,23 +161,6 @@ class AsyncMyConfig:
162
161
  """
163
162
  return await self.create_pool()
164
163
 
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
164
  @asynccontextmanager
183
165
  async def provide_connection(self, *args: Any, **kwargs: Any) -> AsyncGenerator[Connection, None]:
184
166
  """Create and provide a database connection.
@@ -10,8 +10,7 @@ from asyncpg import create_pool as asyncpg_create_pool
10
10
  from sqlspec._serialization import decode_json, encode_json
11
11
  from sqlspec.config import GenericDatabaseConfig, GenericPoolConfig
12
12
  from sqlspec.exceptions import ImproperConfigurationError
13
- from sqlspec.utils.dataclass import simple_asdict
14
- from sqlspec.utils.empty import Empty, EmptyType
13
+ from sqlspec.typing import Empty, EmptyType, dataclass_to_dict
15
14
 
16
15
  if TYPE_CHECKING:
17
16
  from asyncio import AbstractEventLoop
@@ -98,7 +97,7 @@ class AsyncPgConfig(GenericDatabaseConfig):
98
97
  function.
99
98
  """
100
99
  if self.pool_config:
101
- return simple_asdict(self.pool_config, exclude_empty=True, convert_nested=False)
100
+ return dataclass_to_dict(self.pool_config, exclude_empty=True, convert_nested=False)
102
101
  msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
103
102
  raise ImproperConfigurationError(msg)
104
103
 
@@ -124,16 +123,7 @@ class AsyncPgConfig(GenericDatabaseConfig):
124
123
  )
125
124
  return self.pool_instance
126
125
 
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]:
126
+ def provide_pool(self, *args: Any, **kwargs: Any) -> Awaitable[Pool]:
137
127
  """Create a pool instance.
138
128
 
139
129
  Returns:
@@ -2,19 +2,66 @@ from __future__ import annotations
2
2
 
3
3
  from contextlib import contextmanager
4
4
  from dataclasses import dataclass
5
- from typing import TYPE_CHECKING, Any
5
+ from typing import TYPE_CHECKING, Any, cast
6
6
 
7
7
  from sqlspec.config import GenericDatabaseConfig
8
8
  from sqlspec.exceptions import ImproperConfigurationError
9
- from sqlspec.utils.dataclass import simple_asdict
10
- from sqlspec.utils.empty import Empty, EmptyType
9
+ from sqlspec.typing import Empty, EmptyType, dataclass_to_dict
11
10
 
12
11
  if TYPE_CHECKING:
13
- from collections.abc import Generator
12
+ from collections.abc import Generator, Sequence
14
13
 
15
14
  from duckdb import DuckDBPyConnection
16
15
 
17
- __all__ = ("DuckDBConfig",)
16
+ __all__ = ("DuckDBConfig", "ExtensionConfig")
17
+
18
+
19
+ @dataclass
20
+ class ExtensionConfig:
21
+ """Configuration for a DuckDB extension.
22
+
23
+ This class provides configuration options for DuckDB extensions, including installation
24
+ and post-install configuration settings.
25
+
26
+ Args:
27
+ name: The name of the extension to install
28
+ config: Optional configuration settings to apply after installation
29
+ force_install: Whether to force reinstall if already present
30
+ repository: Optional repository name to install from
31
+ repository_url: Optional repository URL to install from
32
+ version: Optional version of the extension to install
33
+ """
34
+
35
+ name: str
36
+ config: dict[str, Any] | None = None
37
+ force_install: bool = False
38
+ repository: str | None = None
39
+ repository_url: str | None = None
40
+ version: str | None = None
41
+
42
+ @classmethod
43
+ def from_dict(cls, name: str, config: dict[str, Any] | bool | None = None) -> ExtensionConfig:
44
+ """Create an ExtensionConfig from a configuration dictionary.
45
+
46
+ Args:
47
+ name: The name of the extension
48
+ config: Configuration dictionary that may contain settings
49
+
50
+ Returns:
51
+ A new ExtensionConfig instance
52
+ """
53
+ if config is None:
54
+ return cls(name=name)
55
+
56
+ if not isinstance(config, dict):
57
+ config = {"force_install": bool(config)}
58
+
59
+ install_args = {
60
+ key: config.pop(key)
61
+ for key in ["force_install", "repository", "repository_url", "version", "config", "name"]
62
+ if key in config
63
+ }
64
+ return cls(name=name, **install_args)
18
65
 
19
66
 
20
67
  @dataclass
@@ -39,6 +86,73 @@ class DuckDBConfig(GenericDatabaseConfig):
39
86
  For details see: https://duckdb.org/docs/api/python/overview#connection-options
40
87
  """
41
88
 
89
+ extensions: Sequence[ExtensionConfig] | EmptyType = Empty
90
+ """A sequence of extension configurations to install and configure upon connection creation."""
91
+
92
+ def __post_init__(self) -> None:
93
+ """Post-initialization validation and processing.
94
+
95
+ This method handles merging extension configurations from both the extensions field
96
+ and the config dictionary, if present. The config['extensions'] field can be either:
97
+ - A dictionary mapping extension names to their configurations
98
+ - A list of extension names (which will be installed with force_install=True)
99
+
100
+ Raises:
101
+ ImproperConfigurationError: If there are duplicate extension configurations.
102
+ """
103
+ if self.config is Empty:
104
+ self.config = {}
105
+
106
+ if self.extensions is Empty:
107
+ self.extensions = []
108
+ # this is purely for mypy
109
+ assert isinstance(self.config, dict) # noqa: S101
110
+ assert isinstance(self.extensions, list) # noqa: S101
111
+
112
+ _e = self.config.pop("extensions", {})
113
+ if not isinstance(_e, (dict, list, tuple)):
114
+ msg = "When configuring extensions in the 'config' dictionary, the value must be a dictionary or sequence of extension names"
115
+ raise ImproperConfigurationError(msg)
116
+ if not isinstance(_e, dict):
117
+ _e = {str(ext): {"force_install": False} for ext in _e}
118
+
119
+ if len(set(_e.keys()).intersection({ext.name for ext in self.extensions})) > 0:
120
+ msg = "Configuring the same extension in both 'extensions' and as a key in 'config['extensions']' is not allowed"
121
+ raise ImproperConfigurationError(msg)
122
+
123
+ self.extensions.extend([ExtensionConfig.from_dict(name, ext_config) for name, ext_config in _e.items()])
124
+
125
+ def _configure_extensions(self, connection: DuckDBPyConnection) -> None:
126
+ """Configure extensions for the connection.
127
+
128
+ Args:
129
+ connection: The DuckDB connection to configure extensions for.
130
+
131
+ Raises:
132
+ ImproperConfigurationError: If extension installation or configuration fails.
133
+ """
134
+ if self.extensions is Empty:
135
+ return
136
+
137
+ for extension in cast("list[ExtensionConfig]", self.extensions):
138
+ try:
139
+ if extension.force_install:
140
+ connection.install_extension(
141
+ extension=extension.name,
142
+ force_install=extension.force_install,
143
+ repository=extension.repository,
144
+ repository_url=extension.repository_url,
145
+ version=extension.version,
146
+ )
147
+ connection.load_extension(extension.name)
148
+
149
+ if extension.config:
150
+ for key, value in extension.config.items():
151
+ connection.execute(f"SET {key}={value}")
152
+ except Exception as e:
153
+ msg = f"Failed to configure extension {extension.name}. Error: {e!s}"
154
+ raise ImproperConfigurationError(msg) from e
155
+
42
156
  @property
43
157
  def connection_config_dict(self) -> dict[str, Any]:
44
158
  """Return the connection configuration as a dict.
@@ -46,44 +160,30 @@ class DuckDBConfig(GenericDatabaseConfig):
46
160
  Returns:
47
161
  A string keyed dict of config kwargs for the duckdb.connect() function.
48
162
  """
49
- config = simple_asdict(self, exclude_empty=True, convert_nested=False)
163
+ config = dataclass_to_dict(self, exclude_empty=True, exclude={"extensions"}, convert_nested=False)
50
164
  if not config.get("database"):
51
165
  config["database"] = ":memory:"
52
166
  return config
53
167
 
54
168
  def create_connection(self) -> DuckDBPyConnection:
55
- """Create and return a new database connection.
169
+ """Create and return a new database connection with configured extensions.
56
170
 
57
171
  Returns:
58
- A new DuckDB connection instance.
172
+ A new DuckDB connection instance with extensions installed and configured.
59
173
 
60
174
  Raises:
61
- ImproperConfigurationError: If the connection could not be established.
175
+ ImproperConfigurationError: If the connection could not be established or extensions could not be configured.
62
176
  """
63
177
  import duckdb
64
178
 
65
179
  try:
66
- return duckdb.connect(**self.connection_config_dict)
180
+ connection = duckdb.connect(**self.connection_config_dict)
181
+ self._configure_extensions(connection)
182
+ return connection
67
183
  except Exception as e:
68
184
  msg = f"Could not configure the DuckDB connection. Error: {e!s}"
69
185
  raise ImproperConfigurationError(msg) from e
70
186
 
71
- @contextmanager
72
- def lifespan(self, *args: Any, **kwargs: Any) -> Generator[None, None, None]:
73
- """Manage the lifecycle of a database connection.
74
-
75
- Yields:
76
- None
77
-
78
- Raises:
79
- ImproperConfigurationError: If the connection could not be established.
80
- """
81
- connection = self.create_connection()
82
- try:
83
- yield
84
- finally:
85
- connection.close()
86
-
87
187
  @contextmanager
88
188
  def provide_connection(self, *args: Any, **kwargs: Any) -> Generator[DuckDBPyConnection, None, None]:
89
189
  """Create and provide a database connection.
@@ -13,7 +13,7 @@ from sqlspec.adapters.oracledb.config._common import (
13
13
  OracleGenericPoolConfig,
14
14
  )
15
15
  from sqlspec.exceptions import ImproperConfigurationError
16
- from sqlspec.utils.dataclass import simple_asdict
16
+ from sqlspec.typing import dataclass_to_dict
17
17
 
18
18
  if TYPE_CHECKING:
19
19
  from collections.abc import AsyncGenerator, Awaitable
@@ -36,6 +36,11 @@ class OracleAsyncDatabaseConfig(OracleGenericDatabaseConfig[AsyncConnectionPool,
36
36
 
37
37
  pool_config: OracleAsyncPoolConfig | None = None
38
38
  """Oracle Pool configuration"""
39
+ pool_instance: AsyncConnectionPool | None = None
40
+ """Optional pool to use.
41
+
42
+ If set, the plugin will use the provided pool rather than instantiate one.
43
+ """
39
44
 
40
45
  @property
41
46
  def pool_config_dict(self) -> dict[str, Any]:
@@ -46,7 +51,7 @@ class OracleAsyncDatabaseConfig(OracleGenericDatabaseConfig[AsyncConnectionPool,
46
51
  function.
47
52
  """
48
53
  if self.pool_config is not None:
49
- return simple_asdict(self.pool_config, exclude_empty=True, convert_nested=False)
54
+ return dataclass_to_dict(self.pool_config, exclude_empty=True, convert_nested=False)
50
55
  msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
51
56
  raise ImproperConfigurationError(msg)
52
57
 
@@ -70,15 +75,7 @@ class OracleAsyncDatabaseConfig(OracleGenericDatabaseConfig[AsyncConnectionPool,
70
75
  raise ImproperConfigurationError(msg)
71
76
  return self.pool_instance
72
77
 
73
- @asynccontextmanager
74
- async def lifespan(self, *args: Any, **kwargs) -> AsyncGenerator[None, None]:
75
- db_pool = await self.create_pool()
76
- try:
77
- yield
78
- finally:
79
- await db_pool.close(force=True)
80
-
81
- def provide_pool(self, *args: Any, **kwargs) -> Awaitable[AsyncConnectionPool]:
78
+ def provide_pool(self, *args: Any, **kwargs: Any) -> Awaitable[AsyncConnectionPool]:
82
79
  """Create a pool instance.
83
80
 
84
81
  Returns:
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Generic, TypeVar
6
6
  from oracledb import ConnectionPool
7
7
 
8
8
  from sqlspec.config import GenericDatabaseConfig, GenericPoolConfig
9
- from sqlspec.utils.empty import Empty
9
+ from sqlspec.typing import Empty
10
10
 
11
11
  if TYPE_CHECKING:
12
12
  import ssl
@@ -17,7 +17,7 @@ if TYPE_CHECKING:
17
17
  from oracledb.connection import AsyncConnection, Connection
18
18
  from oracledb.pool import AsyncConnectionPool, ConnectionPool
19
19
 
20
- from sqlspec.utils.empty import EmptyType
20
+ from sqlspec.typing import EmptyType
21
21
 
22
22
  __all__ = (
23
23
  "OracleGenericDatabaseConfig",
@@ -13,7 +13,7 @@ from sqlspec.adapters.oracledb.config._common import (
13
13
  OracleGenericPoolConfig,
14
14
  )
15
15
  from sqlspec.exceptions import ImproperConfigurationError
16
- from sqlspec.utils.dataclass import simple_asdict
16
+ from sqlspec.typing import dataclass_to_dict
17
17
 
18
18
  if TYPE_CHECKING:
19
19
  from collections.abc import Generator
@@ -51,7 +51,7 @@ class OracleSyncDatabaseConfig(OracleGenericDatabaseConfig[ConnectionPool, Conne
51
51
  function.
52
52
  """
53
53
  if self.pool_config:
54
- return simple_asdict(self.pool_config, exclude_empty=True, convert_nested=False)
54
+ return dataclass_to_dict(self.pool_config, exclude_empty=True, convert_nested=False)
55
55
  msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
56
56
  raise ImproperConfigurationError(msg)
57
57
 
@@ -75,13 +75,6 @@ class OracleSyncDatabaseConfig(OracleGenericDatabaseConfig[ConnectionPool, Conne
75
75
  raise ImproperConfigurationError(msg)
76
76
  return self.pool_instance
77
77
 
78
- def lifespan(self, *args: Any, **kwargs: Any) -> Generator[None, None, None]:
79
- db_pool = self.create_pool()
80
- try:
81
- yield
82
- finally:
83
- db_pool.close()
84
-
85
78
  def provide_pool(self, *args: Any, **kwargs: Any) -> ConnectionPool:
86
79
  """Create a pool instance.
87
80
 
@@ -12,7 +12,7 @@ from sqlspec.adapters.psycopg.config._common import (
12
12
  PsycoPgGenericPoolConfig,
13
13
  )
14
14
  from sqlspec.exceptions import ImproperConfigurationError
15
- from sqlspec.utils.dataclass import simple_asdict
15
+ from sqlspec.typing import dataclass_to_dict
16
16
 
17
17
  if TYPE_CHECKING:
18
18
  from collections.abc import AsyncGenerator, Awaitable
@@ -43,7 +43,7 @@ class PsycoPgAsyncDatabaseConfig(PsycoPgGenericDatabaseConfig[AsyncConnectionPoo
43
43
  def pool_config_dict(self) -> dict[str, Any]:
44
44
  """Return the pool configuration as a dict."""
45
45
  if self.pool_config:
46
- return simple_asdict(self.pool_config, exclude_empty=True, convert_nested=False)
46
+ return dataclass_to_dict(self.pool_config, exclude_empty=True, convert_nested=False)
47
47
  msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
48
48
  raise ImproperConfigurationError(msg)
49
49
 
@@ -63,15 +63,6 @@ class PsycoPgAsyncDatabaseConfig(PsycoPgGenericDatabaseConfig[AsyncConnectionPoo
63
63
  raise ImproperConfigurationError(msg)
64
64
  return self.pool_instance
65
65
 
66
- @asynccontextmanager
67
- async def lifespan(self, *args: Any, **kwargs: Any) -> AsyncGenerator[None, None]:
68
- """Manage the lifecycle of the connection pool."""
69
- pool = await self.create_pool()
70
- try:
71
- yield
72
- finally:
73
- await pool.close()
74
-
75
66
  def provide_pool(self, *args: Any, **kwargs: Any) -> Awaitable[AsyncConnectionPool]:
76
67
  """Create and return a connection pool."""
77
68
  return self.create_pool()
@@ -4,7 +4,7 @@ from dataclasses import dataclass
4
4
  from typing import TYPE_CHECKING, Generic, TypeVar
5
5
 
6
6
  from sqlspec.config import GenericDatabaseConfig, GenericPoolConfig
7
- from sqlspec.utils.empty import Empty
7
+ from sqlspec.typing import Empty
8
8
 
9
9
  if TYPE_CHECKING:
10
10
  from collections.abc import Callable
@@ -13,7 +13,8 @@ if TYPE_CHECKING:
13
13
  from psycopg import AsyncConnection, Connection
14
14
  from psycopg_pool import AsyncConnectionPool, ConnectionPool
15
15
 
16
- from sqlspec.utils.empty import EmptyType
16
+ from sqlspec.typing import EmptyType
17
+
17
18
 
18
19
  __all__ = (
19
20
  "PsycoPgGenericDatabaseConfig",
@@ -12,7 +12,7 @@ from sqlspec.adapters.psycopg.config._common import (
12
12
  PsycoPgGenericPoolConfig,
13
13
  )
14
14
  from sqlspec.exceptions import ImproperConfigurationError
15
- from sqlspec.utils.dataclass import simple_asdict
15
+ from sqlspec.typing import dataclass_to_dict
16
16
 
17
17
  if TYPE_CHECKING:
18
18
  from collections.abc import Generator
@@ -43,7 +43,7 @@ class PsycoPgSyncDatabaseConfig(PsycoPgGenericDatabaseConfig[ConnectionPool, Con
43
43
  def pool_config_dict(self) -> dict[str, Any]:
44
44
  """Return the pool configuration as a dict."""
45
45
  if self.pool_config:
46
- return simple_asdict(self.pool_config, exclude_empty=True, convert_nested=False)
46
+ return dataclass_to_dict(self.pool_config, exclude_empty=True, convert_nested=False)
47
47
  msg = "'pool_config' methods can not be used when a 'pool_instance' is provided."
48
48
  raise ImproperConfigurationError(msg)
49
49
 
@@ -63,15 +63,6 @@ class PsycoPgSyncDatabaseConfig(PsycoPgGenericDatabaseConfig[ConnectionPool, Con
63
63
  raise ImproperConfigurationError(msg)
64
64
  return self.pool_instance
65
65
 
66
- @contextmanager
67
- def lifespan(self, *args: Any, **kwargs: Any) -> Generator[None, None, None]:
68
- """Manage the lifecycle of the connection pool."""
69
- pool = self.create_pool()
70
- try:
71
- yield
72
- finally:
73
- pool.close()
74
-
75
66
  def provide_pool(self, *args: Any, **kwargs: Any) -> ConnectionPool:
76
67
  """Create and return a connection pool."""
77
68
  return self.create_pool()
@@ -6,8 +6,7 @@ from typing import TYPE_CHECKING, Any, Literal
6
6
 
7
7
  from sqlspec.config import GenericDatabaseConfig
8
8
  from sqlspec.exceptions import ImproperConfigurationError
9
- from sqlspec.utils.dataclass import simple_asdict
10
- from sqlspec.utils.empty import Empty, EmptyType
9
+ from sqlspec.typing import Empty, EmptyType, dataclass_to_dict
11
10
 
12
11
  if TYPE_CHECKING:
13
12
  from collections.abc import Generator
@@ -57,7 +56,7 @@ class SqliteConfig(GenericDatabaseConfig):
57
56
  Returns:
58
57
  A string keyed dict of config kwargs for the sqlite3.connect() function.
59
58
  """
60
- return simple_asdict(self, exclude_empty=True, convert_nested=False)
59
+ return dataclass_to_dict(self, exclude_empty=True, convert_nested=False)
61
60
 
62
61
  def create_connection(self) -> Connection:
63
62
  """Create and return a new database connection.
@@ -71,27 +70,11 @@ class SqliteConfig(GenericDatabaseConfig):
71
70
  import sqlite3
72
71
 
73
72
  try:
74
- return sqlite3.connect(**self.connection_config_dict)
73
+ return sqlite3.connect(**self.connection_config_dict) # type: ignore[no-any-return,unused-ignore]
75
74
  except Exception as e:
76
75
  msg = f"Could not configure the SQLite connection. Error: {e!s}"
77
76
  raise ImproperConfigurationError(msg) from e
78
77
 
79
- @contextmanager
80
- def lifespan(self, *args: Any, **kwargs: Any) -> Generator[None, None, None]:
81
- """Manage the lifecycle of a database connection.
82
-
83
- Yields:
84
- None
85
-
86
- Raises:
87
- ImproperConfigurationError: If the connection could not be established.
88
- """
89
- connection = self.create_connection()
90
- try:
91
- yield
92
- finally:
93
- connection.close()
94
-
95
78
  @contextmanager
96
79
  def provide_connection(self, *args: Any, **kwargs: Any) -> Generator[Connection, None, None]:
97
80
  """Create and provide a database connection.
sqlspec/filters.py CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from abc import ABC, abstractmethod
5
+ from abc import ABC
6
6
  from collections import abc # noqa: TC003
7
7
  from dataclasses import dataclass
8
8
  from datetime import datetime # noqa: TC003
9
- from typing import Generic, Literal
9
+ from typing import Generic, Literal, Protocol
10
10
 
11
11
  from typing_extensions import TypeVar
12
12
 
@@ -27,9 +27,11 @@ __all__ = (
27
27
  T = TypeVar("T")
28
28
 
29
29
 
30
- class StatementFilter(ABC):
31
- @abstractmethod
30
+ class StatementFilter(Protocol):
31
+ """Protocol for filters that can be appended to a statement."""
32
+
32
33
  def append_to_statement(self, statement: str) -> str:
34
+ """Append the filter to the statement."""
33
35
  return statement
34
36
 
35
37
 
sqlspec/typing.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Sequence
4
+ from dataclasses import Field, fields
4
5
  from functools import lru_cache
5
6
  from typing import (
6
7
  TYPE_CHECKING,
@@ -18,15 +19,22 @@ from sqlspec._typing import (
18
19
  PYDANTIC_INSTALLED,
19
20
  UNSET,
20
21
  BaseModel,
22
+ DataclassProtocol,
23
+ Empty,
24
+ EmptyType,
21
25
  FailFast,
22
26
  Struct,
23
27
  TypeAdapter,
28
+ UnsetType,
24
29
  convert,
25
30
  )
26
- from sqlspec.utils.dataclass import DataclassProtocol, is_dataclass_instance, simple_asdict
27
31
 
28
32
  if TYPE_CHECKING:
29
- from .filters import StatementFilter
33
+ from collections.abc import Iterable
34
+ from collections.abc import Set as AbstractSet
35
+
36
+ from sqlspec.filters import StatementFilter
37
+
30
38
 
31
39
  PYDANTIC_USE_FAILFAST = False # leave permanently disabled for now
32
40
 
@@ -55,6 +63,18 @@ A list or sequence of any of the following:
55
63
  """
56
64
 
57
65
 
66
+ def is_dataclass_instance(obj: Any) -> TypeGuard[DataclassProtocol]:
67
+ """Check if an object is a dataclass instance.
68
+
69
+ Args:
70
+ obj: An object to check.
71
+
72
+ Returns:
73
+ True if the object is a dataclass instance.
74
+ """
75
+ return hasattr(type(obj), "__dataclass_fields__")
76
+
77
+
58
78
  @lru_cache(typed=True)
59
79
  def get_type_adapter(f: type[T]) -> TypeAdapter[T]:
60
80
  """Caches and returns a pydantic type adapter.
@@ -224,6 +244,104 @@ def is_msgspec_model_without_field(v: Any, field_name: str) -> TypeGuard[Struct]
224
244
  return not is_msgspec_model_with_field(v, field_name)
225
245
 
226
246
 
247
+ def extract_dataclass_fields(
248
+ dt: DataclassProtocol,
249
+ exclude_none: bool = False,
250
+ exclude_empty: bool = False,
251
+ include: AbstractSet[str] | None = None,
252
+ exclude: AbstractSet[str] | None = None,
253
+ ) -> tuple[Field[Any], ...]:
254
+ """Extract dataclass fields.
255
+
256
+ Args:
257
+ dt: A dataclass instance.
258
+ exclude_none: Whether to exclude None values.
259
+ exclude_empty: Whether to exclude Empty values.
260
+ include: An iterable of fields to include.
261
+ exclude: An iterable of fields to exclude.
262
+
263
+
264
+ Returns:
265
+ A tuple of dataclass fields.
266
+ """
267
+ include = include or set()
268
+ exclude = exclude or set()
269
+
270
+ if common := (include & exclude):
271
+ msg = f"Fields {common} are both included and excluded."
272
+ raise ValueError(msg)
273
+
274
+ dataclass_fields: Iterable[Field[Any]] = fields(dt)
275
+ if exclude_none:
276
+ dataclass_fields = (field for field in dataclass_fields if getattr(dt, field.name) is not None)
277
+ if exclude_empty:
278
+ dataclass_fields = (field for field in dataclass_fields if getattr(dt, field.name) is not Empty)
279
+ if include:
280
+ dataclass_fields = (field for field in dataclass_fields if field.name in include)
281
+ if exclude:
282
+ dataclass_fields = (field for field in dataclass_fields if field.name not in exclude)
283
+
284
+ return tuple(dataclass_fields)
285
+
286
+
287
+ def extract_dataclass_items(
288
+ dt: DataclassProtocol,
289
+ exclude_none: bool = False,
290
+ exclude_empty: bool = False,
291
+ include: AbstractSet[str] | None = None,
292
+ exclude: AbstractSet[str] | None = None,
293
+ ) -> tuple[tuple[str, Any], ...]:
294
+ """Extract dataclass name, value pairs.
295
+
296
+ Unlike the 'asdict' method exports by the stdlib, this function does not pickle values.
297
+
298
+ Args:
299
+ dt: A dataclass instance.
300
+ exclude_none: Whether to exclude None values.
301
+ exclude_empty: Whether to exclude Empty values.
302
+ include: An iterable of fields to include.
303
+ exclude: An iterable of fields to exclude.
304
+
305
+ Returns:
306
+ A tuple of key/value pairs.
307
+ """
308
+ dataclass_fields = extract_dataclass_fields(dt, exclude_none, exclude_empty, include, exclude)
309
+ return tuple((field.name, getattr(dt, field.name)) for field in dataclass_fields)
310
+
311
+
312
+ def dataclass_to_dict(
313
+ obj: DataclassProtocol,
314
+ exclude_none: bool = False,
315
+ exclude_empty: bool = False,
316
+ convert_nested: bool = True,
317
+ exclude: set[str] | None = None,
318
+ ) -> dict[str, Any]:
319
+ """Convert a dataclass to a dictionary.
320
+
321
+ This method has important differences to the standard library version:
322
+ - it does not deepcopy values
323
+ - it does not recurse into collections
324
+
325
+ Args:
326
+ obj: A dataclass instance.
327
+ exclude_none: Whether to exclude None values.
328
+ exclude_empty: Whether to exclude Empty values.
329
+ convert_nested: Whether to recursively convert nested dataclasses.
330
+ exclude: An iterable of fields to exclude.
331
+
332
+ Returns:
333
+ A dictionary of key/value pairs.
334
+ """
335
+ ret = {}
336
+ for field in extract_dataclass_fields(obj, exclude_none, exclude_empty, exclude=exclude):
337
+ value = getattr(obj, field.name)
338
+ if is_dataclass_instance(value) and convert_nested:
339
+ ret[field.name] = dataclass_to_dict(value, exclude_none, exclude_empty)
340
+ else:
341
+ ret[field.name] = getattr(obj, field.name)
342
+ return ret
343
+
344
+
227
345
  def schema_dump(
228
346
  data: dict[str, Any] | Struct | BaseModel | DataclassProtocol,
229
347
  exclude_unset: bool = True,
@@ -238,7 +356,7 @@ def schema_dump(
238
356
  :type: dict[str, Any]
239
357
  """
240
358
  if is_dataclass(data):
241
- return simple_asdict(data, exclude_empty=exclude_unset)
359
+ return dataclass_to_dict(data, exclude_empty=exclude_unset)
242
360
  if is_pydantic_model(data):
243
361
  return data.model_dump(exclude_unset=exclude_unset)
244
362
  if is_msgspec_model(data) and exclude_unset:
@@ -254,6 +372,9 @@ __all__ = (
254
372
  "PYDANTIC_USE_FAILFAST",
255
373
  "UNSET",
256
374
  "BaseModel",
375
+ "DataclassProtocol",
376
+ "Empty",
377
+ "EmptyType",
257
378
  "FailFast",
258
379
  "FilterTypeT",
259
380
  "ModelDictListT",
@@ -262,7 +383,14 @@ __all__ = (
262
383
  "TypeAdapter",
263
384
  "UnsetType",
264
385
  "convert",
386
+ "dataclass_to_dict",
387
+ "extract_dataclass_fields",
388
+ "extract_dataclass_items",
265
389
  "get_type_adapter",
390
+ "is_dataclass",
391
+ "is_dataclass_instance",
392
+ "is_dataclass_with_field",
393
+ "is_dataclass_without_field",
266
394
  "is_dict",
267
395
  "is_dict_with_field",
268
396
  "is_dict_without_field",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sqlspec
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: SQL Experiments in Python
5
5
  Author-email: Cody Fincher <cody@litestar.dev>
6
6
  Maintainer-email: Litestar Developers <hello@litestar.dev>
@@ -0,0 +1,39 @@
1
+ sqlspec/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
2
+ sqlspec/__metadata__.py,sha256=Vw99abV_UQNVH2jB0IBa9-8emyZQcXm1J9eMtLxFX2Y,496
3
+ sqlspec/_serialization.py,sha256=OL4x0Rz5UjF7RgAKqhYChi5qSS_ImtaVrIlA4DhIKUE,824
4
+ sqlspec/_typing.py,sha256=Za41GlNdEKAGTy66GTofOiGYROE7ccFMvxZdh_vxIFk,3278
5
+ sqlspec/config.py,sha256=BOX_V_q2MOP33tK0ISpYaiQJt3zrvK4D_JIBD9FOixY,272
6
+ sqlspec/exceptions.py,sha256=fhCOILBj0J7HJP67BNSC0d9YUbW8QpZPXM55xJJzE8A,3039
7
+ sqlspec/filters.py,sha256=H5UAn1HKm598QqDujQHwvytKHjw4QoQ2zPpgXMcYpSU,3552
8
+ sqlspec/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ sqlspec/typing.py,sha256=t5fpzxUCWkKJIpFDKmUeq77RkdmNNFqMj4azgJpRS7k,11316
10
+ sqlspec/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ sqlspec/adapters/adbc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ sqlspec/adapters/adbc/config.py,sha256=BeiZdqoBQXuht0GXWleqVKL_LOFI3Ygna1XWDmb-dBw,1704
13
+ sqlspec/adapters/aiosqlite/__init__.py,sha256=PLqWg24l3TooJvqA0Xf1WErrxtqwo8DEoL_Zp2iSCzs,68
14
+ sqlspec/adapters/aiosqlite/config.py,sha256=5DOKaYT_lBFTimrITJwIqTHoweZ2aZlQ07kti1VJtxk,3821
15
+ sqlspec/adapters/asyncmy/__init__.py,sha256=o0R_Azae3FHiSZ1TQ5ZjyCneDOuvnEeMjmSkhuiKoWo,103
16
+ sqlspec/adapters/asyncmy/config.py,sha256=7VsNEokhBtBgXDj2gakdwplOYPaV3ADIwWX-o22vwNk,5461
17
+ sqlspec/adapters/asyncpg/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ sqlspec/adapters/asyncpg/config.py,sha256=NyVvsT2x3IIZBZF6dTaSv1w6lCPEvmUprYXyTSJ9Ixk,6014
19
+ sqlspec/adapters/duckdb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ sqlspec/adapters/duckdb/config.py,sha256=BgGGkk0Y3CQboQeI1aB81_ABVGmgOL1cbk_cZKzeggQ,7927
21
+ sqlspec/adapters/oracledb/__init__.py,sha256=fFQ2xOxFcgpr-ug4AVv430irnJgBRUINvt4sL3qzyBw,275
22
+ sqlspec/adapters/oracledb/config/__init__.py,sha256=XoHgInT4IbXjDg5ax3ncuUoVvnYB5qQjI-Ib7gwSycU,338
23
+ sqlspec/adapters/oracledb/config/_asyncio.py,sha256=Z87V7t-5blS6NkMhvFjoBgUrJtKgOrTyKSl8xy2uD_M,3211
24
+ sqlspec/adapters/oracledb/config/_common.py,sha256=k8ou3aWR1En1jl5uo4fORMG3CoF-XQ96qdwULtteHLo,6173
25
+ sqlspec/adapters/oracledb/config/_sync.py,sha256=guCrotTdNJajcbG5xwVCLoza7wLPQxZkQaw99s4ibNE,3071
26
+ sqlspec/adapters/psycopg/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
+ sqlspec/adapters/psycopg/config/__init__.py,sha256=pXI9Pa2VYESTchPgM3tt5kFF8tsmgq-ksZRGR6pgiUQ,280
28
+ sqlspec/adapters/psycopg/config/_async.py,sha256=sNQU3dzencGgOToHZuMfxoqhR9EvRyrxdWqtPDC2gUY,2712
29
+ sqlspec/adapters/psycopg/config/_common.py,sha256=gC-QQDy9DNjp0DGZAT3wu7MKW8ejHe5fuHU-318Vgr0,2757
30
+ sqlspec/adapters/psycopg/config/_sync.py,sha256=lwzaeO6I-nYuC7vif-bjn2jLugPwbPjTW226_hcweqo,2590
31
+ sqlspec/adapters/sqlite/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
+ sqlspec/adapters/sqlite/config.py,sha256=rCv6XWWWi1dzpmAVnwHxg-ahmAif78yfFhA548fNaT8,3697
33
+ sqlspec/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
+ sqlspec/extensions/litestar/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
+ sqlspec/extensions/litestar/plugin.py,sha256=oiBFfRffNvy_vnGptREd6JYZGB6Yd98KbtVct_VcW0A,837
36
+ sqlspec-0.4.0.dist-info/METADATA,sha256=OnTkY_OG3IeqaJQBkzHjsKuQwkfmhQO4WnhXKbEoDLc,3222
37
+ sqlspec-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
38
+ sqlspec-0.4.0.dist-info/licenses/NOTICE,sha256=Lyir8ozXWov7CyYS4huVaOCNrtgL17P-bNV-5daLntQ,1634
39
+ sqlspec-0.4.0.dist-info/RECORD,,
sqlspec/utils/__init__.py DELETED
File without changes
@@ -1,138 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from dataclasses import Field, fields
4
- from typing import TYPE_CHECKING, ClassVar, Protocol, runtime_checkable
5
-
6
- from typing_extensions import TypeGuard
7
-
8
- from sqlspec.utils.empty import Empty
9
-
10
- if TYPE_CHECKING:
11
- from collections.abc import Iterable
12
- from collections.abc import Set as AbstractSet
13
- from typing import Any
14
-
15
-
16
- __all__ = (
17
- "extract_dataclass_fields",
18
- "extract_dataclass_items",
19
- "is_dataclass_instance",
20
- "simple_asdict",
21
- )
22
-
23
-
24
- @runtime_checkable
25
- class DataclassProtocol(Protocol):
26
- """Protocol for instance checking dataclasses"""
27
-
28
- __dataclass_fields__: ClassVar[dict[str, Any]]
29
-
30
-
31
- def is_dataclass_instance(obj: Any) -> TypeGuard[DataclassProtocol]:
32
- """Check if an object is a dataclass instance.
33
-
34
- Args:
35
- obj: An object to check.
36
-
37
- Returns:
38
- True if the object is a dataclass instance.
39
- """
40
- return hasattr(type(obj), "__dataclass_fields__")
41
-
42
-
43
- def extract_dataclass_fields(
44
- dt: DataclassProtocol,
45
- exclude_none: bool = False,
46
- exclude_empty: bool = False,
47
- include: AbstractSet[str] | None = None,
48
- exclude: AbstractSet[str] | None = None,
49
- ) -> tuple[Field[Any], ...]:
50
- """Extract dataclass fields.
51
-
52
- Args:
53
- dt: A dataclass instance.
54
- exclude_none: Whether to exclude None values.
55
- exclude_empty: Whether to exclude Empty values.
56
- include: An iterable of fields to include.
57
- exclude: An iterable of fields to exclude.
58
-
59
-
60
- Returns:
61
- A tuple of dataclass fields.
62
- """
63
- include = include or set()
64
- exclude = exclude or set()
65
-
66
- if common := (include & exclude):
67
- msg = f"Fields {common} are both included and excluded."
68
- raise ValueError(msg)
69
-
70
- dataclass_fields: Iterable[Field[Any]] = fields(dt)
71
- if exclude_none:
72
- dataclass_fields = (field for field in dataclass_fields if getattr(dt, field.name) is not None)
73
- if exclude_empty:
74
- dataclass_fields = (field for field in dataclass_fields if getattr(dt, field.name) is not Empty)
75
- if include:
76
- dataclass_fields = (field for field in dataclass_fields if field.name in include)
77
- if exclude:
78
- dataclass_fields = (field for field in dataclass_fields if field.name not in exclude)
79
-
80
- return tuple(dataclass_fields)
81
-
82
-
83
- def extract_dataclass_items(
84
- dt: DataclassProtocol,
85
- exclude_none: bool = False,
86
- exclude_empty: bool = False,
87
- include: AbstractSet[str] | None = None,
88
- exclude: AbstractSet[str] | None = None,
89
- ) -> tuple[tuple[str, Any], ...]:
90
- """Extract dataclass name, value pairs.
91
-
92
- Unlike the 'asdict' method exports by the stdlib, this function does not pickle values.
93
-
94
- Args:
95
- dt: A dataclass instance.
96
- exclude_none: Whether to exclude None values.
97
- exclude_empty: Whether to exclude Empty values.
98
- include: An iterable of fields to include.
99
- exclude: An iterable of fields to exclude.
100
-
101
- Returns:
102
- A tuple of key/value pairs.
103
- """
104
- dataclass_fields = extract_dataclass_fields(dt, exclude_none, exclude_empty, include, exclude)
105
- return tuple((field.name, getattr(dt, field.name)) for field in dataclass_fields)
106
-
107
-
108
- def simple_asdict(
109
- obj: DataclassProtocol,
110
- exclude_none: bool = False,
111
- exclude_empty: bool = False,
112
- convert_nested: bool = True,
113
- exclude: set[str] | None = None,
114
- ) -> dict[str, Any]:
115
- """Convert a dataclass to a dictionary.
116
-
117
- This method has important differences to the standard library version:
118
- - it does not deepcopy values
119
- - it does not recurse into collections
120
-
121
- Args:
122
- obj: A dataclass instance.
123
- exclude_none: Whether to exclude None values.
124
- exclude_empty: Whether to exclude Empty values.
125
- convert_nested: Whether to recursively convert nested dataclasses.
126
- exclude: An iterable of fields to exclude.
127
-
128
- Returns:
129
- A dictionary of key/value pairs.
130
- """
131
- ret = {}
132
- for field in extract_dataclass_fields(obj, exclude_none, exclude_empty, exclude=exclude):
133
- value = getattr(obj, field.name)
134
- if is_dataclass_instance(value) and convert_nested:
135
- ret[field.name] = simple_asdict(value, exclude_none, exclude_empty)
136
- else:
137
- ret[field.name] = getattr(obj, field.name)
138
- return ret
sqlspec/utils/empty.py DELETED
@@ -1,18 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from enum import Enum
4
- from typing import Final, Literal, Union
5
-
6
- from sqlspec.typing import UnsetType
7
-
8
- __all__ = ("Empty", "EmptyType")
9
-
10
-
11
- class _EmptyEnum(Enum):
12
- """A sentinel enum used as placeholder."""
13
-
14
- EMPTY = 0
15
-
16
-
17
- EmptyType = Union[Literal[_EmptyEnum.EMPTY], UnsetType]
18
- Empty: Final = _EmptyEnum.EMPTY
@@ -1,42 +0,0 @@
1
- sqlspec/__init__.py,sha256=U4S_2y3zgLZVfMenHRaJFBW8yqh2mUBuI291LGQVOJ8,35
2
- sqlspec/__metadata__.py,sha256=Vw99abV_UQNVH2jB0IBa9-8emyZQcXm1J9eMtLxFX2Y,496
3
- sqlspec/_serialization.py,sha256=OL4x0Rz5UjF7RgAKqhYChi5qSS_ImtaVrIlA4DhIKUE,824
4
- sqlspec/_typing.py,sha256=8tk4KqqO8OLDe-PIow2oaAnzgHOiFb74Mx7yuoAZUgI,2814
5
- sqlspec/config.py,sha256=BOX_V_q2MOP33tK0ISpYaiQJt3zrvK4D_JIBD9FOixY,272
6
- sqlspec/exceptions.py,sha256=fhCOILBj0J7HJP67BNSC0d9YUbW8QpZPXM55xJJzE8A,3039
7
- sqlspec/filters.py,sha256=UtDJVpABSxHLkadiswMcneBsvawmWsz3Bq7wLxLJn74,3454
8
- sqlspec/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- sqlspec/typing.py,sha256=9gXd4qAxZxejXo1NpGDKflsmWu5kQomB3fps3l4EbZs,7289
10
- sqlspec/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- sqlspec/adapters/adbc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- sqlspec/adapters/adbc/config.py,sha256=lCOoewYM0u9GHHGkux0FbSRAri4oIi91_Zrs_x3FkdQ,1745
13
- sqlspec/adapters/aiosqlite/__init__.py,sha256=PLqWg24l3TooJvqA0Xf1WErrxtqwo8DEoL_Zp2iSCzs,68
14
- sqlspec/adapters/aiosqlite/config.py,sha256=SM1nkRnrIzeUUMtL1cmMEh75epkCKtII1D-Wjgnc7ZY,4308
15
- sqlspec/adapters/asyncmy/__init__.py,sha256=o0R_Azae3FHiSZ1TQ5ZjyCneDOuvnEeMjmSkhuiKoWo,103
16
- sqlspec/adapters/asyncmy/config.py,sha256=0_0tdx7JvTIR1DZuaHeLg8IaK5Rv3li6dVKVOf8k_AI,5956
17
- sqlspec/adapters/asyncpg/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- sqlspec/adapters/asyncpg/config.py,sha256=Q1TgWtI2lrn0SDtP8KTVfJ2f1I6XhOUPSujKngWIf5A,6306
19
- sqlspec/adapters/duckdb/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- sqlspec/adapters/duckdb/config.py,sha256=2LfJnZYMcbXAR9BQ66Y9rhh7R_u6PI2GHApGo1Oy04k,3461
21
- sqlspec/adapters/oracledb/__init__.py,sha256=fFQ2xOxFcgpr-ug4AVv430irnJgBRUINvt4sL3qzyBw,275
22
- sqlspec/adapters/oracledb/config/__init__.py,sha256=XoHgInT4IbXjDg5ax3ncuUoVvnYB5qQjI-Ib7gwSycU,338
23
- sqlspec/adapters/oracledb/config/_asyncio.py,sha256=qB-1jPwjNdHYrDYjjQqR-q1KqMKFXESk-T9aZtdrwDI,3280
24
- sqlspec/adapters/oracledb/config/_common.py,sha256=VwMbZAX-jJ2kyAbtgRUOWDvO12nXsb68wO-3d3L9Wz4,6183
25
- sqlspec/adapters/oracledb/config/_sync.py,sha256=m_OkErwBKvQrFU6Q9PuduSJu_vHbmZl1gWYbpw6b22I,3268
26
- sqlspec/adapters/psycopg/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
- sqlspec/adapters/psycopg/config/__init__.py,sha256=pXI9Pa2VYESTchPgM3tt5kFF8tsmgq-ksZRGR6pgiUQ,280
28
- sqlspec/adapters/psycopg/config/_async.py,sha256=mdqFanrnodTq8u3THim3KkiNmEoIQpsKmBpJSaRiGTc,3004
29
- sqlspec/adapters/psycopg/config/_common.py,sha256=F4rwAlAcVM2HgDVnD87Pda0DjVV-oqzDUd0TofqnUL0,2766
30
- sqlspec/adapters/psycopg/config/_sync.py,sha256=YILMCmOpGX4E62hepuM0WgT7aPQpwbZpUwjAonmjUYc,2860
31
- sqlspec/adapters/sqlite/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
- sqlspec/adapters/sqlite/config.py,sha256=0c_eN01mEcM-OIiaDgs-4fM20z03QivANGxAuxJOisA,4117
33
- sqlspec/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
34
- sqlspec/extensions/litestar/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
35
- sqlspec/extensions/litestar/plugin.py,sha256=oiBFfRffNvy_vnGptREd6JYZGB6Yd98KbtVct_VcW0A,837
36
- sqlspec/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
- sqlspec/utils/dataclass.py,sha256=g88yP0Pi3EiF8N-Npuo-D6QpPExCLuUvJCO0U_3lHkQ,4301
38
- sqlspec/utils/empty.py,sha256=u5KC3HUF7MolHYs9kSt7Uq-jTIl0x9gJqf-jX0y5_BY,349
39
- sqlspec-0.3.0.dist-info/METADATA,sha256=DCx_OsG0w07rjQx01g2av9VxMgQZsmDWcZl0nsqJU8A,3222
40
- sqlspec-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
41
- sqlspec-0.3.0.dist-info/licenses/NOTICE,sha256=Lyir8ozXWov7CyYS4huVaOCNrtgL17P-bNV-5daLntQ,1634
42
- sqlspec-0.3.0.dist-info/RECORD,,