123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530 |
- import os
- from .script import ScriptDirectory
- from .runtime.environment import EnvironmentContext
- from . import util
- from . import autogenerate as autogen
- def list_templates(config):
- """List available templates
- :param config: a :class:`.Config` object.
- """
- config.print_stdout("Available templates:\n")
- for tempname in os.listdir(config.get_template_directory()):
- with open(os.path.join(
- config.get_template_directory(),
- tempname,
- 'README')) as readme:
- synopsis = next(readme)
- config.print_stdout("%s - %s", tempname, synopsis)
- config.print_stdout("\nTemplates are used via the 'init' command, e.g.:")
- config.print_stdout("\n alembic init --template generic ./scripts")
- def init(config, directory, template='generic'):
- """Initialize a new scripts directory.
- :param config: a :class:`.Config` object.
- :param directory: string path of the target directory
- :param template: string name of the migration environment template to
- use.
- """
- if os.access(directory, os.F_OK):
- raise util.CommandError("Directory %s already exists" % directory)
- template_dir = os.path.join(config.get_template_directory(),
- template)
- if not os.access(template_dir, os.F_OK):
- raise util.CommandError("No such template %r" % template)
- util.status("Creating directory %s" % os.path.abspath(directory),
- os.makedirs, directory)
- versions = os.path.join(directory, 'versions')
- util.status("Creating directory %s" % os.path.abspath(versions),
- os.makedirs, versions)
- script = ScriptDirectory(directory)
- for file_ in os.listdir(template_dir):
- file_path = os.path.join(template_dir, file_)
- if file_ == 'alembic.ini.mako':
- config_file = os.path.abspath(config.config_file_name)
- if os.access(config_file, os.F_OK):
- util.msg("File %s already exists, skipping" % config_file)
- else:
- script._generate_template(
- file_path,
- config_file,
- script_location=directory
- )
- elif os.path.isfile(file_path):
- output_file = os.path.join(directory, file_)
- script._copy_file(
- file_path,
- output_file
- )
- util.msg("Please edit configuration/connection/logging "
- "settings in %r before proceeding." % config_file)
- def revision(
- config, message=None, autogenerate=False, sql=False,
- head="head", splice=False, branch_label=None,
- version_path=None, rev_id=None, depends_on=None,
- process_revision_directives=None):
- """Create a new revision file.
- :param config: a :class:`.Config` object.
- :param message: string message to apply to the revision; this is the
- ``-m`` option to ``alembic revision``.
- :param autogenerate: whether or not to autogenerate the script from
- the database; this is the ``--autogenerate`` option to ``alembic revision``.
- :param sql: whether to dump the script out as a SQL string; when specified,
- the script is dumped to stdout. This is the ``--sql`` option to
- ``alembic revision``.
- :param head: head revision to build the new revision upon as a parent;
- this is the ``--head`` option to ``alembic revision``.
- :param splice: whether or not the new revision should be made into a
- new head of its own; is required when the given ``head`` is not itself
- a head. This is the ``--splice`` option to ``alembic revision``.
- :param branch_label: string label to apply to the branch; this is the
- ``--branch-label`` option to ``alembic revision``.
- :param version_path: string symbol identifying a specific version path
- from the configuration; this is the ``--version-path`` option to
- ``alembic revision``.
- :param rev_id: optional revision identifier to use instead of having
- one generated; this is the ``--rev-id`` option to ``alembic revision``.
- :param depends_on: optional list of "depends on" identifiers; this is the
- ``--depends-on`` option to ``alembic revision``.
- :param process_revision_directives: this is a callable that takes the
- same form as the callable described at
- :paramref:`.EnvironmentContext.configure.process_revision_directives`;
- will be applied to the structure generated by the revision process
- where it can be altered programmatically. Note that unlike all
- the other parameters, this option is only available via programmatic
- use of :func:`.command.revision`
- .. versionadded:: 0.9.0
- """
- script_directory = ScriptDirectory.from_config(config)
- command_args = dict(
- message=message,
- autogenerate=autogenerate,
- sql=sql, head=head, splice=splice, branch_label=branch_label,
- version_path=version_path, rev_id=rev_id, depends_on=depends_on
- )
- revision_context = autogen.RevisionContext(
- config, script_directory, command_args,
- process_revision_directives=process_revision_directives)
- environment = util.asbool(
- config.get_main_option("revision_environment")
- )
- if autogenerate:
- environment = True
- if sql:
- raise util.CommandError(
- "Using --sql with --autogenerate does not make any sense")
- def retrieve_migrations(rev, context):
- revision_context.run_autogenerate(rev, context)
- return []
- elif environment:
- def retrieve_migrations(rev, context):
- revision_context.run_no_autogenerate(rev, context)
- return []
- elif sql:
- raise util.CommandError(
- "Using --sql with the revision command when "
- "revision_environment is not configured does not make any sense")
- if environment:
- with EnvironmentContext(
- config,
- script_directory,
- fn=retrieve_migrations,
- as_sql=sql,
- template_args=revision_context.template_args,
- revision_context=revision_context
- ):
- script_directory.run_env()
- scripts = [
- script for script in
- revision_context.generate_scripts()
- ]
- if len(scripts) == 1:
- return scripts[0]
- else:
- return scripts
- def merge(config, revisions, message=None, branch_label=None, rev_id=None):
- """Merge two revisions together. Creates a new migration file.
- .. versionadded:: 0.7.0
- :param config: a :class:`.Config` instance
- :param message: string message to apply to the revision
- :param branch_label: string label name to apply to the new revision
- :param rev_id: hardcoded revision identifier instead of generating a new
- one.
- .. seealso::
- :ref:`branches`
- """
- script = ScriptDirectory.from_config(config)
- template_args = {
- 'config': config # Let templates use config for
- # e.g. multiple databases
- }
- return script.generate_revision(
- rev_id or util.rev_id(), message, refresh=True,
- head=revisions, branch_labels=branch_label,
- **template_args)
- def upgrade(config, revision, sql=False, tag=None):
- """Upgrade to a later version.
- :param config: a :class:`.Config` instance.
- :param revision: string revision target or range for --sql mode
- :param sql: if True, use ``--sql`` mode
- :param tag: an arbitrary "tag" that can be intercepted by custom
- ``env.py`` scripts via the :class:`.EnvironmentContext.get_tag_argument`
- method.
- """
- script = ScriptDirectory.from_config(config)
- starting_rev = None
- if ":" in revision:
- if not sql:
- raise util.CommandError("Range revision not allowed")
- starting_rev, revision = revision.split(':', 2)
- def upgrade(rev, context):
- return script._upgrade_revs(revision, rev)
- with EnvironmentContext(
- config,
- script,
- fn=upgrade,
- as_sql=sql,
- starting_rev=starting_rev,
- destination_rev=revision,
- tag=tag
- ):
- script.run_env()
- def downgrade(config, revision, sql=False, tag=None):
- """Revert to a previous version.
- :param config: a :class:`.Config` instance.
- :param revision: string revision target or range for --sql mode
- :param sql: if True, use ``--sql`` mode
- :param tag: an arbitrary "tag" that can be intercepted by custom
- ``env.py`` scripts via the :class:`.EnvironmentContext.get_tag_argument`
- method.
- """
- script = ScriptDirectory.from_config(config)
- starting_rev = None
- if ":" in revision:
- if not sql:
- raise util.CommandError("Range revision not allowed")
- starting_rev, revision = revision.split(':', 2)
- elif sql:
- raise util.CommandError(
- "downgrade with --sql requires <fromrev>:<torev>")
- def downgrade(rev, context):
- return script._downgrade_revs(revision, rev)
- with EnvironmentContext(
- config,
- script,
- fn=downgrade,
- as_sql=sql,
- starting_rev=starting_rev,
- destination_rev=revision,
- tag=tag
- ):
- script.run_env()
- def show(config, rev):
- """Show the revision(s) denoted by the given symbol.
- :param config: a :class:`.Config` instance.
- :param revision: string revision target
- """
- script = ScriptDirectory.from_config(config)
- if rev == "current":
- def show_current(rev, context):
- for sc in script.get_revisions(rev):
- config.print_stdout(sc.log_entry)
- return []
- with EnvironmentContext(
- config,
- script,
- fn=show_current
- ):
- script.run_env()
- else:
- for sc in script.get_revisions(rev):
- config.print_stdout(sc.log_entry)
- def history(config, rev_range=None, verbose=False):
- """List changeset scripts in chronological order.
- :param config: a :class:`.Config` instance.
- :param rev_range: string revision range
- :param verbose: output in verbose mode.
- """
- script = ScriptDirectory.from_config(config)
- if rev_range is not None:
- if ":" not in rev_range:
- raise util.CommandError(
- "History range requires [start]:[end], "
- "[start]:, or :[end]")
- base, head = rev_range.strip().split(":")
- else:
- base = head = None
- def _display_history(config, script, base, head):
- for sc in script.walk_revisions(
- base=base or "base",
- head=head or "heads"):
- config.print_stdout(
- sc.cmd_format(
- verbose=verbose, include_branches=True,
- include_doc=True, include_parents=True))
- def _display_history_w_current(config, script, base=None, head=None):
- def _display_current_history(rev, context):
- if head is None:
- _display_history(config, script, base, rev)
- elif base is None:
- _display_history(config, script, rev, head)
- return []
- with EnvironmentContext(
- config,
- script,
- fn=_display_current_history
- ):
- script.run_env()
- if base == "current":
- _display_history_w_current(config, script, head=head)
- elif head == "current":
- _display_history_w_current(config, script, base=base)
- else:
- _display_history(config, script, base, head)
- def heads(config, verbose=False, resolve_dependencies=False):
- """Show current available heads in the script directory
- :param config: a :class:`.Config` instance.
- :param verbose: output in verbose mode.
- :param resolve_dependencies: treat dependency version as down revisions.
- """
- script = ScriptDirectory.from_config(config)
- if resolve_dependencies:
- heads = script.get_revisions("heads")
- else:
- heads = script.get_revisions(script.get_heads())
- for rev in heads:
- config.print_stdout(
- rev.cmd_format(
- verbose, include_branches=True, tree_indicators=False))
- def branches(config, verbose=False):
- """Show current branch points.
- :param config: a :class:`.Config` instance.
- :param verbose: output in verbose mode.
- """
- script = ScriptDirectory.from_config(config)
- for sc in script.walk_revisions():
- if sc.is_branch_point:
- config.print_stdout(
- "%s\n%s\n",
- sc.cmd_format(verbose, include_branches=True),
- "\n".join(
- "%s -> %s" % (
- " " * len(str(sc.revision)),
- rev_obj.cmd_format(
- False, include_branches=True, include_doc=verbose)
- ) for rev_obj in
- (script.get_revision(rev) for rev in sc.nextrev)
- )
- )
- def current(config, verbose=False, head_only=False):
- """Display the current revision for a database.
- :param config: a :class:`.Config` instance.
- :param verbose: output in verbose mode.
- :param head_only: deprecated; use ``verbose`` for additional output.
- """
- script = ScriptDirectory.from_config(config)
- if head_only:
- util.warn("--head-only is deprecated")
- def display_version(rev, context):
- if verbose:
- config.print_stdout(
- "Current revision(s) for %s:",
- util.obfuscate_url_pw(context.connection.engine.url)
- )
- for rev in script.get_all_current(rev):
- config.print_stdout(rev.cmd_format(verbose))
- return []
- with EnvironmentContext(
- config,
- script,
- fn=display_version
- ):
- script.run_env()
- def stamp(config, revision, sql=False, tag=None):
- """'stamp' the revision table with the given revision; don't
- run any migrations.
- :param config: a :class:`.Config` instance.
- :param revision: target revision.
- :param sql: use ``--sql`` mode
- :param tag: an arbitrary "tag" that can be intercepted by custom
- ``env.py`` scripts via the :class:`.EnvironmentContext.get_tag_argument`
- method.
- """
- script = ScriptDirectory.from_config(config)
- starting_rev = None
- if ":" in revision:
- if not sql:
- raise util.CommandError("Range revision not allowed")
- starting_rev, revision = revision.split(':', 2)
- def do_stamp(rev, context):
- return script._stamp_revs(revision, rev)
- with EnvironmentContext(
- config,
- script,
- fn=do_stamp,
- as_sql=sql,
- destination_rev=revision,
- starting_rev=starting_rev,
- tag=tag
- ):
- script.run_env()
- def edit(config, rev):
- """Edit revision script(s) using $EDITOR.
- :param config: a :class:`.Config` instance.
- :param rev: target revision.
- """
- script = ScriptDirectory.from_config(config)
- if rev == "current":
- def edit_current(rev, context):
- if not rev:
- raise util.CommandError("No current revisions")
- for sc in script.get_revisions(rev):
- util.edit(sc.path)
- return []
- with EnvironmentContext(
- config,
- script,
- fn=edit_current
- ):
- script.run_env()
- else:
- revs = script.get_revisions(rev)
- if not revs:
- raise util.CommandError(
- "No revision files indicated by symbol '%s'" % rev)
- for sc in revs:
- util.edit(sc.path)
|