]> git.ipfire.org Git - thirdparty/sqlalchemy/alembic.git/commitdiff
honor get_templates_path() first
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 21 May 2025 21:14:37 +0000 (17:14 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 21 May 2025 21:16:34 +0000 (17:16 -0400)
Fixed regression caused by the ``pathlib`` refactoring that removed the use
of :meth:`.Config.get_template_directory` as the canonical source of
templates; the method is still present however it no longer would be
consulted for a custom config subclass, as was the case with flask-migrate.

Fixed regression caused by the ``pathlib`` refactoring where the "missing
template" error message failed to render the name of the template that
could not be found.

Fixes: #1659
Fixes: #1660
Change-Id: I1244f19c9f288eb52c8e9ffcc3835824d6b74bb6

alembic/command.py
alembic/config.py
docs/build/unreleased/1660.rst [new file with mode: 0644]
tests/test_command.py

index 6080f3639c68d1152ae87815db60ed15a686046e..8e4854744ab32cd89f06c870a62a2cc711543c15 100644 (file)
@@ -68,7 +68,7 @@ def init(
     template_path = config._get_template_path() / template
 
     if not template_path.exists():
-        raise util.CommandError("No such template {template_path}")
+        raise util.CommandError(f"No such template {template_path}")
 
     # left as os.access() to suit unit test mocking
     if not os.access(directory_path, os.F_OK):
index eb98bcdf9f864c52ca3040fa278e07b63dc22531..1137578809402bc28ce9b4af0907ef6c0a847c1c 100644 (file)
@@ -264,7 +264,10 @@ class Config:
         commands.
 
         """
-        return self._get_template_path().as_posix()
+        import alembic
+
+        package_dir = Path(alembic.__file__).absolute().parent
+        return str(package_dir / "templates")
 
     def _get_template_path(self) -> Path:
         """Return the directory where Alembic setup templates are found.
@@ -275,10 +278,7 @@ class Config:
         .. versionadded:: 1.16.0
 
         """
-        import alembic
-
-        package_dir = Path(alembic.__file__).absolute().parent
-        return package_dir / "templates"
+        return Path(self.get_template_directory())
 
     @overload
     def get_section(
diff --git a/docs/build/unreleased/1660.rst b/docs/build/unreleased/1660.rst
new file mode 100644 (file)
index 0000000..8184778
--- /dev/null
@@ -0,0 +1,16 @@
+.. change::
+    :tags: bug, command
+    :tickets: 1660
+
+    Fixed regression caused by the ``pathlib`` refactoring that removed the use
+    of :meth:`.Config.get_template_directory` as the canonical source of
+    templates; the method is still present however it no longer would be
+    consulted for a custom config subclass, as was the case with flask-migrate.
+
+.. change::
+    :tags: bug, command
+    :tickets: 1659
+
+    Fixed regression caused by the ``pathlib`` refactoring where the "missing
+    template" error message failed to render the name of the template that
+    could not be found.
index 44597d2ec6f00041dc5049ef10fadbc6c5fbeb6d..167cfbeed095a00f26fd80dd4c65711f71560375 100644 (file)
@@ -7,6 +7,7 @@ from io import TextIOWrapper
 import os
 import pathlib
 import re
+import shutil
 from typing import cast
 
 from sqlalchemy import exc as sqla_exc
@@ -1145,7 +1146,6 @@ class CommandLineTest(TestBase):
     def setup_class(cls):
         cls.env = staging_env()
         cls.cfg = _sqlite_testing_config()
-        cls.a, cls.b, cls.c = three_rev_fixture(cls.cfg)
 
     def tearDown(self):
         os.environ.pop("ALEMBIC_CONFIG", None)
@@ -1520,6 +1520,70 @@ class CommandLineTest(TestBase):
                 ],
             )
 
+    @testing.fixture
+    def custom_template_fixture(self):
+        templates_path = pathlib.Path(
+            _get_staging_directory(), "my_special_templates_place"
+        )
+
+        os.makedirs(templates_path / "mytemplate")
+
+        with pathlib.Path(templates_path, "mytemplate", "myfile.txt").open(
+            "w"
+        ) as file_:
+            file_.write("This is myfile.txt")
+        with pathlib.Path(templates_path, "mytemplate", "README").open(
+            "w"
+        ) as file_:
+            file_.write("This is my template")
+        with pathlib.Path(
+            templates_path, "mytemplate", "alembic.ini.mako"
+        ).open("w") as file_:
+            file_.write("[alembic]\nscript_directory=%(here)s\n")
+
+        class MyConfig(config.Config):
+            def get_template_directory(self) -> str:
+                return templates_path.as_posix()
+
+        yield MyConfig(self.cfg.config_file_name)
+
+        shutil.rmtree(templates_path)
+
+    @testing.variation("cmd", ["list_templates", "init"])
+    def test_init_custom_template_location(self, cmd, custom_template_fixture):
+        """test #1660"""
+
+        cfg = custom_template_fixture
+
+        if cmd.init:
+            path = pathlib.Path(_get_staging_directory(), "foobar")
+            command.init(cfg, directory=path.as_posix(), template="mytemplate")
+
+            eq_(
+                (path / "myfile.txt").open().read(),
+                "This is myfile.txt",
+            )
+        elif cmd.list_templates:
+            cfg.stdout = buf = StringIO()
+            command.list_templates(cfg)
+            assert buf.getvalue().startswith(
+                "Available templates:\n\nmytemplate - This is my template"
+            )
+
+        else:
+            cmd.fail()
+
+    def test_init_no_such_template(self):
+        """test #1659"""
+
+        path = os.path.join(_get_staging_directory(), "foobar")
+
+        with expect_raises_message(
+            util.CommandError,
+            r"No such template .*asfd",
+        ):
+            command.init(self.cfg, directory=path, template="asfd")
+
     def test_version_text(self):
         buf = StringIO()
         to_mock = "sys.stdout"