]> git.ipfire.org Git - thirdparty/sqlalchemy/alembic.git/commitdiff
- add argparse to install requires
authorMike Bayer <mike_mp@zzzcomputing.com>
Tue, 15 Nov 2011 04:34:17 +0000 (23:34 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Tue, 15 Nov 2011 04:34:17 +0000 (23:34 -0500)
- more docs
- get env.py to work in --sql mode even without the DBAPI installed,
using just the URL
- add get_section_option()

alembic/config.py
alembic/context.py
alembic/templates/generic/env.py
alembic/templates/multidb/env.py
alembic/templates/pylons/env.py
docs/build/api.rst
docs/build/tutorial.rst
setup.py

index 26750a980546a33f385b828dc1ee9c908ae843a0..c5389f80f4448a275196bd72ac176b814d2d151c 100644 (file)
@@ -6,17 +6,17 @@ import os
 
 class Config(object):
     """Represent an Alembic configuration.
-    
+
     You can get at one of these by specifying the name of 
     an .ini file::
-    
+
         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`.
-    
+
     """
     def __init__(self, file_, ini_section='alembic'):
         self.config_file_name = file_
@@ -30,13 +30,13 @@ class Config(object):
     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 runnier.
-    
+
     """
 
     @util.memoized_property
     def file_config(self):
         """Return the underlying :class:`ConfigParser` object.
-        
+
         Direct access to the .ini file is available here,
         though the :meth:`.Config.get_section` and 
         :meth:`.Config.get_main_option`
@@ -51,36 +51,42 @@ class Config(object):
 
     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.
-        
+
         """
         return os.path.join(package_dir, 'templates')
 
     def get_section(self, name):
         """Return all the configuration options from a given .ini file section
         as a dictionary.
-        
+
         """
         return dict(self.file_config.items(name))
 
+    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):
+            util.err("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.
-        
+
         """
-        if not self.file_config.has_section(self.config_ini_section):
-            util.err("No config file %r found, or file has no "
-                                "'[%s]' section" % 
-                                (self.config_file_name, self.config_ini_section))
-        if self.file_config.has_option(self.config_ini_section, name):
-            return self.file_config.get(self.config_ini_section, name)
-        else:
-            return default
+        return self.get_section_option(self.config_ini_section, name, default)
 
 def main(argv):
     """The console runner function for Alembic."""
index 752bd2ddfbb5fda3e68657c59ab9efbd4b5719cf..adcb9e066973f861084c23ddb4818d17a99bf315 100644 (file)
@@ -19,6 +19,10 @@ class Context(object):
     
     Mediates the relationship between an ``env.py`` environment script, 
     a :class:`.ScriptDirectory` instance, and a :class:`.DDLImpl` instance.
+
+    The :class:`.Context` is available via the :func:`.get_context` function,
+    though usually one would call the various module level functions
+    described here.
     
     """
     def __init__(self, dialect, script, connection, fn, 
@@ -88,6 +92,8 @@ class Context(object):
                 if self.as_sql and not current_rev:
                     _version.create(self.connection)
             log.info("Running %s %s -> %s", change.__name__, prev_rev, rev)
+            if self.as_sql:
+                self.impl.static_output("-- Running %s %s -> %s" %(change.__name__, prev_rev, rev))
             change(**kw)
             if not self.impl.transactional_ddl:
                 self._update_current_rev(prev_rev, rev)
@@ -151,18 +157,23 @@ def requires_connection():
     """Return True if the current migrations environment should have
     an active database connection.
     
+    Currently, this is ``True`` or ``False`` depending 
+    on the the ``--sql`` flag passed.
+    
     """
     return not _context_opts.get('as_sql', False)
 
 def get_head_revision():
-    """Return the value of the 'head' revision."""
+    """Return the hex identifier of the 'head' revision."""
     return _script._as_rev_number("head")
 
 def get_starting_revision_argument():
     """Return the 'starting revision' argument,
-    if the revision was passed as start:end.
+    if the revision was passed using ``start:end``.
     
-    This is only usable in "offline" mode.
+    This is only meaningful in "offline" mode.
+    Returns ``None`` if no value is available
+    or was configured.
 
     """
     if _context is not None:
@@ -175,15 +186,28 @@ def get_starting_revision_argument():
 def get_revision_argument():
     """Get the 'destination' revision argument.
     
-    This will be the target rev number.  'head'
-    is translated into the actual version number
-    as is 'base' which is translated to None.
+    This is typically the argument passed to the 
+    ``upgrade`` or ``downgrade`` command, but can
+    be overridden via the ``destination_rev`` argument
+    passed to :func:`.configure`.
+    
+    If 
+    it was specified as ``head``, the actual 
+    version number is returned; if specified
+    as ``base``, ``None`` is returned.
 
     """
     return _script._as_rev_number(_context_opts['destination_rev'])
 
 def get_tag_argument():
-    """Return the value passed for the ``--tag`` argument, if any."""
+    """Return the value passed for the ``--tag`` argument, if any.
+    
+    The ``--tag`` argument is not used directly by Alembic,
+    but is available for custom ``env.py`` configurations that 
+    wish to use it; particularly for offline generation scripts
+    that wish to generate tagged filenames.
+    
+    """
     return _context_opts.get('tag', None)
 
 def configure(
@@ -292,9 +316,7 @@ def execute(sql):
     get_context().execute(sql)
 
 def get_context():
-    """Return the current :class:`.DefaultContext` object.
-    
-    This object is the entrypoint to dialect specific behavior.
+    """Return the current :class:`.Context` object.
     
     Generally, env.py scripts should access the module-level functions
     in :mod:`alebmic.context` to get at this object's functionality.
index ac64bee7be36b78828abe9926816b731c4a383f5..8f5018f1295e7c3339fd4827a45a1bb023baddfc 100644 (file)
@@ -10,12 +10,6 @@ config = context.config
 # This line sets up loggers basically.
 fileConfig(config.config_file_name)
 
-# Produce a SQLAlchemy engine using the key/values
-# within the "alembic" section of the documentation,
-# other otherwise what config_ini_section points to.
-engine = engine_from_config(
-            config.get_section(config.config_ini_section), prefix='sqlalchemy.')
-
 
 # other values from the config, defined by the needs of env.py,
 # can be acquired:
@@ -23,12 +17,22 @@ engine = engine_from_config(
 # ... etc.
 
 # if we're running in --sql mode, do everything connectionless.
+# We only need a URL, not an Engine, thereby not even requiring
+# the DBAPI be installed, though an actual Engine would of course be fine as well.
 if not context.requires_connection():
-    context.configure(dialect_name=engine.name)
+    url = config.get_main_option("sqlalchemy.url")
+    context.configure(url=url)
     context.run_migrations()
 
 # otherwise we need to make a connection.
 else:
+
+    # Produce a SQLAlchemy engine using the key/values
+    # within the "alembic" section of the documentation,
+    # other otherwise what config_ini_section points to.
+    engine = engine_from_config(
+                config.get_section(config.config_ini_section), prefix='sqlalchemy.')
+
     connection = engine.connect()
     context.configure(connection=connection, dialect_name=engine.name)
 
index 3acb27cf3f7550757be4a124524ed8e5daee84d6..e561e9becf945d031d012ab108e0bc106df84bc7 100644 (file)
@@ -1,6 +1,6 @@
 USE_TWOPHASE = False
 
-from alembic import options, context
+from alembic import context
 from sqlalchemy import engine_from_config
 import re
 import sys
@@ -8,25 +8,37 @@ import sys
 import logging
 logging.fileConfig(options.config_file)
 
+# gather section names referring to different 
+# databases.
 db_names = options.get_main_option('databases')
+
+# set aside if we need engines or just URLs to do this.
+need_engine = context.requires_connection()
+
+# load up SQLAlchemy engines or URLs.
 engines = {}
 for name in re.split(r',\s*', db_names):
     engines[name] = rec = {}
-    rec['engine'] = engine = \
-                engine_from_config(options.get_section(name),
+    if need_engine:
+        rec['engine'] = engine_from_config(context.config.get_section(name),
                                 prefix='sqlalchemy.')
+    else:
+        rec['url'] = context.config.get_section_option(name, "sqlalchemy.url")
 
-
-if not context.requires_connection():
+# for the --sql use case, run migrations for each URL into
+# individual files.
+if not need_engine:
     for name, rec in engines.items():
-        # Write output to individual per-engine files.
         file_ = "%s.sql" % name
         sys.stderr.write("Writing output to %s\n" % file_)
         context.configure(
-                    dialect_name=rec['engine'].name,
+                    url=rec['url'],
                     output_buffer=file(file_, 'w')
                 )
         context.run_migrations(engine=name)
+
+# for the direct-to-DB use case, start a transaction on all
+# engines, then run all migrations, then commit all transactions.
 else:
     for name, rec in engines.items():
         engine = rec['engine']
index 799ba0256bf8598d03ae5d96a24c81eeea90ddfb..2a3d99726c70bb8fda56c2542f9e3f318784cc4d 100644 (file)
@@ -4,7 +4,7 @@ Place 'pylons_config_file' into alembic.ini, and the application will
 be loaded from there.
 
 """
-from alembic import config, context
+from alembic import context
 from paste.deploy import loadapp
 import logging
 
@@ -23,6 +23,7 @@ except:
 # customize this section for non-standard engine configurations.
 meta = __import__("%s.model.meta" % config['pylons.package']).model.meta
 
+
 if not context.requires_connection():
     context.configure(
                 dialect_name=meta.engine.name)
index 411600a1d6d091643746a3c6ec8516b0de72bb18..cff5826c8bd20028fe53c298a95469f764ba5c9d 100644 (file)
@@ -8,37 +8,42 @@ a migration environment's ``env.py`` file.
 env.py Directives
 =================
 
+The :mod:`alembic.context` module contains API features that are generally used within
+``env.py`` files.
+
 .. autofunction:: sqlalchemy.engine.engine_from_config
-.. autofunction:: alembic.context.configure
-.. autofunction:: alembic.context.get_context
-.. autofunction:: alembic.context.execute
-.. autofunction:: alembic.context.requires_connection
-.. autofunction:: alembic.context.run_migrations
 
-Internals
-=========
+.. currentmodule:: alembic.context
 
-.. currentmodule:: alembic.command
+.. automodule:: alembic.context
+    :members:
 
 Commands
---------
+=========
 
 Alembic commands are all represented by functions in the :mod:`alembic.command`
 package.  They all accept the same style of usage, being sent
 the :class:`~.alembic.config.Config` object as the first argument.
 
+.. currentmodule:: alembic.command
 
 .. automodule:: alembic.command
     :members:
     :undoc-members:
 
-Misc
-----
+Configuration
+==============
+
+.. currentmodule:: alembic.config
+
 .. automodule:: alembic.config
     :members:
     :undoc-members:
 
 
+Internals
+=========
+
 .. automodule:: alembic.script
     :members:
     :undoc-members:
index 652ed7b4d9ceec85a0d86a12f25248ca43f92721..2b2eb63986d8c35283d855ab8f159fe3269bcc9f 100644 (file)
@@ -457,11 +457,11 @@ Autogenerate can *not* detect:
   yet implemented.
 
 
-Generating SQL Scripts
-======================
+Generating SQL Scripts (a.k.a. "Offline Mode")
+==============================================
 
 A major capability of Alembic is to generate migrations as SQL scripts, instead of running
-them against the database - this is also referred to as "offline" mode.   
+them against the database - this is also referred to as *offline mode*.   
 This is a critical feature when working in large organizations
 where access to DDL is restricted, and SQL scripts must be handed off to DBAs.   Alembic makes
 this easy via the ``--sql`` option passed to any ``upgrade`` or ``downgrade`` command.   We 
index cdc6483cb262b1d18d37c8edb56142192bb7b097..e6e2beb4d052bde584979e9d84ad9e2bc8f25f2a 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -41,7 +41,11 @@ setup(name='alembic',
       zip_safe=False,
       install_requires=[
           'SQLAlchemy>=0.6.0',
-          'Mako'
+          'Mako',
+          # TODO: should this not be here if the env. is 
+          # Python 2.7/3.2 ? not sure how this is supposed 
+          # to be handled
+          'argparse'
       ],
       entry_points="""
       """,