sqlspec 0.12.1__py3-none-any.whl → 0.13.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 (113) hide show
  1. sqlspec/_sql.py +21 -180
  2. sqlspec/adapters/adbc/config.py +10 -12
  3. sqlspec/adapters/adbc/driver.py +120 -118
  4. sqlspec/adapters/aiosqlite/config.py +3 -3
  5. sqlspec/adapters/aiosqlite/driver.py +116 -141
  6. sqlspec/adapters/asyncmy/config.py +3 -4
  7. sqlspec/adapters/asyncmy/driver.py +123 -135
  8. sqlspec/adapters/asyncpg/config.py +3 -7
  9. sqlspec/adapters/asyncpg/driver.py +98 -140
  10. sqlspec/adapters/bigquery/config.py +4 -5
  11. sqlspec/adapters/bigquery/driver.py +231 -181
  12. sqlspec/adapters/duckdb/config.py +3 -6
  13. sqlspec/adapters/duckdb/driver.py +132 -124
  14. sqlspec/adapters/oracledb/config.py +6 -5
  15. sqlspec/adapters/oracledb/driver.py +242 -259
  16. sqlspec/adapters/psqlpy/config.py +3 -7
  17. sqlspec/adapters/psqlpy/driver.py +118 -93
  18. sqlspec/adapters/psycopg/config.py +34 -30
  19. sqlspec/adapters/psycopg/driver.py +342 -214
  20. sqlspec/adapters/sqlite/config.py +3 -3
  21. sqlspec/adapters/sqlite/driver.py +150 -104
  22. sqlspec/config.py +0 -4
  23. sqlspec/driver/_async.py +89 -98
  24. sqlspec/driver/_common.py +52 -17
  25. sqlspec/driver/_sync.py +81 -105
  26. sqlspec/driver/connection.py +207 -0
  27. sqlspec/driver/mixins/_csv_writer.py +91 -0
  28. sqlspec/driver/mixins/_pipeline.py +38 -49
  29. sqlspec/driver/mixins/_result_utils.py +27 -9
  30. sqlspec/driver/mixins/_storage.py +149 -216
  31. sqlspec/driver/mixins/_type_coercion.py +3 -4
  32. sqlspec/driver/parameters.py +138 -0
  33. sqlspec/exceptions.py +10 -2
  34. sqlspec/extensions/aiosql/adapter.py +0 -10
  35. sqlspec/extensions/litestar/handlers.py +0 -1
  36. sqlspec/extensions/litestar/plugin.py +0 -3
  37. sqlspec/extensions/litestar/providers.py +0 -14
  38. sqlspec/loader.py +31 -118
  39. sqlspec/protocols.py +542 -0
  40. sqlspec/service/__init__.py +3 -2
  41. sqlspec/service/_util.py +147 -0
  42. sqlspec/service/base.py +1116 -9
  43. sqlspec/statement/builder/__init__.py +42 -32
  44. sqlspec/statement/builder/_ddl_utils.py +0 -10
  45. sqlspec/statement/builder/_parsing_utils.py +10 -4
  46. sqlspec/statement/builder/base.py +70 -23
  47. sqlspec/statement/builder/column.py +283 -0
  48. sqlspec/statement/builder/ddl.py +102 -65
  49. sqlspec/statement/builder/delete.py +23 -7
  50. sqlspec/statement/builder/insert.py +29 -15
  51. sqlspec/statement/builder/merge.py +4 -4
  52. sqlspec/statement/builder/mixins/_aggregate_functions.py +113 -14
  53. sqlspec/statement/builder/mixins/_common_table_expr.py +0 -1
  54. sqlspec/statement/builder/mixins/_delete_from.py +1 -1
  55. sqlspec/statement/builder/mixins/_from.py +10 -8
  56. sqlspec/statement/builder/mixins/_group_by.py +0 -1
  57. sqlspec/statement/builder/mixins/_insert_from_select.py +0 -1
  58. sqlspec/statement/builder/mixins/_insert_values.py +0 -2
  59. sqlspec/statement/builder/mixins/_join.py +20 -13
  60. sqlspec/statement/builder/mixins/_limit_offset.py +3 -3
  61. sqlspec/statement/builder/mixins/_merge_clauses.py +3 -4
  62. sqlspec/statement/builder/mixins/_order_by.py +2 -2
  63. sqlspec/statement/builder/mixins/_pivot.py +4 -7
  64. sqlspec/statement/builder/mixins/_select_columns.py +6 -5
  65. sqlspec/statement/builder/mixins/_unpivot.py +6 -9
  66. sqlspec/statement/builder/mixins/_update_from.py +2 -1
  67. sqlspec/statement/builder/mixins/_update_set.py +11 -8
  68. sqlspec/statement/builder/mixins/_where.py +61 -34
  69. sqlspec/statement/builder/select.py +32 -17
  70. sqlspec/statement/builder/update.py +25 -11
  71. sqlspec/statement/filters.py +39 -14
  72. sqlspec/statement/parameter_manager.py +220 -0
  73. sqlspec/statement/parameters.py +210 -79
  74. sqlspec/statement/pipelines/__init__.py +166 -23
  75. sqlspec/statement/pipelines/analyzers/_analyzer.py +22 -25
  76. sqlspec/statement/pipelines/context.py +35 -39
  77. sqlspec/statement/pipelines/transformers/__init__.py +2 -3
  78. sqlspec/statement/pipelines/transformers/_expression_simplifier.py +19 -187
  79. sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +667 -43
  80. sqlspec/statement/pipelines/transformers/_remove_comments_and_hints.py +76 -0
  81. sqlspec/statement/pipelines/validators/_dml_safety.py +33 -18
  82. sqlspec/statement/pipelines/validators/_parameter_style.py +87 -14
  83. sqlspec/statement/pipelines/validators/_performance.py +38 -23
  84. sqlspec/statement/pipelines/validators/_security.py +39 -62
  85. sqlspec/statement/result.py +37 -129
  86. sqlspec/statement/splitter.py +0 -12
  87. sqlspec/statement/sql.py +885 -379
  88. sqlspec/statement/sql_compiler.py +140 -0
  89. sqlspec/storage/__init__.py +10 -2
  90. sqlspec/storage/backends/fsspec.py +82 -35
  91. sqlspec/storage/backends/obstore.py +66 -49
  92. sqlspec/storage/capabilities.py +101 -0
  93. sqlspec/storage/registry.py +56 -83
  94. sqlspec/typing.py +6 -434
  95. sqlspec/utils/cached_property.py +25 -0
  96. sqlspec/utils/correlation.py +0 -2
  97. sqlspec/utils/logging.py +0 -6
  98. sqlspec/utils/sync_tools.py +0 -4
  99. sqlspec/utils/text.py +0 -5
  100. sqlspec/utils/type_guards.py +892 -0
  101. {sqlspec-0.12.1.dist-info → sqlspec-0.13.0.dist-info}/METADATA +1 -1
  102. sqlspec-0.13.0.dist-info/RECORD +150 -0
  103. sqlspec/statement/builder/protocols.py +0 -20
  104. sqlspec/statement/pipelines/base.py +0 -315
  105. sqlspec/statement/pipelines/result_types.py +0 -41
  106. sqlspec/statement/pipelines/transformers/_remove_comments.py +0 -66
  107. sqlspec/statement/pipelines/transformers/_remove_hints.py +0 -81
  108. sqlspec/statement/pipelines/validators/base.py +0 -67
  109. sqlspec/storage/protocol.py +0 -170
  110. sqlspec-0.12.1.dist-info/RECORD +0 -145
  111. {sqlspec-0.12.1.dist-info → sqlspec-0.13.0.dist-info}/WHEEL +0 -0
  112. {sqlspec-0.12.1.dist-info → sqlspec-0.13.0.dist-info}/licenses/LICENSE +0 -0
  113. {sqlspec-0.12.1.dist-info → sqlspec-0.13.0.dist-info}/licenses/NOTICE +0 -0
@@ -3,11 +3,12 @@ import logging
3
3
  from collections.abc import AsyncGenerator, Sequence
4
4
  from contextlib import asynccontextmanager
5
5
  from pathlib import Path
6
- from typing import TYPE_CHECKING, Any, Optional, Union, cast
6
+ from typing import TYPE_CHECKING, Any, Optional
7
7
 
8
8
  import aiosqlite
9
9
 
10
10
  from sqlspec.driver import AsyncDriverAdapterProtocol
11
+ from sqlspec.driver.connection import managed_transaction_async
11
12
  from sqlspec.driver.mixins import (
12
13
  AsyncPipelinedExecutionMixin,
13
14
  AsyncStorageMixin,
@@ -15,10 +16,11 @@ from sqlspec.driver.mixins import (
15
16
  ToSchemaMixin,
16
17
  TypeCoercionMixin,
17
18
  )
18
- from sqlspec.statement.parameters import ParameterStyle
19
- from sqlspec.statement.result import DMLResultDict, ScriptResultDict, SelectResultDict, SQLResult
19
+ from sqlspec.driver.parameters import normalize_parameter_sequence
20
+ from sqlspec.statement.parameters import ParameterStyle, ParameterValidator
21
+ from sqlspec.statement.result import SQLResult
20
22
  from sqlspec.statement.sql import SQL, SQLConfig
21
- from sqlspec.typing import DictRow, ModelDTOT, RowT
23
+ from sqlspec.typing import DictRow, RowT
22
24
  from sqlspec.utils.serializers import to_json
23
25
 
24
26
  if TYPE_CHECKING:
@@ -97,22 +99,24 @@ class AiosqliteDriver(
97
99
 
98
100
  async def _execute_statement(
99
101
  self, statement: SQL, connection: Optional[AiosqliteConnection] = None, **kwargs: Any
100
- ) -> Union[SelectResultDict, DMLResultDict, ScriptResultDict]:
102
+ ) -> SQLResult[RowT]:
101
103
  if statement.is_script:
102
104
  sql, _ = statement.compile(placeholder_style=ParameterStyle.STATIC)
103
105
  return await self._execute_script(sql, connection=connection, **kwargs)
104
106
 
105
- # Determine if we need to convert parameter style
106
- detected_styles = {p.style for p in statement.parameter_info}
107
+ detected_styles = set()
108
+ sql_str = statement.to_sql(placeholder_style=None) # Get raw SQL
109
+ validator = self.config.parameter_validator if self.config else ParameterValidator()
110
+ param_infos = validator.extract_parameters(sql_str)
111
+ if param_infos:
112
+ detected_styles = {p.style for p in param_infos}
113
+
107
114
  target_style = self.default_parameter_style
108
115
 
109
- # Check if any detected style is not supported
110
116
  unsupported_styles = detected_styles - set(self.supported_parameter_styles)
111
117
  if unsupported_styles:
112
- # Convert to default style if we have unsupported styles
113
118
  target_style = self.default_parameter_style
114
119
  elif detected_styles:
115
- # Use the first detected style if all are supported
116
120
  # Prefer the first supported style found
117
121
  for style in detected_styles:
118
122
  if style in self.supported_parameter_styles:
@@ -122,89 +126,114 @@ class AiosqliteDriver(
122
126
  if statement.is_many:
123
127
  sql, params = statement.compile(placeholder_style=target_style)
124
128
 
125
- # Process parameter list through type coercion
126
129
  params = self._process_parameters(params)
127
130
 
128
131
  return await self._execute_many(sql, params, connection=connection, **kwargs)
129
132
 
130
133
  sql, params = statement.compile(placeholder_style=target_style)
131
134
 
132
- # Process parameters through type coercion
133
135
  params = self._process_parameters(params)
134
136
 
135
137
  return await self._execute(sql, params, statement, connection=connection, **kwargs)
136
138
 
137
139
  async def _execute(
138
140
  self, sql: str, parameters: Any, statement: SQL, connection: Optional[AiosqliteConnection] = None, **kwargs: Any
139
- ) -> Union[SelectResultDict, DMLResultDict]:
141
+ ) -> SQLResult[RowT]:
140
142
  conn = self._connection(connection)
141
- # Convert parameters to the format expected by the SQL
142
- # Note: SQL was already rendered with appropriate placeholder style in _execute_statement
143
- if ":param_" in sql or (parameters and isinstance(parameters, dict)):
144
- # SQL has named placeholders, ensure params are dict
145
- converted_params = self._convert_parameters_to_driver_format(
146
- sql, parameters, target_style=ParameterStyle.NAMED_COLON
147
- )
148
- else:
149
- # SQL has positional placeholders, ensure params are list/tuple
150
- converted_params = self._convert_parameters_to_driver_format(
151
- sql, parameters, target_style=ParameterStyle.QMARK
152
- )
153
- async with self._get_cursor(conn) as cursor:
154
- # Aiosqlite handles both dict and tuple parameters
155
- await cursor.execute(sql, converted_params or ())
156
- if self.returns_rows(statement.expression):
157
- fetched_data = await cursor.fetchall()
158
- column_names = [desc[0] for desc in cursor.description or []]
159
- # Convert to list of dicts or tuples as expected by TypedDict
160
- data_list: list[Any] = list(fetched_data) if fetched_data else []
161
- result: SelectResultDict = {
162
- "data": data_list,
163
- "column_names": column_names,
164
- "rows_affected": len(data_list),
165
- }
166
- return result
167
- dml_result: DMLResultDict = {"rows_affected": cursor.rowcount, "status_message": "OK"}
168
- return dml_result
143
+
144
+ async with managed_transaction_async(conn, auto_commit=True) as txn_conn:
145
+ normalized_params = normalize_parameter_sequence(parameters)
146
+
147
+ # Extract the actual parameters from the normalized list
148
+ if normalized_params and len(normalized_params) == 1:
149
+ actual_params = normalized_params[0]
150
+ else:
151
+ actual_params = normalized_params
152
+
153
+ # AIOSQLite expects tuple or dict - handle parameter conversion
154
+ if ":param_" in sql or (isinstance(actual_params, dict)):
155
+ # SQL has named placeholders, ensure params are dict
156
+ converted_params = self._convert_parameters_to_driver_format(
157
+ sql, actual_params, target_style=ParameterStyle.NAMED_COLON
158
+ )
159
+ else:
160
+ # SQL has positional placeholders, ensure params are list/tuple
161
+ converted_params = self._convert_parameters_to_driver_format(
162
+ sql, actual_params, target_style=ParameterStyle.QMARK
163
+ )
164
+
165
+ async with self._get_cursor(txn_conn) as cursor:
166
+ # Aiosqlite handles both dict and tuple parameters
167
+ await cursor.execute(sql, converted_params or ())
168
+ if self.returns_rows(statement.expression):
169
+ fetched_data = await cursor.fetchall()
170
+ column_names = [desc[0] for desc in cursor.description or []]
171
+ data_list: list[Any] = list(fetched_data) if fetched_data else []
172
+ return SQLResult(
173
+ statement=statement,
174
+ data=data_list,
175
+ column_names=column_names,
176
+ rows_affected=len(data_list),
177
+ operation_type="SELECT",
178
+ )
179
+
180
+ return SQLResult(
181
+ statement=statement,
182
+ data=[],
183
+ rows_affected=cursor.rowcount,
184
+ operation_type=self._determine_operation_type(statement),
185
+ metadata={"status_message": "OK"},
186
+ )
169
187
 
170
188
  async def _execute_many(
171
189
  self, sql: str, param_list: Any, connection: Optional[AiosqliteConnection] = None, **kwargs: Any
172
- ) -> DMLResultDict:
173
- conn = self._connection(connection)
174
- logger.debug("Executing SQL (executemany): %s", sql)
175
- if param_list:
176
- logger.debug("Query parameters (batch): %s", param_list)
177
-
178
- # Convert parameter list to proper format for executemany
179
- params_list: list[tuple[Any, ...]] = []
180
- if param_list and isinstance(param_list, Sequence):
181
- for param_set in param_list:
182
- param_set = cast("Any", param_set)
183
- if isinstance(param_set, (list, tuple)):
184
- params_list.append(tuple(param_set))
185
- elif param_set is None:
186
- params_list.append(())
187
-
188
- async with self._get_cursor(conn) as cursor:
189
- await cursor.executemany(sql, params_list)
190
- result: DMLResultDict = {"rows_affected": cursor.rowcount, "status_message": "OK"}
191
- return result
190
+ ) -> SQLResult[RowT]:
191
+ # Use provided connection or driver's default connection
192
+ conn = connection if connection is not None else self._connection(None)
193
+
194
+ async with managed_transaction_async(conn, auto_commit=True) as txn_conn:
195
+ # Normalize parameter list using consolidated utility
196
+ normalized_param_list = normalize_parameter_sequence(param_list)
197
+
198
+ params_list: list[tuple[Any, ...]] = []
199
+ if normalized_param_list and isinstance(normalized_param_list, Sequence):
200
+ for param_set in normalized_param_list:
201
+ if isinstance(param_set, (list, tuple)):
202
+ params_list.append(tuple(param_set))
203
+ elif param_set is None:
204
+ params_list.append(())
205
+
206
+ async with self._get_cursor(txn_conn) as cursor:
207
+ await cursor.executemany(sql, params_list)
208
+ return SQLResult(
209
+ statement=SQL(sql, _dialect=self.dialect),
210
+ data=[],
211
+ rows_affected=cursor.rowcount,
212
+ operation_type="EXECUTE",
213
+ metadata={"status_message": "OK"},
214
+ )
192
215
 
193
216
  async def _execute_script(
194
217
  self, script: str, connection: Optional[AiosqliteConnection] = None, **kwargs: Any
195
- ) -> ScriptResultDict:
196
- conn = self._connection(connection)
197
- async with self._get_cursor(conn) as cursor:
198
- await cursor.executescript(script)
199
- result: ScriptResultDict = {
200
- "statements_executed": -1, # AIOSQLite doesn't provide this info
201
- "status_message": "SCRIPT EXECUTED",
202
- }
203
- return result
218
+ ) -> SQLResult[RowT]:
219
+ # Use provided connection or driver's default connection
220
+ conn = connection if connection is not None else self._connection(None)
221
+
222
+ async with managed_transaction_async(conn, auto_commit=True) as txn_conn:
223
+ async with self._get_cursor(txn_conn) as cursor:
224
+ await cursor.executescript(script)
225
+ return SQLResult(
226
+ statement=SQL(script, _dialect=self.dialect).as_script(),
227
+ data=[],
228
+ rows_affected=0,
229
+ operation_type="SCRIPT",
230
+ metadata={"status_message": "SCRIPT EXECUTED"},
231
+ total_statements=-1, # AIOSQLite doesn't provide this info
232
+ successful_statements=-1,
233
+ )
204
234
 
205
235
  async def _bulk_load_file(self, file_path: Path, table_name: str, format: str, mode: str, **options: Any) -> int:
206
- """Database-specific bulk load implementation."""
207
- # TODO: convert this to use the storage backend. it has async support
236
+ """Database-specific bulk load implementation using storage backend."""
208
237
  if format != "csv":
209
238
  msg = f"aiosqlite driver only supports CSV for bulk loading, not {format}."
210
239
  raise NotImplementedError(msg)
@@ -215,80 +244,26 @@ class AiosqliteDriver(
215
244
  if mode == "replace":
216
245
  await cursor.execute(f"DELETE FROM {table_name}")
217
246
 
218
- # Using sync file IO here as it's a fallback path and aiofiles is not a dependency
219
- with Path(file_path).open(encoding="utf-8") as f: # noqa: ASYNC230
220
- reader = csv.reader(f, **options)
221
- header = next(reader) # Skip header
222
- placeholders = ", ".join("?" for _ in header)
223
- sql = f"INSERT INTO {table_name} VALUES ({placeholders})"
224
- data_iter = list(reader)
225
- await cursor.executemany(sql, data_iter)
226
- rowcount = cursor.rowcount
247
+ # Use async storage backend to read the file
248
+ file_path_str = str(file_path)
249
+ backend = self._get_storage_backend(file_path_str)
250
+ content = await backend.read_text_async(file_path_str, encoding="utf-8")
251
+ # Parse CSV content
252
+ import io
253
+
254
+ csv_file = io.StringIO(content)
255
+ reader = csv.reader(csv_file, **options)
256
+ header = next(reader) # Skip header
257
+ placeholders = ", ".join("?" for _ in header)
258
+ sql = f"INSERT INTO {table_name} VALUES ({placeholders})"
259
+ data_iter = list(reader)
260
+ await cursor.executemany(sql, data_iter)
261
+ rowcount = cursor.rowcount
227
262
  await conn.commit()
228
263
  return rowcount
229
264
  finally:
230
265
  await conn.close()
231
266
 
232
- async def _wrap_select_result(
233
- self, statement: SQL, result: SelectResultDict, schema_type: "Optional[type[ModelDTOT]]" = None, **kwargs: Any
234
- ) -> Union[SQLResult[ModelDTOT], SQLResult[RowT]]:
235
- fetched_data = result["data"]
236
- column_names = result["column_names"]
237
- rows_affected = result["rows_affected"]
238
-
239
- rows_as_dicts: list[dict[str, Any]] = [dict(row) for row in fetched_data]
240
-
241
- if self.returns_rows(statement.expression):
242
- converted_data_seq = self.to_schema(data=rows_as_dicts, schema_type=schema_type)
243
- return SQLResult[ModelDTOT](
244
- statement=statement,
245
- data=list(converted_data_seq),
246
- column_names=column_names,
247
- rows_affected=rows_affected,
248
- operation_type="SELECT",
249
- )
250
- return SQLResult[RowT](
251
- statement=statement,
252
- data=rows_as_dicts,
253
- column_names=column_names,
254
- rows_affected=rows_affected,
255
- operation_type="SELECT",
256
- )
257
-
258
- async def _wrap_execute_result(
259
- self, statement: SQL, result: Union[DMLResultDict, ScriptResultDict], **kwargs: Any
260
- ) -> SQLResult[RowT]:
261
- operation_type = "UNKNOWN"
262
- if statement.expression:
263
- operation_type = str(statement.expression.key).upper()
264
-
265
- if "statements_executed" in result:
266
- script_result = cast("ScriptResultDict", result)
267
- return SQLResult[RowT](
268
- statement=statement,
269
- data=[],
270
- rows_affected=0,
271
- operation_type="SCRIPT",
272
- total_statements=script_result.get("statements_executed", -1),
273
- metadata={"status_message": script_result.get("status_message", "")},
274
- )
275
-
276
- if "rows_affected" in result:
277
- dml_result = cast("DMLResultDict", result)
278
- rows_affected = dml_result["rows_affected"]
279
- status_message = dml_result["status_message"]
280
- return SQLResult[RowT](
281
- statement=statement,
282
- data=[],
283
- rows_affected=rows_affected,
284
- operation_type=operation_type,
285
- metadata={"status_message": status_message},
286
- )
287
-
288
- # This shouldn't happen with TypedDict approach
289
- msg = f"Unexpected result type: {type(result)}"
290
- raise ValueError(msg)
291
-
292
267
  def _connection(self, connection: Optional[AiosqliteConnection] = None) -> AiosqliteConnection:
293
268
  """Get the connection to use for the operation."""
294
269
  return connection or self.connection
@@ -3,7 +3,6 @@
3
3
  import logging
4
4
  from collections.abc import AsyncGenerator
5
5
  from contextlib import asynccontextmanager
6
- from dataclasses import replace
7
6
  from typing import TYPE_CHECKING, Any, ClassVar, Optional, Union
8
7
 
9
8
  import asyncmy
@@ -193,7 +192,6 @@ class AsyncmyConfig(AsyncDatabaseConfig[AsyncmyConnection, "Pool", AsyncmyDriver
193
192
  if getattr(self, field, None) is not None and getattr(self, field) is not Empty
194
193
  }
195
194
 
196
- # Add connection-specific extras (not pool-specific ones)
197
195
  config.update(self.extras)
198
196
 
199
197
  return config
@@ -264,15 +262,16 @@ class AsyncmyConfig(AsyncDatabaseConfig[AsyncmyConnection, "Pool", AsyncmyDriver
264
262
  An AsyncmyDriver instance.
265
263
  """
266
264
  async with self.provide_connection(*args, **kwargs) as connection:
267
- # Create statement config with parameter style info if not already set
268
265
  statement_config = self.statement_config
266
+ # Inject parameter style info if not already set
269
267
  if statement_config.allowed_parameter_styles is None:
268
+ from dataclasses import replace
269
+
270
270
  statement_config = replace(
271
271
  statement_config,
272
272
  allowed_parameter_styles=self.supported_parameter_styles,
273
273
  target_parameter_style=self.preferred_parameter_style,
274
274
  )
275
-
276
275
  yield self.driver_type(connection=connection, config=statement_config)
277
276
 
278
277
  async def provide_pool(self, *args: Any, **kwargs: Any) -> "Pool": # pyright: ignore