sqlspec 0.16.1__cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.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 (148) hide show
  1. 51ff5a9eadfdefd49f98__mypyc.cpython-39-aarch64-linux-gnu.so +0 -0
  2. sqlspec/__init__.py +92 -0
  3. sqlspec/__main__.py +12 -0
  4. sqlspec/__metadata__.py +14 -0
  5. sqlspec/_serialization.py +77 -0
  6. sqlspec/_sql.py +1780 -0
  7. sqlspec/_typing.py +680 -0
  8. sqlspec/adapters/__init__.py +0 -0
  9. sqlspec/adapters/adbc/__init__.py +5 -0
  10. sqlspec/adapters/adbc/_types.py +12 -0
  11. sqlspec/adapters/adbc/config.py +361 -0
  12. sqlspec/adapters/adbc/driver.py +512 -0
  13. sqlspec/adapters/aiosqlite/__init__.py +19 -0
  14. sqlspec/adapters/aiosqlite/_types.py +13 -0
  15. sqlspec/adapters/aiosqlite/config.py +253 -0
  16. sqlspec/adapters/aiosqlite/driver.py +248 -0
  17. sqlspec/adapters/asyncmy/__init__.py +19 -0
  18. sqlspec/adapters/asyncmy/_types.py +12 -0
  19. sqlspec/adapters/asyncmy/config.py +180 -0
  20. sqlspec/adapters/asyncmy/driver.py +274 -0
  21. sqlspec/adapters/asyncpg/__init__.py +21 -0
  22. sqlspec/adapters/asyncpg/_types.py +17 -0
  23. sqlspec/adapters/asyncpg/config.py +229 -0
  24. sqlspec/adapters/asyncpg/driver.py +344 -0
  25. sqlspec/adapters/bigquery/__init__.py +18 -0
  26. sqlspec/adapters/bigquery/_types.py +12 -0
  27. sqlspec/adapters/bigquery/config.py +298 -0
  28. sqlspec/adapters/bigquery/driver.py +558 -0
  29. sqlspec/adapters/duckdb/__init__.py +22 -0
  30. sqlspec/adapters/duckdb/_types.py +12 -0
  31. sqlspec/adapters/duckdb/config.py +504 -0
  32. sqlspec/adapters/duckdb/driver.py +368 -0
  33. sqlspec/adapters/oracledb/__init__.py +32 -0
  34. sqlspec/adapters/oracledb/_types.py +14 -0
  35. sqlspec/adapters/oracledb/config.py +317 -0
  36. sqlspec/adapters/oracledb/driver.py +538 -0
  37. sqlspec/adapters/psqlpy/__init__.py +16 -0
  38. sqlspec/adapters/psqlpy/_types.py +11 -0
  39. sqlspec/adapters/psqlpy/config.py +214 -0
  40. sqlspec/adapters/psqlpy/driver.py +530 -0
  41. sqlspec/adapters/psycopg/__init__.py +32 -0
  42. sqlspec/adapters/psycopg/_types.py +17 -0
  43. sqlspec/adapters/psycopg/config.py +426 -0
  44. sqlspec/adapters/psycopg/driver.py +796 -0
  45. sqlspec/adapters/sqlite/__init__.py +15 -0
  46. sqlspec/adapters/sqlite/_types.py +11 -0
  47. sqlspec/adapters/sqlite/config.py +240 -0
  48. sqlspec/adapters/sqlite/driver.py +294 -0
  49. sqlspec/base.py +571 -0
  50. sqlspec/builder/__init__.py +62 -0
  51. sqlspec/builder/_base.py +473 -0
  52. sqlspec/builder/_column.py +320 -0
  53. sqlspec/builder/_ddl.py +1346 -0
  54. sqlspec/builder/_ddl_utils.py +103 -0
  55. sqlspec/builder/_delete.py +76 -0
  56. sqlspec/builder/_insert.py +256 -0
  57. sqlspec/builder/_merge.py +71 -0
  58. sqlspec/builder/_parsing_utils.py +140 -0
  59. sqlspec/builder/_select.py +170 -0
  60. sqlspec/builder/_update.py +188 -0
  61. sqlspec/builder/mixins/__init__.py +55 -0
  62. sqlspec/builder/mixins/_cte_and_set_ops.py +222 -0
  63. sqlspec/builder/mixins/_delete_operations.py +41 -0
  64. sqlspec/builder/mixins/_insert_operations.py +244 -0
  65. sqlspec/builder/mixins/_join_operations.py +122 -0
  66. sqlspec/builder/mixins/_merge_operations.py +476 -0
  67. sqlspec/builder/mixins/_order_limit_operations.py +135 -0
  68. sqlspec/builder/mixins/_pivot_operations.py +153 -0
  69. sqlspec/builder/mixins/_select_operations.py +603 -0
  70. sqlspec/builder/mixins/_update_operations.py +187 -0
  71. sqlspec/builder/mixins/_where_clause.py +621 -0
  72. sqlspec/cli.py +247 -0
  73. sqlspec/config.py +395 -0
  74. sqlspec/core/__init__.py +63 -0
  75. sqlspec/core/cache.cpython-39-aarch64-linux-gnu.so +0 -0
  76. sqlspec/core/cache.py +871 -0
  77. sqlspec/core/compiler.cpython-39-aarch64-linux-gnu.so +0 -0
  78. sqlspec/core/compiler.py +417 -0
  79. sqlspec/core/filters.cpython-39-aarch64-linux-gnu.so +0 -0
  80. sqlspec/core/filters.py +830 -0
  81. sqlspec/core/hashing.cpython-39-aarch64-linux-gnu.so +0 -0
  82. sqlspec/core/hashing.py +310 -0
  83. sqlspec/core/parameters.cpython-39-aarch64-linux-gnu.so +0 -0
  84. sqlspec/core/parameters.py +1237 -0
  85. sqlspec/core/result.cpython-39-aarch64-linux-gnu.so +0 -0
  86. sqlspec/core/result.py +677 -0
  87. sqlspec/core/splitter.cpython-39-aarch64-linux-gnu.so +0 -0
  88. sqlspec/core/splitter.py +819 -0
  89. sqlspec/core/statement.cpython-39-aarch64-linux-gnu.so +0 -0
  90. sqlspec/core/statement.py +676 -0
  91. sqlspec/driver/__init__.py +19 -0
  92. sqlspec/driver/_async.py +502 -0
  93. sqlspec/driver/_common.py +631 -0
  94. sqlspec/driver/_sync.py +503 -0
  95. sqlspec/driver/mixins/__init__.py +6 -0
  96. sqlspec/driver/mixins/_result_tools.py +193 -0
  97. sqlspec/driver/mixins/_sql_translator.py +86 -0
  98. sqlspec/exceptions.py +193 -0
  99. sqlspec/extensions/__init__.py +0 -0
  100. sqlspec/extensions/aiosql/__init__.py +10 -0
  101. sqlspec/extensions/aiosql/adapter.py +461 -0
  102. sqlspec/extensions/litestar/__init__.py +6 -0
  103. sqlspec/extensions/litestar/_utils.py +52 -0
  104. sqlspec/extensions/litestar/cli.py +48 -0
  105. sqlspec/extensions/litestar/config.py +92 -0
  106. sqlspec/extensions/litestar/handlers.py +260 -0
  107. sqlspec/extensions/litestar/plugin.py +145 -0
  108. sqlspec/extensions/litestar/providers.py +454 -0
  109. sqlspec/loader.cpython-39-aarch64-linux-gnu.so +0 -0
  110. sqlspec/loader.py +760 -0
  111. sqlspec/migrations/__init__.py +35 -0
  112. sqlspec/migrations/base.py +414 -0
  113. sqlspec/migrations/commands.py +443 -0
  114. sqlspec/migrations/loaders.py +402 -0
  115. sqlspec/migrations/runner.py +213 -0
  116. sqlspec/migrations/tracker.py +140 -0
  117. sqlspec/migrations/utils.py +129 -0
  118. sqlspec/protocols.py +407 -0
  119. sqlspec/py.typed +0 -0
  120. sqlspec/storage/__init__.py +23 -0
  121. sqlspec/storage/backends/__init__.py +0 -0
  122. sqlspec/storage/backends/base.py +163 -0
  123. sqlspec/storage/backends/fsspec.py +386 -0
  124. sqlspec/storage/backends/obstore.py +459 -0
  125. sqlspec/storage/capabilities.py +102 -0
  126. sqlspec/storage/registry.py +239 -0
  127. sqlspec/typing.py +299 -0
  128. sqlspec/utils/__init__.py +3 -0
  129. sqlspec/utils/correlation.py +150 -0
  130. sqlspec/utils/deprecation.py +106 -0
  131. sqlspec/utils/fixtures.cpython-39-aarch64-linux-gnu.so +0 -0
  132. sqlspec/utils/fixtures.py +58 -0
  133. sqlspec/utils/logging.py +127 -0
  134. sqlspec/utils/module_loader.py +89 -0
  135. sqlspec/utils/serializers.py +4 -0
  136. sqlspec/utils/singleton.py +32 -0
  137. sqlspec/utils/sync_tools.cpython-39-aarch64-linux-gnu.so +0 -0
  138. sqlspec/utils/sync_tools.py +237 -0
  139. sqlspec/utils/text.cpython-39-aarch64-linux-gnu.so +0 -0
  140. sqlspec/utils/text.py +96 -0
  141. sqlspec/utils/type_guards.cpython-39-aarch64-linux-gnu.so +0 -0
  142. sqlspec/utils/type_guards.py +1139 -0
  143. sqlspec-0.16.1.dist-info/METADATA +365 -0
  144. sqlspec-0.16.1.dist-info/RECORD +148 -0
  145. sqlspec-0.16.1.dist-info/WHEEL +7 -0
  146. sqlspec-0.16.1.dist-info/entry_points.txt +2 -0
  147. sqlspec-0.16.1.dist-info/licenses/LICENSE +21 -0
  148. sqlspec-0.16.1.dist-info/licenses/NOTICE +29 -0
@@ -0,0 +1,443 @@
1
+ """Migration command implementations for SQLSpec.
2
+
3
+ This module provides the main command interface for database migrations.
4
+ """
5
+
6
+ from typing import TYPE_CHECKING, Any, Union, cast
7
+
8
+ from rich.console import Console
9
+ from rich.table import Table
10
+
11
+ from sqlspec._sql import sql
12
+ from sqlspec.migrations.base import BaseMigrationCommands
13
+ from sqlspec.migrations.runner import AsyncMigrationRunner, SyncMigrationRunner
14
+ from sqlspec.migrations.tracker import AsyncMigrationTracker, SyncMigrationTracker
15
+ from sqlspec.migrations.utils import create_migration_file
16
+ from sqlspec.utils.logging import get_logger
17
+ from sqlspec.utils.sync_tools import await_
18
+
19
+ if TYPE_CHECKING:
20
+ from sqlspec.config import AsyncConfigT, SyncConfigT
21
+
22
+ __all__ = ("AsyncMigrationCommands", "MigrationCommands", "SyncMigrationCommands")
23
+
24
+ logger = get_logger("migrations.commands")
25
+ console = Console()
26
+
27
+
28
+ class SyncMigrationCommands(BaseMigrationCommands["SyncConfigT", Any]):
29
+ """SQLSpec native migration commands."""
30
+
31
+ def __init__(self, config: "SyncConfigT") -> None:
32
+ """Initialize migration commands.
33
+
34
+ Args:
35
+ config: The SQLSpec configuration.
36
+ """
37
+ super().__init__(config)
38
+ self.tracker = SyncMigrationTracker(self.version_table)
39
+ self.runner = SyncMigrationRunner(self.migrations_path)
40
+
41
+ def init(self, directory: str, package: bool = True) -> None:
42
+ """Initialize migration directory structure.
43
+
44
+ Args:
45
+ directory: Directory to initialize migrations in.
46
+ package: Whether to create __init__.py file.
47
+ """
48
+ self.init_directory(directory, package)
49
+
50
+ def current(self, verbose: bool = False) -> None:
51
+ """Show current migration version.
52
+
53
+ Args:
54
+ verbose: Whether to show detailed migration history.
55
+ """
56
+ with self.config.provide_session() as driver:
57
+ self.tracker.ensure_tracking_table(driver)
58
+
59
+ current = self.tracker.get_current_version(driver)
60
+ if not current:
61
+ console.print("[yellow]No migrations applied yet[/]")
62
+ return
63
+
64
+ console.print(f"[green]Current version:[/] {current}")
65
+
66
+ if verbose:
67
+ applied = self.tracker.get_applied_migrations(driver)
68
+
69
+ table = Table(title="Applied Migrations")
70
+ table.add_column("Version", style="cyan")
71
+ table.add_column("Description")
72
+ table.add_column("Applied At")
73
+ table.add_column("Time (ms)", justify="right")
74
+ table.add_column("Applied By")
75
+
76
+ for migration in applied:
77
+ table.add_row(
78
+ migration["version_num"],
79
+ migration.get("description", ""),
80
+ str(migration.get("applied_at", "")),
81
+ str(migration.get("execution_time_ms", "")),
82
+ migration.get("applied_by", ""),
83
+ )
84
+
85
+ console.print(table)
86
+
87
+ def upgrade(self, revision: str = "head") -> None:
88
+ """Upgrade to a target revision.
89
+
90
+ Args:
91
+ revision: Target revision or "head" for latest.
92
+ """
93
+ with self.config.provide_session() as driver:
94
+ self.tracker.ensure_tracking_table(driver)
95
+
96
+ current = self.tracker.get_current_version(driver)
97
+ all_migrations = self.runner.get_migration_files()
98
+ pending = []
99
+ for version, file_path in all_migrations:
100
+ if (current is None or version > current) and (revision == "head" or version <= revision):
101
+ pending.append((version, file_path))
102
+
103
+ if not pending:
104
+ console.print("[green]Already at latest version[/]")
105
+ return
106
+
107
+ console.print(f"[yellow]Found {len(pending)} pending migrations[/]")
108
+
109
+ for version, file_path in pending:
110
+ migration = self.runner.load_migration(file_path)
111
+
112
+ console.print(f"\n[cyan]Applying {version}:[/] {migration['description']}")
113
+
114
+ try:
115
+ _, execution_time = self.runner.execute_upgrade(driver, migration)
116
+ self.tracker.record_migration(
117
+ driver, migration["version"], migration["description"], execution_time, migration["checksum"]
118
+ )
119
+ console.print(f"[green]✓ Applied in {execution_time}ms[/]")
120
+
121
+ except Exception as e:
122
+ console.print(f"[red]✗ Failed: {e}[/]")
123
+ raise
124
+
125
+ def downgrade(self, revision: str = "-1") -> None:
126
+ """Downgrade to a target revision.
127
+
128
+ Args:
129
+ revision: Target revision or "-1" for one step back.
130
+ """
131
+ with self.config.provide_session() as driver:
132
+ self.tracker.ensure_tracking_table(driver)
133
+ applied = self.tracker.get_applied_migrations(driver)
134
+ if not applied:
135
+ console.print("[yellow]No migrations to downgrade[/]")
136
+ return
137
+ to_revert = []
138
+ if revision == "-1":
139
+ to_revert = [applied[-1]]
140
+ else:
141
+ for migration in reversed(applied):
142
+ if migration["version_num"] > revision:
143
+ to_revert.append(migration)
144
+
145
+ if not to_revert:
146
+ console.print("[yellow]Nothing to downgrade[/]")
147
+ return
148
+
149
+ console.print(f"[yellow]Reverting {len(to_revert)} migrations[/]")
150
+ all_files = dict(self.runner.get_migration_files())
151
+ for migration_record in to_revert:
152
+ version = migration_record["version_num"]
153
+ if version not in all_files:
154
+ console.print(f"[red]Migration file not found for {version}[/]")
155
+ continue
156
+ migration = self.runner.load_migration(all_files[version])
157
+ console.print(f"\n[cyan]Reverting {version}:[/] {migration['description']}")
158
+ try:
159
+ _, execution_time = self.runner.execute_downgrade(driver, migration)
160
+ self.tracker.remove_migration(driver, version)
161
+ console.print(f"[green]✓ Reverted in {execution_time}ms[/]")
162
+ except Exception as e:
163
+ console.print(f"[red]✗ Failed: {e}[/]")
164
+ raise
165
+
166
+ def stamp(self, revision: str) -> None:
167
+ """Mark database as being at a specific revision without running migrations.
168
+
169
+ Args:
170
+ revision: The revision to stamp.
171
+ """
172
+ with self.config.provide_session() as driver:
173
+ self.tracker.ensure_tracking_table(driver)
174
+ all_migrations = dict(self.runner.get_migration_files())
175
+ if revision not in all_migrations:
176
+ console.print(f"[red]Unknown revision: {revision}[/]")
177
+ return
178
+ clear_sql = sql.delete().from_(self.tracker.version_table)
179
+ driver.execute(clear_sql)
180
+ self.tracker.record_migration(driver, revision, f"Stamped to {revision}", 0, "manual-stamp")
181
+ console.print(f"[green]Database stamped at revision {revision}[/]")
182
+
183
+ def revision(self, message: str, file_type: str = "sql") -> None:
184
+ """Create a new migration file.
185
+
186
+ Args:
187
+ message: Description for the migration.
188
+ file_type: Type of migration file to create ('sql' or 'py').
189
+ """
190
+ existing = self.runner.get_migration_files()
191
+ next_num = int(existing[-1][0]) + 1 if existing else 1
192
+ next_version = str(next_num).zfill(4)
193
+ file_path = create_migration_file(self.migrations_path, next_version, message, file_type)
194
+ console.print(f"[green]Created migration:[/] {file_path}")
195
+
196
+
197
+ class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
198
+ """SQLSpec native migration commands."""
199
+
200
+ def __init__(self, sqlspec_config: "AsyncConfigT") -> None:
201
+ """Initialize migration commands.
202
+
203
+ Args:
204
+ sqlspec_config: The SQLSpec configuration.
205
+ """
206
+ super().__init__(sqlspec_config)
207
+ self.tracker = AsyncMigrationTracker(self.version_table)
208
+ self.runner = AsyncMigrationRunner(self.migrations_path)
209
+
210
+ async def init(self, directory: str, package: bool = True) -> None:
211
+ """Initialize migration directory structure.
212
+
213
+ Args:
214
+ directory: Directory path for migrations.
215
+ package: Whether to create __init__.py in the directory.
216
+ """
217
+ self.init_directory(directory, package)
218
+
219
+ async def current(self, verbose: bool = False) -> None:
220
+ """Show current migration version.
221
+
222
+ Args:
223
+ verbose: Whether to show detailed migration history.
224
+ """
225
+ async with self.config.provide_session() as driver:
226
+ await self.tracker.ensure_tracking_table(driver)
227
+
228
+ current = await self.tracker.get_current_version(driver)
229
+ if not current:
230
+ console.print("[yellow]No migrations applied yet[/]")
231
+ return
232
+
233
+ console.print(f"[green]Current version:[/] {current}")
234
+ if verbose:
235
+ applied = await self.tracker.get_applied_migrations(driver)
236
+ table = Table(title="Applied Migrations")
237
+ table.add_column("Version", style="cyan")
238
+ table.add_column("Description")
239
+ table.add_column("Applied At")
240
+ table.add_column("Time (ms)", justify="right")
241
+ table.add_column("Applied By")
242
+ for migration in applied:
243
+ table.add_row(
244
+ migration["version_num"],
245
+ migration.get("description", ""),
246
+ str(migration.get("applied_at", "")),
247
+ str(migration.get("execution_time_ms", "")),
248
+ migration.get("applied_by", ""),
249
+ )
250
+ console.print(table)
251
+
252
+ async def upgrade(self, revision: str = "head") -> None:
253
+ """Upgrade to a target revision.
254
+
255
+ Args:
256
+ revision: Target revision or "head" for latest.
257
+ """
258
+ async with self.config.provide_session() as driver:
259
+ await self.tracker.ensure_tracking_table(driver)
260
+
261
+ current = await self.tracker.get_current_version(driver)
262
+ all_migrations = await self.runner.get_migration_files()
263
+ pending = []
264
+ for version, file_path in all_migrations:
265
+ if (current is None or version > current) and (revision == "head" or version <= revision):
266
+ pending.append((version, file_path))
267
+ if not pending:
268
+ console.print("[green]Already at latest version[/]")
269
+ return
270
+ console.print(f"[yellow]Found {len(pending)} pending migrations[/]")
271
+ for version, file_path in pending:
272
+ migration = await self.runner.load_migration(file_path)
273
+ console.print(f"\n[cyan]Applying {version}:[/] {migration['description']}")
274
+ try:
275
+ _, execution_time = await self.runner.execute_upgrade(driver, migration)
276
+ await self.tracker.record_migration(
277
+ driver, migration["version"], migration["description"], execution_time, migration["checksum"]
278
+ )
279
+ console.print(f"[green]✓ Applied in {execution_time}ms[/]")
280
+ except Exception as e:
281
+ console.print(f"[red]✗ Failed: {e}[/]")
282
+ raise
283
+
284
+ async def downgrade(self, revision: str = "-1") -> None:
285
+ """Downgrade to a target revision.
286
+
287
+ Args:
288
+ revision: Target revision or "-1" for one step back.
289
+ """
290
+ async with self.config.provide_session() as driver:
291
+ await self.tracker.ensure_tracking_table(driver)
292
+
293
+ applied = await self.tracker.get_applied_migrations(driver)
294
+ if not applied:
295
+ console.print("[yellow]No migrations to downgrade[/]")
296
+ return
297
+ to_revert = []
298
+ if revision == "-1":
299
+ to_revert = [applied[-1]]
300
+ else:
301
+ for migration in reversed(applied):
302
+ if migration["version_num"] > revision:
303
+ to_revert.append(migration)
304
+ if not to_revert:
305
+ console.print("[yellow]Nothing to downgrade[/]")
306
+ return
307
+
308
+ console.print(f"[yellow]Reverting {len(to_revert)} migrations[/]")
309
+ all_files = dict(await self.runner.get_migration_files())
310
+ for migration_record in to_revert:
311
+ version = migration_record["version_num"]
312
+ if version not in all_files:
313
+ console.print(f"[red]Migration file not found for {version}[/]")
314
+ continue
315
+
316
+ migration = await self.runner.load_migration(all_files[version])
317
+ console.print(f"\n[cyan]Reverting {version}:[/] {migration['description']}")
318
+
319
+ try:
320
+ _, execution_time = await self.runner.execute_downgrade(driver, migration)
321
+ await self.tracker.remove_migration(driver, version)
322
+ console.print(f"[green]✓ Reverted in {execution_time}ms[/]")
323
+ except Exception as e:
324
+ console.print(f"[red]✗ Failed: {e}[/]")
325
+ raise
326
+
327
+ async def stamp(self, revision: str) -> None:
328
+ """Mark database as being at a specific revision without running migrations.
329
+
330
+ Args:
331
+ revision: The revision to stamp.
332
+ """
333
+ async with self.config.provide_session() as driver:
334
+ await self.tracker.ensure_tracking_table(driver)
335
+
336
+ all_migrations = dict(await self.runner.get_migration_files())
337
+ if revision not in all_migrations:
338
+ console.print(f"[red]Unknown revision: {revision}[/]")
339
+ return
340
+
341
+ clear_sql = sql.delete().from_(self.tracker.version_table)
342
+ await driver.execute(clear_sql)
343
+ await self.tracker.record_migration(driver, revision, f"Stamped to {revision}", 0, "manual-stamp")
344
+ console.print(f"[green]Database stamped at revision {revision}[/]")
345
+
346
+ async def revision(self, message: str, file_type: str = "sql") -> None:
347
+ """Create a new migration file.
348
+
349
+ Args:
350
+ message: Description for the migration.
351
+ file_type: Type of migration file to create ('sql' or 'py').
352
+ """
353
+ existing = await self.runner.get_migration_files()
354
+ next_num = int(existing[-1][0]) + 1 if existing else 1
355
+ next_version = str(next_num).zfill(4)
356
+ file_path = create_migration_file(self.migrations_path, next_version, message, file_type)
357
+ console.print(f"[green]Created migration:[/] {file_path}")
358
+
359
+
360
+ class MigrationCommands:
361
+ """Unified migration commands that adapt to sync/async configs."""
362
+
363
+ def __init__(self, config: "Union[SyncConfigT, AsyncConfigT]") -> None:
364
+ """Initialize migration commands with sync/async implementation.
365
+
366
+ Args:
367
+ config: The SQLSpec configuration.
368
+ """
369
+ if config.is_async:
370
+ self._impl: Union[AsyncMigrationCommands[Any], SyncMigrationCommands[Any]] = AsyncMigrationCommands(
371
+ cast("AsyncConfigT", config)
372
+ )
373
+ else:
374
+ self._impl = SyncMigrationCommands(cast("SyncConfigT", config))
375
+ self._is_async = config.is_async
376
+
377
+ def init(self, directory: str, package: bool = True) -> None:
378
+ """Initialize migration directory structure.
379
+
380
+ Args:
381
+ directory: Directory to initialize migrations in.
382
+ package: Whether to create __init__.py file.
383
+ """
384
+ if self._is_async:
385
+ await_(cast("AsyncMigrationCommands[Any]", self._impl).init)(directory, package=package)
386
+ else:
387
+ cast("SyncMigrationCommands[Any]", self._impl).init(directory, package=package)
388
+
389
+ def current(self, verbose: bool = False) -> None:
390
+ """Show current migration version.
391
+
392
+ Args:
393
+ verbose: Whether to show detailed migration history.
394
+ """
395
+ if self._is_async:
396
+ await_(cast("AsyncMigrationCommands[Any]", self._impl).current, raise_sync_error=False)(verbose=verbose)
397
+ else:
398
+ cast("SyncMigrationCommands[Any]", self._impl).current(verbose=verbose)
399
+
400
+ def upgrade(self, revision: str = "head") -> None:
401
+ """Upgrade to a target revision.
402
+
403
+ Args:
404
+ revision: Target revision or "head" for latest.
405
+ """
406
+ if self._is_async:
407
+ await_(cast("AsyncMigrationCommands[Any]", self._impl).upgrade, raise_sync_error=False)(revision=revision)
408
+ else:
409
+ cast("SyncMigrationCommands[Any]", self._impl).upgrade(revision=revision)
410
+
411
+ def downgrade(self, revision: str = "-1") -> None:
412
+ """Downgrade to a target revision.
413
+
414
+ Args:
415
+ revision: Target revision or "-1" for one step back.
416
+ """
417
+ if self._is_async:
418
+ await_(cast("AsyncMigrationCommands[Any]", self._impl).downgrade, raise_sync_error=False)(revision=revision)
419
+ else:
420
+ cast("SyncMigrationCommands[Any]", self._impl).downgrade(revision=revision)
421
+
422
+ def stamp(self, revision: str) -> None:
423
+ """Mark database as being at a specific revision without running migrations.
424
+
425
+ Args:
426
+ revision: The revision to stamp.
427
+ """
428
+ if self._is_async:
429
+ await_(cast("AsyncMigrationCommands[Any]", self._impl).stamp, raise_sync_error=False)(revision)
430
+ else:
431
+ cast("SyncMigrationCommands[Any]", self._impl).stamp(revision)
432
+
433
+ def revision(self, message: str, file_type: str = "sql") -> None:
434
+ """Create a new migration file.
435
+
436
+ Args:
437
+ message: Description for the migration.
438
+ file_type: Type of migration file to create ('sql' or 'py').
439
+ """
440
+ if self._is_async:
441
+ await_(cast("AsyncMigrationCommands[Any]", self._impl).revision, raise_sync_error=False)(message, file_type)
442
+ else:
443
+ cast("SyncMigrationCommands[Any]", self._impl).revision(message, file_type)