diff options
-rw-r--r-- | alembic/script.py | 33 | ||||
-rw-r--r-- | alembic/templates/generic/alembic.ini.mako | 5 | ||||
-rw-r--r-- | alembic/templates/multidb/alembic.ini.mako | 5 | ||||
-rw-r--r-- | alembic/templates/pylons/alembic.ini.mako | 5 | ||||
-rw-r--r-- | docs/build/changelog.rst | 8 | ||||
-rw-r--r-- | docs/build/tutorial.rst | 21 | ||||
-rw-r--r-- | tests/__init__.py | 7 | ||||
-rw-r--r-- | tests/test_versioning.py | 35 |
8 files changed, 101 insertions, 18 deletions
diff --git a/alembic/script.py b/alembic/script.py index 32adf1c..3294366 100644 --- a/alembic/script.py +++ b/alembic/script.py @@ -4,7 +4,8 @@ import re import shutil from . import util -_rev_file = re.compile(r'(.*\.py)(c|o)?$') +_sourceless_rev_file = re.compile(r'(.*\.py)(c|o)?$') +_only_source_rev_file = re.compile(r'(.*\.py)$') _legacy_rev = re.compile(r'([a-f0-9]+)\.py$') _mod_def_re = re.compile(r'(upgrade|downgrade)_([a-z0-9]+)') _slug_re = re.compile(r'\w+') @@ -31,11 +32,13 @@ class ScriptDirectory(object): """ def __init__(self, dir, file_template=_default_file_template, - truncate_slug_length=40): + truncate_slug_length=40, + sourceless=False): self.dir = dir self.versions = os.path.join(self.dir, 'versions') self.file_template = file_template self.truncate_slug_length = truncate_slug_length or 40 + self.sourceless = sourceless if not os.access(dir, os.F_OK): raise util.CommandError("Path doesn't exist: %r. Please use " @@ -63,7 +66,8 @@ class ScriptDirectory(object): file_template=config.get_main_option( 'file_template', _default_file_template), - truncate_slug_length=truncate_slug_length + truncate_slug_length=truncate_slug_length, + sourceless=config.get_main_option("sourceless") == "true" ) def walk_revisions(self, base="base", head="head"): @@ -206,7 +210,7 @@ class ScriptDirectory(object): def _revision_map(self): map_ = {} for file_ in os.listdir(self.versions): - script = Script._from_filename(self.versions, file_) + script = Script._from_filename(self, self.versions, file_) if script is None: continue if script.revision in map_: @@ -348,7 +352,7 @@ class ScriptDirectory(object): **kw ) if refresh: - script = Script._from_path(path) + script = Script._from_path(self, path) self._revision_map[script.revision] = script if script.down_revision: self._revision_map[script.down_revision].\ @@ -457,20 +461,27 @@ class Script(object): self.doc) @classmethod - def _from_path(cls, path): + def _from_path(cls, scriptdir, path): dir_, filename = os.path.split(path) - return cls._from_filename(dir_, filename) + return cls._from_filename(scriptdir, dir_, filename) @classmethod - def _from_filename(cls, dir_, filename): - py_match = _rev_file.match(filename) + def _from_filename(cls, scriptdir, dir_, filename): + if scriptdir.sourceless: + py_match = _sourceless_rev_file.match(filename) + else: + py_match = _only_source_rev_file.match(filename) if not py_match: return None py_filename = py_match.group(1) - is_c = py_match.group(2) == 'c' - is_o = py_match.group(2) == 'o' + + if scriptdir.sourceless: + is_c = py_match.group(2) == 'c' + is_o = py_match.group(2) == 'o' + else: + is_c = is_o = False if is_o or is_c: py_exists = os.path.exists(os.path.join(dir_, py_filename)) diff --git a/alembic/templates/generic/alembic.ini.mako b/alembic/templates/generic/alembic.ini.mako index f7a5301..a738a24 100644 --- a/alembic/templates/generic/alembic.ini.mako +++ b/alembic/templates/generic/alembic.ini.mako @@ -15,6 +15,11 @@ script_location = ${script_location} # the 'revision' command, regardless of autogenerate # revision_environment = false +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + sqlalchemy.url = driver://user:pass@localhost/dbname diff --git a/alembic/templates/multidb/alembic.ini.mako b/alembic/templates/multidb/alembic.ini.mako index bd29898..132b246 100644 --- a/alembic/templates/multidb/alembic.ini.mako +++ b/alembic/templates/multidb/alembic.ini.mako @@ -15,6 +15,11 @@ script_location = ${script_location} # the 'revision' command, regardless of autogenerate # revision_environment = false +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + databases = engine1, engine2 [engine1] diff --git a/alembic/templates/pylons/alembic.ini.mako b/alembic/templates/pylons/alembic.ini.mako index 009b675..771c027 100644 --- a/alembic/templates/pylons/alembic.ini.mako +++ b/alembic/templates/pylons/alembic.ini.mako @@ -15,6 +15,11 @@ script_location = ${script_location} # the 'revision' command, regardless of autogenerate # revision_environment = false +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + pylons_config_file = ./development.ini # that's it !
\ No newline at end of file diff --git a/docs/build/changelog.rst b/docs/build/changelog.rst index caaa5c9..0b142df 100644 --- a/docs/build/changelog.rst +++ b/docs/build/changelog.rst @@ -6,6 +6,14 @@ Changelog :version: 0.6.4 .. change:: + :tags: feature + :tickets: 163 + + Altered the support for "sourceless" migration files (e.g. only + .pyc or .pyo present) so that the flag "sourceless=true" needs to + be in alembic.ini for this behavior to take effect. + + .. change:: :tags: bug, mssql :tickets: 185 diff --git a/docs/build/tutorial.rst b/docs/build/tutorial.rst index 1819f2f..aade686 100644 --- a/docs/build/tutorial.rst +++ b/docs/build/tutorial.rst @@ -127,6 +127,11 @@ The file generated with the "generic" configuration looks like:: # the 'revision' command, regardless of autogenerate # revision_environment = false + # set to 'true' to allow .pyc and .pyo files without + # a source .py file to be detected as revisions in the + # versions/ directory + # sourceless = false + sqlalchemy.url = driver://user:pass@localhost/dbname # Logging configuration @@ -198,12 +203,12 @@ This file contains the following features: ``%%(minute).2d``, ``%%(second).2d`` - components of the create date as returned by ``datetime.datetime.now()`` - .. versionadded:: 0.3.6 - added date parameters to ``file_template``. + .. versionadded:: 0.3.6 - added date parameters to ``file_template``. * ``truncate_slug_length`` - defaults to 40, the max number of characters to include in the "slug" field. - .. versionadded:: 0.6.1 - added ``truncate_slug_length`` configuration + .. versionadded:: 0.6.1 - added ``truncate_slug_length`` configuration * ``sqlalchemy.url`` - A URL to connect to the database via SQLAlchemy. This key is in fact only referenced within the ``env.py`` file that is specific to the "generic" configuration; @@ -212,7 +217,17 @@ This file contains the following features: of the file. * ``revision_environment`` - this is a flag which when set to the value 'true', will indicate that the migration environment script ``env.py`` should be run unconditionally when - generating new revision files (new in 0.3.3). + generating new revision files + + .. versionadded:: 0.3.3 + +* ``sourceless`` - when set to 'true', revision files that only exist as .pyc + or .pyo files in the versions directory will be used as versions, allowing + "sourceless" versioning folders. When left at the default of 'false', + only .py files are consumed as version files. + + .. versionadded:: 0.6.4 + * ``[loggers]``, ``[handlers]``, ``[formatters]``, ``[logger_*]``, ``[handler_*]``, ``[formatter_*]`` - these sections are all part of Python's standard logging configuration, the mechanics of which are documented at `Configuration File Format <http://docs.python.org/library/logging.config.html#configuration-file-format>`_. diff --git a/tests/__init__.py b/tests/__init__.py index 3c4633e..9bb5ef6 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -241,12 +241,13 @@ config = context.config with open(path, 'w') as f: f.write(txt) -def _sqlite_testing_config(): +def _sqlite_testing_config(sourceless=False): dir_ = os.path.join(staging_directory, 'scripts') return _write_config_file(""" [alembic] script_location = %s sqlalchemy.url = sqlite:///%s/foo.db +sourceless = %s [loggers] keys = root @@ -271,7 +272,7 @@ keys = generic [formatter_generic] format = %%(levelname)-5.5s [%%(name)s] %%(message)s datefmt = %%H:%%M:%%S - """ % (dir_, dir_)) + """ % (dir_, dir_, "true" if sourceless else "false")) def _no_sql_testing_config(dialect="postgresql", directives=""): @@ -362,7 +363,7 @@ def write_script(scriptdir, rev_id, content, encoding='ascii', sourceless=False) pyc_path = util.pyc_file_from_path(path) if os.access(pyc_path, os.F_OK): os.unlink(pyc_path) - script = Script._from_path(path) + script = Script._from_path(scriptdir, path) old = scriptdir._revision_map[script.revision] if old.down_revision != script.down_revision: raise Exception("Can't change down_revision " diff --git a/tests/test_versioning.py b/tests/test_versioning.py index 41e4be1..68440fc 100644 --- a/tests/test_versioning.py +++ b/tests/test_versioning.py @@ -100,7 +100,7 @@ class VersioningTest(unittest.TestCase): @classmethod def setup_class(cls): cls.env = staging_env(sourceless=cls.sourceless) - cls.cfg = _sqlite_testing_config() + cls.cfg = _sqlite_testing_config(sourceless=cls.sourceless) @classmethod def teardown_class(cls): @@ -188,3 +188,36 @@ class VersionNameTemplateTest(unittest.TestCase): class SourcelessVersioningTest(VersioningTest): sourceless = True +class SourcelessNeedsFlagTest(unittest.TestCase): + def setUp(self): + self.env = staging_env(sourceless=False) + self.cfg = _sqlite_testing_config() + + def tearDown(self): + clear_staging_env() + + def test_needs_flag(self): + a = util.rev_id() + + script = ScriptDirectory.from_config(self.cfg) + script.generate_revision(a, None, refresh=True) + write_script(script, a, """ + revision = '%s' + down_revision = None + + from alembic import op + + def upgrade(): + op.execute("CREATE TABLE foo(id integer)") + + def downgrade(): + op.execute("DROP TABLE foo") + + """ % a, sourceless=True) + + script = ScriptDirectory.from_config(self.cfg) + eq_(script.get_heads(), []) + + self.cfg.set_main_option("sourceless", "true") + script = ScriptDirectory.from_config(self.cfg) + eq_(script.get_heads(), [a]) |