From: Mike Bayer Date: Sat, 9 Apr 2016 01:32:07 +0000 (-0400) Subject: - Errors which occur within the Mako render step are now intercepted X-Git-Tag: rel_0_8_6~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=9b32cb748550cf5c412a9d34c1c209aa168c9c08;p=thirdparty%2Fsqlalchemy%2Falembic.git - Errors which occur within the Mako render step are now intercepted and raised as CommandErrors like other failure cases; the Mako exception itself is written using template-line formatting to a temporary file which is named in the exception message. fixes #367 --- diff --git a/alembic/util/__init__.py b/alembic/util/__init__.py index e53199bf..22411110 100644 --- a/alembic/util/__init__.py +++ b/alembic/util/__init__.py @@ -9,10 +9,7 @@ from .pyfiles import ( # noqa from .sqla_compat import ( # noqa sqla_07, sqla_079, sqla_08, sqla_083, sqla_084, sqla_09, sqla_092, sqla_094, sqla_099, sqla_100, sqla_105, sqla_110) - - -class CommandError(Exception): - pass +from .exc import CommandError if not sqla_07: diff --git a/alembic/util/exc.py b/alembic/util/exc.py new file mode 100644 index 00000000..f7ad0211 --- /dev/null +++ b/alembic/util/exc.py @@ -0,0 +1,2 @@ +class CommandError(Exception): + pass diff --git a/alembic/util/pyfiles.py b/alembic/util/pyfiles.py index c4de0710..e6514035 100644 --- a/alembic/util/pyfiles.py +++ b/alembic/util/pyfiles.py @@ -3,14 +3,27 @@ import os import re from .compat import load_module_py, load_module_pyc from mako.template import Template +from mako import exceptions +import tempfile +from .exc import CommandError def template_to_file(template_file, dest, output_encoding, **kw): - with open(dest, 'wb') as f: - template = Template(filename=template_file) - f.write( - template.render_unicode(**kw).encode(output_encoding) - ) + template = Template(filename=template_file) + try: + output = template.render_unicode(**kw).encode(output_encoding) + except: + with tempfile.NamedTemporaryFile(suffix='.txt', delete=False) as ntf: + ntf.write( + exceptions.text_error_template(). + render_unicode().encode(output_encoding)) + fname = ntf.name + raise CommandError( + "Template rendering failed; see %s for a " + "template-oriented traceback." % fname) + else: + with open(dest, 'wb') as f: + f.write(output) def coerce_resource_to_filename(fname): @@ -63,7 +76,6 @@ def edit(path): """Given a source path, run the EDITOR for it""" import editor - from . import CommandError try: editor.edit(path) except Exception as exc: diff --git a/docs/build/changelog.rst b/docs/build/changelog.rst index 4ac59414..f42f1b60 100644 --- a/docs/build/changelog.rst +++ b/docs/build/changelog.rst @@ -6,6 +6,15 @@ Changelog .. changelog:: :version: 0.8.6 + .. change:: + :tags: bug, commands + :tickets: 367 + + Errors which occur within the Mako render step are now intercepted + and raised as CommandErrors like other failure cases; the Mako + exception itself is written using template-line formatting to + a temporary file which is named in the exception message. + .. change:: :tags: bug, postgresql :tickets: 365 diff --git a/tests/test_script_production.py b/tests/test_script_production.py index d2e397f1..66e311d1 100644 --- a/tests/test_script_production.py +++ b/tests/test_script_production.py @@ -16,6 +16,8 @@ import os import datetime import sqlalchemy as sa from sqlalchemy.engine.reflection import Inspector +from alembic.util import CommandError +import re env, abc, def_ = None, None, None @@ -220,6 +222,8 @@ class RevisionCommandTest(TestBase): ) + + class CustomizeRevisionTest(TestBase): def setUp(self): self.env = staging_env() @@ -812,3 +816,24 @@ down_revision = ${repr(down_revision)} with open(rev.path) as f: text = f.read() assert "somearg: somevalue" in text + + def test_bad_render(self): + env_file_fixture(""" +context.configure(dialect_name='sqlite', template_args={"somearg":"somevalue"}) +""") + script_file_fixture(""" + <% z = x + y %> +""") + + try: + command.revision(self.cfg, message="some rev") + except CommandError as ce: + m = re.match( + r"^Template rendering failed; see (.+?) " + "for a template-oriented", + str(ce) + ) + assert m, "Command error did not produce a file" + contents = open(m.group(1)).read() + os.remove(m.group(1)) + assert "<% z = x + y %>" in contents