0
# Script Management
1
2
Classes and utilities for managing migration scripts, revision tracking, and dependency graphs. The script management system handles migration file generation, versioning, and execution ordering.
3
4
## Core Imports
5
6
```python
7
from alembic.script import ScriptDirectory, Script
8
from alembic.script.revision import RevisionMap
9
```
10
11
## Capabilities
12
13
### Script Directory Management
14
15
Main class for managing the migration script directory and its contents.
16
17
```python { .api }
18
class ScriptDirectory:
19
def __init__(self, dir, file_template=None, truncate_slug_length=40, version_locations=None, sourceless=False, output_encoding='utf-8', timezone=None, hook=None, env_py_location=None):
20
"""
21
Initialize script directory manager.
22
23
Args:
24
dir (str): Path to script directory
25
file_template (str): Template for migration file names
26
truncate_slug_length (int): Maximum length for revision slugs
27
version_locations (list): Additional version file locations
28
sourceless (bool): Whether to support .pyc-only migrations
29
output_encoding (str): Encoding for generated files
30
timezone (str): Timezone for timestamps
31
hook (callable): Custom hook function
32
env_py_location (str): Path to env.py file
33
"""
34
35
@classmethod
36
def from_config(cls, config):
37
"""
38
Create ScriptDirectory from Alembic configuration.
39
40
Args:
41
config (Config): Alembic configuration object
42
43
Returns:
44
ScriptDirectory: Configured script directory
45
"""
46
47
def get_revision(self, id_):
48
"""
49
Get a revision by ID.
50
51
Args:
52
id_ (str): Revision identifier
53
54
Returns:
55
Script: Script object for the revision
56
"""
57
58
def get_revisions(self, id_):
59
"""
60
Get multiple revisions by ID pattern.
61
62
Args:
63
id_ (str): Revision identifier or pattern
64
65
Returns:
66
tuple: Tuple of Script objects
67
"""
68
69
def get_all_current(self, id_):
70
"""
71
Get all current head revisions.
72
73
Args:
74
id_ (str): Starting revision identifier
75
76
Returns:
77
tuple: Tuple of current head revisions
78
"""
79
80
def get_heads(self):
81
"""
82
Get all head revisions.
83
84
Returns:
85
tuple: Tuple of head revision identifiers
86
"""
87
88
def get_base(self):
89
"""
90
Get base revision identifier.
91
92
Returns:
93
str: Base revision identifier
94
"""
95
96
def walk_revisions(self, base='base', head='heads'):
97
"""
98
Walk through revisions from base to head.
99
100
Args:
101
base (str): Starting revision
102
head (str): Ending revision
103
104
Yields:
105
Script: Script objects in dependency order
106
"""
107
108
def get_current_head(self):
109
"""
110
Get the current single head revision.
111
112
Returns:
113
str: Head revision identifier
114
115
Raises:
116
CommandError: If multiple heads exist
117
"""
118
```
119
120
**Usage Examples**:
121
```python
122
from alembic.script import ScriptDirectory
123
from alembic.config import Config
124
125
# Create from configuration
126
config = Config('alembic.ini')
127
script_dir = ScriptDirectory.from_config(config)
128
129
# Get revision information
130
latest_rev = script_dir.get_current_head()
131
all_heads = script_dir.get_heads()
132
133
# Walk through revision history
134
for script in script_dir.walk_revisions():
135
print(f"Revision: {script.revision}, Message: {script.doc}")
136
137
# Get specific revision
138
rev_abc123 = script_dir.get_revision('abc123')
139
print(f"Down revision: {rev_abc123.down_revision}")
140
```
141
142
### Revision Generation
143
144
Generate new migration script files.
145
146
```python { .api }
147
class ScriptDirectory:
148
def generate_revision(self, revid, message, refresh=False, head='head', splice=False, branch_labels=None, version_path=None, depends_on=None, **kw):
149
"""
150
Generate a new revision script.
151
152
Args:
153
revid (str): Revision identifier
154
message (str): Revision message
155
refresh (bool): Refresh revision map
156
head (str): Head revision to base on
157
splice (bool): Allow revision to be spliced
158
branch_labels (list): Branch labels for revision
159
version_path (str): Path for version files
160
depends_on (list): Dependencies for revision
161
**kw: Additional template variables
162
163
Returns:
164
Script: Generated script object
165
"""
166
167
def run_env(self):
168
"""
169
Execute the env.py script.
170
171
Returns:
172
dict: Environment module's globals
173
"""
174
175
def get_upgrade_token(self):
176
"""
177
Get the upgrade token from env.py.
178
179
Returns:
180
str: Upgrade token identifier
181
"""
182
183
def get_downgrade_token(self):
184
"""
185
Get the downgrade token from env.py.
186
187
Returns:
188
str: Downgrade token identifier
189
"""
190
```
191
192
**Usage Examples**:
193
```python
194
# Generate new revision
195
script = script_dir.generate_revision(
196
revid='abc123',
197
message='Create user table',
198
head='head',
199
branch_labels=['feature']
200
)
201
202
# Generate with dependencies
203
script = script_dir.generate_revision(
204
revid='def456',
205
message='Add indexes',
206
depends_on=['abc123']
207
)
208
209
# Generate with custom template variables
210
script = script_dir.generate_revision(
211
revid='ghi789',
212
message='Custom migration',
213
author='John Doe',
214
created_by='AutoGen'
215
)
216
```
217
218
### Script Objects
219
220
Individual migration script representation.
221
222
```python { .api }
223
class Script:
224
def __init__(self, module, rev_id, path):
225
"""
226
Initialize script object.
227
228
Args:
229
module: Python module containing the migration
230
rev_id (str): Revision identifier
231
path (str): File path to script
232
"""
233
234
# Properties
235
revision: str # Revision identifier
236
down_revision: Optional[str] # Previous revision
237
branch_labels: Optional[Set[str]] # Branch labels
238
depends_on: Optional[Set[str]] # Dependencies
239
path: str # File path
240
doc: str # Revision message
241
module: ModuleType # Python module
242
243
def get_path(self):
244
"""
245
Get the file path for this script.
246
247
Returns:
248
str: Path to script file
249
"""
250
251
def nextrev(self):
252
"""
253
Get the next revision in the chain.
254
255
Returns:
256
str: Next revision identifier
257
"""
258
259
def is_base(self):
260
"""
261
Check if this is a base revision.
262
263
Returns:
264
bool: True if base revision
265
"""
266
267
def is_head(self):
268
"""
269
Check if this is a head revision.
270
271
Returns:
272
bool: True if head revision
273
"""
274
275
def is_merge_point(self):
276
"""
277
Check if this is a merge point.
278
279
Returns:
280
bool: True if merge point
281
"""
282
283
def is_branch_point(self):
284
"""
285
Check if this is a branch point.
286
287
Returns:
288
bool: True if branch point
289
"""
290
```
291
292
**Usage Examples**:
293
```python
294
# Access script properties
295
script = script_dir.get_revision('abc123')
296
print(f"Revision: {script.revision}")
297
print(f"Message: {script.doc}")
298
print(f"Path: {script.path}")
299
print(f"Down revision: {script.down_revision}")
300
print(f"Branch labels: {script.branch_labels}")
301
302
# Check script status
303
if script.is_head():
304
print("This is a head revision")
305
if script.is_merge_point():
306
print("This is a merge point")
307
```
308
309
### Revision Maps
310
311
Manage revision dependency graphs and navigation.
312
313
```python { .api }
314
class RevisionMap:
315
def __init__(self, generator):
316
"""
317
Initialize revision map.
318
319
Args:
320
generator: Generator function yielding Script objects
321
"""
322
323
def get_revision(self, id_):
324
"""
325
Get revision by identifier.
326
327
Args:
328
id_ (str): Revision identifier
329
330
Returns:
331
Script: Script object
332
"""
333
334
def get_revisions(self, id_):
335
"""
336
Get multiple revisions by identifier.
337
338
Args:
339
id_ (str): Revision identifier pattern
340
341
Returns:
342
tuple: Tuple of Script objects
343
"""
344
345
def iterate_revisions(self, upper, lower):
346
"""
347
Iterate revisions between upper and lower bounds.
348
349
Args:
350
upper (str): Upper bound revision
351
lower (str): Lower bound revision
352
353
Yields:
354
Script: Script objects in order
355
"""
356
357
def get_heads(self):
358
"""
359
Get all head revision identifiers.
360
361
Returns:
362
tuple: Head revision identifiers
363
"""
364
365
def get_bases(self):
366
"""
367
Get all base revision identifiers.
368
369
Returns:
370
tuple: Base revision identifiers
371
"""
372
```
373
374
## Version Locations
375
376
Manage multiple locations for migration scripts.
377
378
```python
379
# Configure multiple version locations
380
script_dir = ScriptDirectory(
381
'migrations',
382
version_locations=[
383
'migrations/versions', # Default location
384
'migrations/feature', # Feature branch migrations
385
'migrations/hotfix' # Hotfix migrations
386
]
387
)
388
389
# Generate revision in specific location
390
script = script_dir.generate_revision(
391
revid='feature_001',
392
message='Feature migration',
393
version_path='migrations/feature'
394
)
395
```
396
397
## Branching and Merging
398
399
### Branch Management
400
401
```python
402
# Create branch revision
403
branch_script = script_dir.generate_revision(
404
revid='branch_001',
405
message='Start feature branch',
406
head='abc123', # Branch from this revision
407
branch_labels=['feature_x']
408
)
409
410
# Create merge revision
411
merge_script = script_dir.generate_revision(
412
revid='merge_001',
413
message='Merge feature branch',
414
head=['head', 'feature_x'] # Merge multiple heads
415
)
416
```
417
418
#### Dependency Management
419
420
```python
421
# Create revision with dependencies
422
dependent_script = script_dir.generate_revision(
423
revid='dep_001',
424
message='Migration with dependencies',
425
depends_on=['abc123', 'def456'] # Depends on these revisions
426
)
427
428
# Check dependencies
429
for dep in dependent_script.depends_on:
430
dep_script = script_dir.get_revision(dep)
431
print(f"Depends on: {dep_script.doc}")
432
```
433
434
## Template Customization
435
436
### Custom File Templates
437
438
```python
439
# Custom template with additional variables
440
custom_template = '''"""${message}
441
442
Revision ID: ${up_revision}
443
Revises: ${down_revision | comma,n}
444
Create Date: ${create_date}
445
Author: ${author}
446
Branch: ${branch_name}
447
"""
448
449
from alembic import op
450
import sqlalchemy as sa
451
${imports if imports else ""}
452
453
# revision identifiers, used by Alembic.
454
revision = ${repr(up_revision)}
455
down_revision = ${repr(down_revision)}
456
branch_labels = ${repr(branch_labels)}
457
depends_on = ${repr(depends_on)}
458
459
def upgrade():
460
${upgrades if upgrades else "pass"}
461
462
def downgrade():
463
${downgrades if downgrades else "pass"}
464
'''
465
466
# Use custom template
467
script_dir = ScriptDirectory(
468
'migrations',
469
file_template=custom_template
470
)
471
```
472
473
### Template Variables
474
475
Available variables in migration templates:
476
477
- `${message}`: Revision message
478
- `${up_revision}`: Revision identifier
479
- `${down_revision}`: Previous revision identifier
480
- `${create_date}`: Creation timestamp
481
- `${branch_labels}`: Branch labels
482
- `${depends_on}`: Dependencies
483
- `${upgrades}`: Upgrade operations (autogenerate)
484
- `${downgrades}`: Downgrade operations (autogenerate)
485
- `${imports}`: Required imports (autogenerate)
486
487
## Advanced Features
488
489
### Sourceless Migrations
490
491
Run migrations from compiled .pyc files without .py sources.
492
493
```python
494
# Configure sourceless mode
495
script_dir = ScriptDirectory(
496
'migrations',
497
sourceless=True # Support .pyc-only migrations
498
)
499
```
500
501
### Custom Hook Functions
502
503
Execute custom logic during script operations.
504
505
```python
506
def custom_hook(rev, context):
507
"""Custom hook function."""
508
print(f"Processing revision: {rev}")
509
# Custom validation or processing logic
510
511
script_dir = ScriptDirectory(
512
'migrations',
513
hook=custom_hook
514
)
515
```
516
517
### Environment Integration
518
519
```python
520
# Custom env.py location
521
script_dir = ScriptDirectory(
522
'migrations',
523
env_py_location='custom/env.py'
524
)
525
526
# Execute environment script
527
env_globals = script_dir.run_env()
528
target_metadata = env_globals.get('target_metadata')
529
```
530
531
## Error Handling
532
533
Script management operations may raise:
534
- `RevisionError`: Revision identifier conflicts or invalid references
535
- `MultipleHeads`: Multiple head revisions when single expected
536
- `CommandError`: General script management errors
537
- `FileNotFoundError`: Missing script files or directories
538
539
## Integration Examples
540
541
### Web Application Integration
542
543
```python
544
from alembic.script import ScriptDirectory
545
from alembic.config import Config
546
547
class MigrationManager:
548
def __init__(self, config_path):
549
self.config = Config(config_path)
550
self.script_dir = ScriptDirectory.from_config(self.config)
551
552
def get_migration_status(self):
553
"""Get current migration status."""
554
current_heads = self.script_dir.get_heads()
555
return {
556
'heads': current_heads,
557
'is_current': len(current_heads) == 1
558
}
559
560
def list_migrations(self):
561
"""List all migrations."""
562
migrations = []
563
for script in self.script_dir.walk_revisions():
564
migrations.append({
565
'revision': script.revision,
566
'message': script.doc,
567
'down_revision': script.down_revision
568
})
569
return migrations
570
```
571
572
### Automated Migration Generation
573
574
```python
575
def auto_generate_migration(app_metadata, message):
576
"""Generate migration automatically."""
577
config = Config('alembic.ini')
578
script_dir = ScriptDirectory.from_config(config)
579
580
# Generate unique revision ID
581
import uuid
582
rev_id = str(uuid.uuid4())[:12]
583
584
# Generate migration script
585
script = script_dir.generate_revision(
586
revid=rev_id,
587
message=message,
588
autogenerate=True,
589
target_metadata=app_metadata
590
)
591
592
return script
593
```
594
595
## Types
596
597
```python { .api }
598
# Script management types
599
class Script:
600
revision: str
601
down_revision: Optional[str]
602
branch_labels: Optional[Set[str]]
603
depends_on: Optional[Set[str]]
604
path: str
605
doc: str
606
module: ModuleType
607
608
class ScriptDirectory:
609
dir: str
610
file_template: str
611
truncate_slug_length: int
612
version_locations: Optional[List[str]]
613
614
class RevisionMap:
615
map: Dict[str, Script]
616
617
# Hook function type
618
HookFunc = Callable[[str, Any], None]
619
```