Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/alembic/command.py : 10%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1import os
3from . import autogenerate as autogen
4from . import util
5from .runtime.environment import EnvironmentContext
6from .script import ScriptDirectory
9def list_templates(config):
10 """List available templates.
12 :param config: a :class:`.Config` object.
14 """
16 config.print_stdout("Available templates:\n")
17 for tempname in os.listdir(config.get_template_directory()):
18 with open(
19 os.path.join(config.get_template_directory(), tempname, "README")
20 ) as readme:
21 synopsis = next(readme)
22 config.print_stdout("%s - %s", tempname, synopsis)
24 config.print_stdout("\nTemplates are used via the 'init' command, e.g.:")
25 config.print_stdout("\n alembic init --template generic ./scripts")
28def init(config, directory, template="generic", package=False):
29 """Initialize a new scripts directory.
31 :param config: a :class:`.Config` object.
33 :param directory: string path of the target directory
35 :param template: string name of the migration environment template to
36 use.
38 :param package: when True, write ``__init__.py`` files into the
39 environment location as well as the versions/ location.
41 .. versionadded:: 1.2
44 """
46 if os.access(directory, os.F_OK) and os.listdir(directory):
47 raise util.CommandError(
48 "Directory %s already exists and is not empty" % directory
49 )
51 template_dir = os.path.join(config.get_template_directory(), template)
52 if not os.access(template_dir, os.F_OK):
53 raise util.CommandError("No such template %r" % template)
55 if not os.access(directory, os.F_OK):
56 util.status(
57 "Creating directory %s" % os.path.abspath(directory),
58 os.makedirs,
59 directory,
60 )
62 versions = os.path.join(directory, "versions")
63 util.status(
64 "Creating directory %s" % os.path.abspath(versions),
65 os.makedirs,
66 versions,
67 )
69 script = ScriptDirectory(directory)
71 for file_ in os.listdir(template_dir):
72 file_path = os.path.join(template_dir, file_)
73 if file_ == "alembic.ini.mako":
74 config_file = os.path.abspath(config.config_file_name)
75 if os.access(config_file, os.F_OK):
76 util.msg("File %s already exists, skipping" % config_file)
77 else:
78 script._generate_template(
79 file_path, config_file, script_location=directory
80 )
81 elif os.path.isfile(file_path):
82 output_file = os.path.join(directory, file_)
83 script._copy_file(file_path, output_file)
85 if package:
86 for path in [
87 os.path.join(os.path.abspath(directory), "__init__.py"),
88 os.path.join(os.path.abspath(versions), "__init__.py"),
89 ]:
90 file_ = util.status("Adding %s" % path, open, path, "w")
91 file_.close()
93 util.msg(
94 "Please edit configuration/connection/logging "
95 "settings in %r before proceeding." % config_file
96 )
99def revision(
100 config,
101 message=None,
102 autogenerate=False,
103 sql=False,
104 head="head",
105 splice=False,
106 branch_label=None,
107 version_path=None,
108 rev_id=None,
109 depends_on=None,
110 process_revision_directives=None,
111):
112 """Create a new revision file.
114 :param config: a :class:`.Config` object.
116 :param message: string message to apply to the revision; this is the
117 ``-m`` option to ``alembic revision``.
119 :param autogenerate: whether or not to autogenerate the script from
120 the database; this is the ``--autogenerate`` option to
121 ``alembic revision``.
123 :param sql: whether to dump the script out as a SQL string; when specified,
124 the script is dumped to stdout. This is the ``--sql`` option to
125 ``alembic revision``.
127 :param head: head revision to build the new revision upon as a parent;
128 this is the ``--head`` option to ``alembic revision``.
130 :param splice: whether or not the new revision should be made into a
131 new head of its own; is required when the given ``head`` is not itself
132 a head. This is the ``--splice`` option to ``alembic revision``.
134 :param branch_label: string label to apply to the branch; this is the
135 ``--branch-label`` option to ``alembic revision``.
137 :param version_path: string symbol identifying a specific version path
138 from the configuration; this is the ``--version-path`` option to
139 ``alembic revision``.
141 :param rev_id: optional revision identifier to use instead of having
142 one generated; this is the ``--rev-id`` option to ``alembic revision``.
144 :param depends_on: optional list of "depends on" identifiers; this is the
145 ``--depends-on`` option to ``alembic revision``.
147 :param process_revision_directives: this is a callable that takes the
148 same form as the callable described at
149 :paramref:`.EnvironmentContext.configure.process_revision_directives`;
150 will be applied to the structure generated by the revision process
151 where it can be altered programmatically. Note that unlike all
152 the other parameters, this option is only available via programmatic
153 use of :func:`.command.revision`
155 .. versionadded:: 0.9.0
157 """
159 script_directory = ScriptDirectory.from_config(config)
161 command_args = dict(
162 message=message,
163 autogenerate=autogenerate,
164 sql=sql,
165 head=head,
166 splice=splice,
167 branch_label=branch_label,
168 version_path=version_path,
169 rev_id=rev_id,
170 depends_on=depends_on,
171 )
172 revision_context = autogen.RevisionContext(
173 config,
174 script_directory,
175 command_args,
176 process_revision_directives=process_revision_directives,
177 )
179 environment = util.asbool(config.get_main_option("revision_environment"))
181 if autogenerate:
182 environment = True
184 if sql:
185 raise util.CommandError(
186 "Using --sql with --autogenerate does not make any sense"
187 )
189 def retrieve_migrations(rev, context):
190 revision_context.run_autogenerate(rev, context)
191 return []
193 elif environment:
195 def retrieve_migrations(rev, context):
196 revision_context.run_no_autogenerate(rev, context)
197 return []
199 elif sql:
200 raise util.CommandError(
201 "Using --sql with the revision command when "
202 "revision_environment is not configured does not make any sense"
203 )
205 if environment:
206 with EnvironmentContext(
207 config,
208 script_directory,
209 fn=retrieve_migrations,
210 as_sql=sql,
211 template_args=revision_context.template_args,
212 revision_context=revision_context,
213 ):
214 script_directory.run_env()
216 # the revision_context now has MigrationScript structure(s) present.
217 # these could theoretically be further processed / rewritten *here*,
218 # in addition to the hooks present within each run_migrations() call,
219 # or at the end of env.py run_migrations_online().
221 scripts = [script for script in revision_context.generate_scripts()]
222 if len(scripts) == 1:
223 return scripts[0]
224 else:
225 return scripts
228def merge(config, revisions, message=None, branch_label=None, rev_id=None):
229 """Merge two revisions together. Creates a new migration file.
231 .. versionadded:: 0.7.0
233 :param config: a :class:`.Config` instance
235 :param message: string message to apply to the revision
237 :param branch_label: string label name to apply to the new revision
239 :param rev_id: hardcoded revision identifier instead of generating a new
240 one.
242 .. seealso::
244 :ref:`branches`
246 """
248 script = ScriptDirectory.from_config(config)
249 template_args = {
250 "config": config # Let templates use config for
251 # e.g. multiple databases
252 }
253 return script.generate_revision(
254 rev_id or util.rev_id(),
255 message,
256 refresh=True,
257 head=revisions,
258 branch_labels=branch_label,
259 **template_args
260 )
263def upgrade(config, revision, sql=False, tag=None):
264 """Upgrade to a later version.
266 :param config: a :class:`.Config` instance.
268 :param revision: string revision target or range for --sql mode
270 :param sql: if True, use ``--sql`` mode
272 :param tag: an arbitrary "tag" that can be intercepted by custom
273 ``env.py`` scripts via the :meth:`.EnvironmentContext.get_tag_argument`
274 method.
276 """
278 script = ScriptDirectory.from_config(config)
280 starting_rev = None
281 if ":" in revision:
282 if not sql:
283 raise util.CommandError("Range revision not allowed")
284 starting_rev, revision = revision.split(":", 2)
286 def upgrade(rev, context):
287 return script._upgrade_revs(revision, rev)
289 with EnvironmentContext(
290 config,
291 script,
292 fn=upgrade,
293 as_sql=sql,
294 starting_rev=starting_rev,
295 destination_rev=revision,
296 tag=tag,
297 ):
298 script.run_env()
301def downgrade(config, revision, sql=False, tag=None):
302 """Revert to a previous version.
304 :param config: a :class:`.Config` instance.
306 :param revision: string revision target or range for --sql mode
308 :param sql: if True, use ``--sql`` mode
310 :param tag: an arbitrary "tag" that can be intercepted by custom
311 ``env.py`` scripts via the :meth:`.EnvironmentContext.get_tag_argument`
312 method.
314 """
316 script = ScriptDirectory.from_config(config)
317 starting_rev = None
318 if ":" in revision:
319 if not sql:
320 raise util.CommandError("Range revision not allowed")
321 starting_rev, revision = revision.split(":", 2)
322 elif sql:
323 raise util.CommandError(
324 "downgrade with --sql requires <fromrev>:<torev>"
325 )
327 def downgrade(rev, context):
328 return script._downgrade_revs(revision, rev)
330 with EnvironmentContext(
331 config,
332 script,
333 fn=downgrade,
334 as_sql=sql,
335 starting_rev=starting_rev,
336 destination_rev=revision,
337 tag=tag,
338 ):
339 script.run_env()
342def show(config, rev):
343 """Show the revision(s) denoted by the given symbol.
345 :param config: a :class:`.Config` instance.
347 :param revision: string revision target
349 """
351 script = ScriptDirectory.from_config(config)
353 if rev == "current":
355 def show_current(rev, context):
356 for sc in script.get_revisions(rev):
357 config.print_stdout(sc.log_entry)
358 return []
360 with EnvironmentContext(config, script, fn=show_current):
361 script.run_env()
362 else:
363 for sc in script.get_revisions(rev):
364 config.print_stdout(sc.log_entry)
367def history(config, rev_range=None, verbose=False, indicate_current=False):
368 """List changeset scripts in chronological order.
370 :param config: a :class:`.Config` instance.
372 :param rev_range: string revision range
374 :param verbose: output in verbose mode.
376 :param indicate_current: indicate current revision.
378 ..versionadded:: 0.9.9
380 """
382 script = ScriptDirectory.from_config(config)
383 if rev_range is not None:
384 if ":" not in rev_range:
385 raise util.CommandError(
386 "History range requires [start]:[end], " "[start]:, or :[end]"
387 )
388 base, head = rev_range.strip().split(":")
389 else:
390 base = head = None
392 environment = (
393 util.asbool(config.get_main_option("revision_environment"))
394 or indicate_current
395 )
397 def _display_history(config, script, base, head, currents=()):
398 for sc in script.walk_revisions(
399 base=base or "base", head=head or "heads"
400 ):
402 if indicate_current:
403 sc._db_current_indicator = sc.revision in currents
405 config.print_stdout(
406 sc.cmd_format(
407 verbose=verbose,
408 include_branches=True,
409 include_doc=True,
410 include_parents=True,
411 )
412 )
414 def _display_history_w_current(config, script, base, head):
415 def _display_current_history(rev, context):
416 if head == "current":
417 _display_history(config, script, base, rev, rev)
418 elif base == "current":
419 _display_history(config, script, rev, head, rev)
420 else:
421 _display_history(config, script, base, head, rev)
422 return []
424 with EnvironmentContext(config, script, fn=_display_current_history):
425 script.run_env()
427 if base == "current" or head == "current" or environment:
428 _display_history_w_current(config, script, base, head)
429 else:
430 _display_history(config, script, base, head)
433def heads(config, verbose=False, resolve_dependencies=False):
434 """Show current available heads in the script directory.
436 :param config: a :class:`.Config` instance.
438 :param verbose: output in verbose mode.
440 :param resolve_dependencies: treat dependency version as down revisions.
442 """
444 script = ScriptDirectory.from_config(config)
445 if resolve_dependencies:
446 heads = script.get_revisions("heads")
447 else:
448 heads = script.get_revisions(script.get_heads())
450 for rev in heads:
451 config.print_stdout(
452 rev.cmd_format(
453 verbose, include_branches=True, tree_indicators=False
454 )
455 )
458def branches(config, verbose=False):
459 """Show current branch points.
461 :param config: a :class:`.Config` instance.
463 :param verbose: output in verbose mode.
465 """
466 script = ScriptDirectory.from_config(config)
467 for sc in script.walk_revisions():
468 if sc.is_branch_point:
469 config.print_stdout(
470 "%s\n%s\n",
471 sc.cmd_format(verbose, include_branches=True),
472 "\n".join(
473 "%s -> %s"
474 % (
475 " " * len(str(sc.revision)),
476 rev_obj.cmd_format(
477 False, include_branches=True, include_doc=verbose
478 ),
479 )
480 for rev_obj in (
481 script.get_revision(rev) for rev in sc.nextrev
482 )
483 ),
484 )
487def current(config, verbose=False, head_only=False):
488 """Display the current revision for a database.
490 :param config: a :class:`.Config` instance.
492 :param verbose: output in verbose mode.
494 :param head_only: deprecated; use ``verbose`` for additional output.
496 """
498 script = ScriptDirectory.from_config(config)
500 if head_only:
501 util.warn("--head-only is deprecated", stacklevel=3)
503 def display_version(rev, context):
504 if verbose:
505 config.print_stdout(
506 "Current revision(s) for %s:",
507 util.obfuscate_url_pw(context.connection.engine.url),
508 )
509 for rev in script.get_all_current(rev):
510 config.print_stdout(rev.cmd_format(verbose))
512 return []
514 with EnvironmentContext(config, script, fn=display_version):
515 script.run_env()
518def stamp(config, revision, sql=False, tag=None, purge=False):
519 """'stamp' the revision table with the given revision; don't
520 run any migrations.
522 :param config: a :class:`.Config` instance.
524 :param revision: target revision or list of revisions. May be a list
525 to indicate stamping of multiple branch heads.
527 .. note:: this parameter is called "revisions" in the command line
528 interface.
530 .. versionchanged:: 1.2 The revision may be a single revision or
531 list of revisions when stamping multiple branch heads.
533 :param sql: use ``--sql`` mode
535 :param tag: an arbitrary "tag" that can be intercepted by custom
536 ``env.py`` scripts via the :class:`.EnvironmentContext.get_tag_argument`
537 method.
539 :param purge: delete all entries in the version table before stamping.
541 .. versionadded:: 1.2
543 """
545 script = ScriptDirectory.from_config(config)
547 if sql:
548 destination_revs = []
549 starting_rev = None
550 for _revision in util.to_list(revision):
551 if ":" in _revision:
552 srev, _revision = _revision.split(":", 2)
554 if starting_rev != srev:
555 if starting_rev is None:
556 starting_rev = srev
557 else:
558 raise util.CommandError(
559 "Stamp operation with --sql only supports a "
560 "single starting revision at a time"
561 )
562 destination_revs.append(_revision)
563 else:
564 destination_revs = util.to_list(revision)
566 def do_stamp(rev, context):
567 return script._stamp_revs(util.to_tuple(destination_revs), rev)
569 with EnvironmentContext(
570 config,
571 script,
572 fn=do_stamp,
573 as_sql=sql,
574 starting_rev=starting_rev if sql else None,
575 destination_rev=util.to_tuple(destination_revs),
576 tag=tag,
577 purge=purge,
578 ):
579 script.run_env()
582def edit(config, rev):
583 """Edit revision script(s) using $EDITOR.
585 :param config: a :class:`.Config` instance.
587 :param rev: target revision.
589 """
591 script = ScriptDirectory.from_config(config)
593 if rev == "current":
595 def edit_current(rev, context):
596 if not rev:
597 raise util.CommandError("No current revisions")
598 for sc in script.get_revisions(rev):
599 util.edit(sc.path)
600 return []
602 with EnvironmentContext(config, script, fn=edit_current):
603 script.run_env()
604 else:
605 revs = script.get_revisions(rev)
606 if not revs:
607 raise util.CommandError(
608 "No revision files indicated by symbol '%s'" % rev
609 )
610 for sc in revs:
611 util.edit(sc.path)