From 4effc8124fa2ad5aab42119e4e7aa1452982d257 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Wed, 6 Sep 2017 10:20:39 -0400 Subject: [PATCH] Use env in history when revision_history=true The ``alembic history`` command will now make use of the revision environment ``env.py`` unconditionally if the ``revision_environment`` configuration flag is set to True. Previously, the environment would only be invoked if the history specification were against a database-stored revision token. Change-Id: I2f9dc2554c8a4523f93248859587e8e276fe4d4a Fixes: #447 --- alembic/command.py | 8 ++++ docs/build/tutorial.rst | 7 +++- docs/build/unreleased/447.rst | 9 +++++ tests/test_command.py | 74 +++++++++++++++++++++++++++++++---- 4 files changed, 90 insertions(+), 8 deletions(-) create mode 100644 docs/build/unreleased/447.rst diff --git a/alembic/command.py b/alembic/command.py index 02805281..86750051 100644 --- a/alembic/command.py +++ b/alembic/command.py @@ -342,6 +342,10 @@ def history(config, rev_range=None, verbose=False): else: base = head = None + environment = util.asbool( + config.get_main_option("revision_environment") + ) + def _display_history(config, script, base, head): for sc in script.walk_revisions( base=base or "base", @@ -357,6 +361,8 @@ def history(config, rev_range=None, verbose=False): _display_history(config, script, base, rev) elif base is None: _display_history(config, script, rev, head) + else: + _display_history(config, script, base, head) return [] with EnvironmentContext( @@ -370,6 +376,8 @@ def history(config, rev_range=None, verbose=False): _display_history_w_current(config, script, head=head) elif head == "current": _display_history_w_current(config, script, base=base) + elif environment: + _display_history_w_current(config, script, base, head) else: _display_history(config, script, base, head) diff --git a/docs/build/tutorial.rst b/docs/build/tutorial.rst index 2f71b022..735c481f 100644 --- a/docs/build/tutorial.rst +++ b/docs/build/tutorial.rst @@ -246,9 +246,14 @@ This file contains the following features: a file that can be customized by the developer. A multiple database configuration may respond to multiple keys here, or may reference other sections 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 + generating new revision files, as well as when running the ``alembic history`` + command. + + .. versionchanged:: 0.9.6 the ``alembic history`` command uses the environment + unconditionally when ``revision_environment`` is set to true. * ``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 diff --git a/docs/build/unreleased/447.rst b/docs/build/unreleased/447.rst new file mode 100644 index 00000000..6aefb2f8 --- /dev/null +++ b/docs/build/unreleased/447.rst @@ -0,0 +1,9 @@ +.. change:: + :tags: feature, commands + :tickets: 447 + + The ``alembic history`` command will now make use of the revision + environment ``env.py`` unconditionally if the ``revision_environment`` + configuration flag is set to True. Previously, the environment would + only be invoked if the history specification were against a database-stored + revision token. \ No newline at end of file diff --git a/tests/test_command.py b/tests/test_command.py index 58a827f3..995911f9 100644 --- a/tests/test_command.py +++ b/tests/test_command.py @@ -30,22 +30,58 @@ class HistoryTest(_BufMixin, TestBase): cls.env = staging_env() cls.cfg = _sqlite_testing_config() cls.a, cls.b, cls.c = three_rev_fixture(cls.cfg) + cls._setup_env_file() @classmethod def teardown_class(cls): clear_staging_env() - def _eq_cmd_output(self, buf, expected): + def teardown(self): + self.cfg.set_main_option("revision_environment", "false") + + @classmethod + def _setup_env_file(self): + env_file_fixture(r""" + +from sqlalchemy import MetaData, engine_from_config +target_metadata = MetaData() + +engine = engine_from_config( + config.get_section(config.config_ini_section), + prefix='sqlalchemy.') + +connection = engine.connect() + +context.configure( + connection=connection, target_metadata=target_metadata +) + +try: + with context.begin_transaction(): + config.stdout.write(u"environment included OK\n") + context.run_migrations() +finally: + connection.close() + +""") + + def _eq_cmd_output(self, buf, expected, env_token=False): script = ScriptDirectory.from_config(self.cfg) # test default encode/decode behavior as well, # rev B has a non-ascii char in it + a coding header. + + assert_lines = [ + script.get_revision(rev).log_entry + for rev in expected + ] + if env_token: + assert_lines.insert(0, "environment included OK") + eq_( buf.getvalue().decode("ascii", 'replace').strip(), - "\n".join([ - script.get_revision(rev).log_entry - for rev in expected - ]).encode("ascii", "replace").decode("ascii").strip() + "\n".join(assert_lines). + encode("ascii", "replace").decode("ascii").strip() ) def test_history_full(self): @@ -53,11 +89,23 @@ class HistoryTest(_BufMixin, TestBase): command.history(self.cfg, verbose=True) self._eq_cmd_output(buf, [self.c, self.b, self.a]) + def test_history_full_environment(self): + self.cfg.stdout = buf = self._buf_fixture() + self.cfg.set_main_option("revision_environment", "true") + command.history(self.cfg, verbose=True) + self._eq_cmd_output(buf, [self.c, self.b, self.a], env_token=True) + def test_history_num_range(self): self.cfg.stdout = buf = self._buf_fixture() command.history(self.cfg, "%s:%s" % (self.a, self.b), verbose=True) self._eq_cmd_output(buf, [self.b, self.a]) + def test_history_num_range_environment(self): + self.cfg.stdout = buf = self._buf_fixture() + self.cfg.set_main_option("revision_environment", "true") + command.history(self.cfg, "%s:%s" % (self.a, self.b), verbose=True) + self._eq_cmd_output(buf, [self.b, self.a], env_token=True) + def test_history_base_to_num(self): self.cfg.stdout = buf = self._buf_fixture() command.history(self.cfg, ":%s" % (self.b), verbose=True) @@ -68,6 +116,12 @@ class HistoryTest(_BufMixin, TestBase): command.history(self.cfg, "%s:" % (self.a), verbose=True) self._eq_cmd_output(buf, [self.c, self.b, self.a]) + def test_history_num_to_head_environment(self): + self.cfg.stdout = buf = self._buf_fixture() + self.cfg.set_main_option("revision_environment", "true") + command.history(self.cfg, "%s:" % (self.a), verbose=True) + self._eq_cmd_output(buf, [self.c, self.b, self.a], env_token=True) + def test_history_num_plus_relative(self): self.cfg.stdout = buf = self._buf_fixture() command.history(self.cfg, "%s:+2" % (self.a), verbose=True) @@ -87,13 +141,19 @@ class HistoryTest(_BufMixin, TestBase): command.stamp(self.cfg, self.b) self.cfg.stdout = buf = self._buf_fixture() command.history(self.cfg, "current:", verbose=True) - self._eq_cmd_output(buf, [self.c, self.b]) + self._eq_cmd_output(buf, [self.c, self.b], env_token=True) def test_history_current_to_head_as_base(self): command.stamp(self.cfg, "base") self.cfg.stdout = buf = self._buf_fixture() command.history(self.cfg, "current:", verbose=True) - self._eq_cmd_output(buf, [self.c, self.b, self.a]) + self._eq_cmd_output(buf, [self.c, self.b, self.a], env_token=True) + + def test_history_include_env(self): + self.cfg.stdout = buf = self._buf_fixture() + self.cfg.set_main_option("revision_environment", "true") + command.history(self.cfg, verbose=True) + self._eq_cmd_output(buf, [self.c, self.b, self.a], env_token=True) class CurrentTest(_BufMixin, TestBase): -- 2.47.2