text: from argparse import ArgumentParser
  import inspect
  import os
  import sys
  
  from . import __version__
  from . import command
  from . import util
  from .util import compat
  from .util.compat import SafeConfigParser
  
  
  class Config(object):
  
      r"""Represent an Alembic configuration.
  
      Within an ``env.py`` script, this is available
      via the :attr:`.EnvironmentContext.config` attribute,
      which in turn is available at ``alembic.context``::
  
          from alembic import context
  
          some_param = context.config.get_main_option("my option")
  
      When invoking Alembic programatically, a new
      :class:`.Config` can be created by passing
      the name of an .ini file to the constructor::
  
          from alembic.config import Config
          alembic_cfg = Config("/path/to/yourapp/alembic.ini")
  
      With a :class:`.Config` object, you can then
      run Alembic commands programmatically using the directives
      in :mod:`alembic.command`.
  
      The :class:`.Config` object can also be constructed without
      a filename.   Values can be set programmatically, and
      new sections will be created as needed::
  
          from alembic.config import Config
          alembic_cfg = Config()
          alembic_cfg.set_main_option("script_location", "myapp:migrations")
          alembic_cfg.set_main_option("sqlalchemy.url", "postgresql://foo/bar")
          alembic_cfg.set_section_option("mysection", "foo", "bar")
  
      .. warning::
  
         When using programmatic configuration, make sure the
         ``env.py`` file in use is compatible with the target configuration;
         including that the call to Python ``logging.fileConfig()`` is
         omitted if the programmatic configuration doesn't actually include
         logging directives.
  
      For passing non-string values to environments, such as connections and
      engines, use the :attr:`.Config.attributes` dictionary::
  
          with engine.begin() as connection:
              alembic_cfg.attributes['connection'] = connection
              command.upgrade(alembic_cfg, "head")
  
      :param file\_: name of the .ini file to open.
      :param ini_section: name of the main Alembic section within the
       .ini file
      :param output_buffer: optional file-like input buffer which
       will be passed to the :class:`.MigrationContext` - used to redirect
       the output of "offline generation" when using Alembic programmatically.
      :param stdout: buffer where the "print" output of commands will be sent.
       Defaults to ``sys.stdout``.
  
      :param config_args: A dictionary of keys and values that will be used
       for substitution in the alembic config file.  The dictionary as given
       is **copied** to a new one, stored locally as the attribute
       ``.config_args``. When the :attr:`.Config.file_config` attribute is
       first invoked, the replacement variable ``here`` will be added to this
       dictionary before the dictionary is passed to ``SafeConfigParser()``
       to parse the .ini file.
  
      :param attributes: optional dictionary of arbitrary Python keys/values,
       which will be populated into the :attr:`.Config.attributes` dictionary.
  
       .. seealso::
  
          :ref:`connection_sharing`
  
      """
  
      def __init__(
          self,
          file_=None,
          ini_section="alembic",
          output_buffer=None,
          stdout=sys.stdout,
          cmd_opts=None,
          config_args=util.immutabledict(),
          attributes=None,
      ):
          """Construct a new :class:`.Config`"""
          self.config_file_name = file_
          self.config_ini_section = ini_section
          self.output_buffer = output_buffer
          self.stdout = stdout
          self.cmd_opts = cmd_opts
          self.config_args = dict(config_args)
          if attributes:
              self.attributes.update(attributes)
  
      cmd_opts = None
      """The command-line options passed to the ``alembic`` script.
  
      Within an ``env.py`` script this can be accessed via the
      :attr:`.EnvironmentContext.config` attribute.
  
      .. seealso::
  
          :meth:`.EnvironmentContext.get_x_argument`
  
      """
  
      config_file_name = None
      """Filesystem path to the .ini file in use."""
  
      config_ini_section = None
      """Name of the config file section to read basic configuration
      from.  Defaults to ``alembic``, that is the ``[alembic]`` section
      of the .ini file.  This value is modified using the ``-n/--name``
      option to the Alembic runner.
  
      """
  
      @util.memoized_property
      def attributes(self):
          """A Python dictionary for storage of additional state.
  
  
          This is a utility dictionary which can include not just strings but
          engines, connections, schema objects, or anything else.
          Use this to pass objects into an env.py script, such as passing
          a :class:`sqlalchemy.engine.base.Connection` when calling
          commands from :mod:`alembic.command` programmatically.
  
          .. seealso::
  
              :ref:`connection_sharing`
  
              :paramref:`.Config.attributes`
  
          """
          return {}
  
      def print_stdout(self, text, *arg):
          """Render a message to standard out.
  
          When :meth:`.Config.print_stdout` is called with additional args
          those arguments will formatted against the provided text,
          otherwise we simply output the provided text verbatim.
  
          e.g.::
  
              >>> config.print_stdout('Some text %s', 'arg')
              Some Text arg
  
          """
  
          if arg:
              output = compat.text_type(text) % arg
          else:
              output = compat.text_type(text)
  
          util.write_outstream(self.stdout, output, "\n")
  
      @util.memoized_property
      def file_config(self):
          """Return the underlying ``ConfigParser`` object.
  
          Direct access to the .ini file is available here,
          though the :meth:`.Config.get_section` and
          :meth:`.Config.get_main_option`
          methods provide a possibly simpler interface.
  
          """
  
          if self.config_file_name:
              here = os.path.abspath(os.path.dirname(self.config_file_name))
          else:
              here = ""
          self.config_args["here"] = here
          file_config = SafeConfigParser(self.config_args)
          if self.config_file_name:
              file_config.read([self.config_file_name])
          else:
              file_config.add_section(self.config_ini_section)
          return file_config
  
      def get_template_directory(self):
          """Return the directory where Alembic setup templates are found.
  
          This method is used by the alembic ``init`` and ``list_templates``
          commands.
  
          """
          import alembic
  
          package_dir = os.path.abspath(os.path.dirname(alembic.__file__))
          return os.path.join(package_dir, "templates")
  
      def get_section(self, name, default=None):
          """Return all the configuration options from a given .ini file section
          as a dictionary.
  
          """
          if not self.file_config.has_section(name):
              return default
  
          return dict(self.file_config.items(name))
  
      def set_main_option(self, name, value):
          """Set an option programmatically within the 'main' section.
  
          This overrides whatever was in the .ini file.
  
          :param name: name of the value
  
          :param value: the value.  Note that this value is passed to
           ``ConfigParser.set``, which supports variable interpolation using
           pyformat (e.g. ``%(some_value)s``).   A raw percent sign not part of
           an interpolation symbol must therefore be escaped, e.g. ``%%``.
           The given value may refer to another value already in the file
           using the interpolation format.
  
          """
          self.set_section_option(self.config_ini_section, name, value)
  
      def remove_main_option(self, name):
          self.file_config.remove_option(self.config_ini_section, name)
  
      def set_section_option(self, section, name, value):
          """Set an option programmatically within the given section.
  
          The section is created if it doesn't exist already.
          The value here will override whatever was in the .ini
          file.
  
          :param section: name of the section
  
          :param name: name of the value
  
          :param value: the value.  Note that this value is passed to
           ``ConfigParser.set``, which supports variable interpolation using
           pyformat (e.g. ``%(some_value)s``).   A raw percent sign not part of
           an interpolation symbol must therefore be escaped, e.g. ``%%``.
           The given value may refer to another value already in the file
           using the interpolation format.
  
          """
  
          if not self.file_config.has_section(section):
              self.file_config.add_section(section)
          self.file_config.set(section, name, value)
  
      def get_section_option(self, section, name, default=None):
          """Return an option from the given section of the .ini file."""
          if not self.file_config.has_section(section):
              raise util.CommandError(
                  "No config file %r found, or file has no "
                  "'[%s]' section" % (self.config_file_name, section)
              )
          if self.file_config.has_option(section, name):
              return self.file_config.get(section, name)
          else:
              return default
  
      def get_main_option(self, name, default=None):
          """Return an option from the 'main' section of the .ini file.
  
          This defaults to being a key from the ``[alembic]``
          section, unless the ``-n/--name`` flag were used to
          indicate a different section.
  
          """
          return self.get_section_option(self.config_ini_section, name, default)
  
  
  class CommandLine(object):
      def __init__(self, prog=None):
          self._generate_args(prog)
  
      def _generate_args(self, prog):
          def add_options(fn, parser, positional, kwargs):
              kwargs_opts = {
                  "template": (
                      "-t",
                      "--template",
                      dict(
                          default="generic",
                          type=str,
                          help="Setup template for use with 'init'",
                      ),
                  ),
                  "message": (
                      "-m",
                      "--message",
                      dict(
                          type=str, help="Message string to use with 'revision'"
                      ),
                  ),
                  "sql": (
                      "--sql",
                      dict(
                          action="store_true",
                          help="Don't emit SQL to database - dump to "
                          "standard output/file instead. See docs on "
                          "offline mode.",
                      ),
                  ),
                  "tag": (
                      "--tag",
                      dict(
                          type=str,
                          help="Arbitrary 'tag' name - can be used by "
                          "custom env.py scripts.",
                      ),
                  ),
                  "head": (
                      "--head",
                      dict(
                          type=str,
                          help="Specify head revision or <branchname>@head "
                          "to base new revision on.",
                      ),
                  ),
                  "splice": (
                      "--splice",
                      dict(
                          action="store_true",
                          help="Allow a non-head revision as the "
                          "'head' to splice onto",
                      ),
                  ),
                  "depends_on": (
                      "--depends-on",
                      dict(
                          action="append",
                          help="Specify one or more revision identifiers "
                          "which this revision should depend on.",
                      ),
                  ),
                  "rev_id": (
                      "--rev-id",
                      dict(
                          type=str,
                          help="Specify a hardcoded revision id instead of "
                          "generating one",
                      ),
                  ),
                  "version_path": (
                      "--version-path",
                      dict(
                          type=str,
                          help="Specify specific path from config for "
                          "version file",
                      ),
                  ),
                  "branch_label": (
                      "--branch-label",
                      dict(
                          type=str,
                          help="Specify a branch label to apply to the "
                          "new revision",
                      ),
                  ),
                  "verbose": (
                      "-v",
                      "--verbose",
                      dict(action="store_true", help="Use more verbose output"),
                  ),
                  "resolve_dependencies": (
                      "--resolve-dependencies",
                      dict(
                          action="store_true",
                          help="Treat dependency versions as down revisions",
                      ),
                  ),
                  "autogenerate": (
                      "--autogenerate",
                      dict(
                          action="store_true",
                          help="Populate revision script with candidate "
                          "migration operations, based on comparison "
                          "of database to model.",
                      ),
                  ),
                  "rev_range": (
                      "-r",
                      "--rev-range",
                      dict(
                          action="store",
                          help="Specify a revision range; "
                          "format is [start]:[end]",
                      ),
                  ),
                  "indicate_current": (
                      "-i",
                      "--indicate-current",
                      dict(
                          action="store_true",
                          help="Indicate the current revision",
                      ),
                  ),
                  "purge": (
                      "--purge",
                      dict(
                          action="store_true",
                          help="Unconditionally erase the version table "
                          "before stamping",
                      ),
                  ),
                  "package": (
                      "--package",
                      dict(
                          action="store_true",
                          help="Write empty __init__.py files to the "
                          "environment and version locations",
                      ),
                  ),
              }
              positional_help = {
                  "directory": "location of scripts directory",
                  "revision": "revision identifier",
                  "revisions": "one or more revisions, or 'heads' for all heads",
              }
              for arg in kwargs:
                  if arg in kwargs_opts:
                      args = kwargs_opts[arg]
                      args, kw = args[0:-1], args[-1]
                      parser.add_argument(*args, **kw)
  
              for arg in positional:
                  if (
                      arg == "revisions"
                      or fn in positional_translations
                      and positional_translations[fn][arg] == "revisions"
                  ):
                      subparser.add_argument(
                          "revisions",
                          nargs="+",
                          help=positional_help.get("revisions"),
                      )
                  else:
                      subparser.add_argument(arg, help=positional_help.get(arg))
  
          parser = ArgumentParser(prog=prog)
  
          parser.add_argument(
              "--version", action="version", version="%%(prog)s %s" % __version__
          )
          parser.add_argument(
              "-c",
              "--config",
              type=str,
              default=os.environ.get("ALEMBIC_CONFIG", "alembic.ini"),
              help="Alternate config file; defaults to value of "
              'ALEMBIC_CONFIG environment variable, or "alembic.ini"',
          )
          parser.add_argument(
              "-n",
              "--name",
              type=str,
              default="alembic",
              help="Name of section in .ini file to " "use for Alembic config",
          )
          parser.add_argument(
              "-x",
              action="append",
              help="Additional arguments consumed by "
              "custom env.py scripts, e.g. -x "
              "setting1=somesetting -x setting2=somesetting",
          )
          parser.add_argument(
              "--raiseerr",
              action="store_true",
              help="Raise a full stack trace on error",
          )
          subparsers = parser.add_subparsers()
  
          positional_translations = {command.stamp: {"revision": "revisions"}}
  
          for fn in [getattr(command, n) for n in dir(command)]:
              if (
                  inspect.isfunction(fn)
                  and fn.__name__[0] != "_"
                  and fn.__module__ == "alembic.command"
              ):
  
                  spec = compat.inspect_getargspec(fn)
                  if spec[3]:
                      positional = spec[0][1 : -len(spec[3])]
                      kwarg = spec[0][-len(spec[3]) :]
                  else:
                      positional = spec[0][1:]
                      kwarg = []
  
                  if fn in positional_translations:
                      positional = [
                          positional_translations[fn].get(name, name)
                          for name in positional
                      ]
  
                  # parse first line(s) of helptext without a line break
                  help_ = fn.__doc__
                  if help_:
                      help_text = []
                      for line in help_.split("\n"):
                          if not line.strip():
                              break
                          else:
                              help_text.append(line.strip())
                  else:
                      help_text = ""
                  subparser = subparsers.add_parser(
                      fn.__name__, help=" ".join(help_text)
                  )
                  add_options(fn, subparser, positional, kwarg)
                  subparser.set_defaults(cmd=(fn, positional, kwarg))
          self.parser = parser
  
      def run_cmd(self, config, options):
          fn, positional, kwarg = options.cmd
  
          try:
              fn(
                  config,
                  *[getattr(options, k, None) for k in positional],
                  **dict((k, getattr(options, k, None)) for k in kwarg)
              )
          except util.CommandError as e:
              if options.raiseerr:
                  raise
              else:
                  util.err(str(e))
  
      def main(self, argv=None):
          options = self.parser.parse_args(argv)
          if not hasattr(options, "cmd"):
              # see http://bugs.python.org/issue9253, argparse
              # behavior changed incompatibly in py3.3
              self.parser.error("too few arguments")
          else:
              cfg = Config(
                  file_=options.config,
                  ini_section=options.name,
                  cmd_opts=options,
              )
              self.run_cmd(cfg, options)
  
  
  def main(argv=None, prog=None, **kwargs):
      """The console runner function for Alembic."""
  
      CommandLine(prog=prog).main(argv=argv)
  
  
  if __name__ == "__main__":
      main()
  