From e0fe54bd57753a2821374dcbdcff2676d3d3be1d Mon Sep 17 00:00:00 2001 From: kernmod Date: Sat, 14 Jun 2025 22:47:02 -0300 Subject: [PATCH] Fix pyproject.toml formatting when appending alembic config - Modified _append_template to ensure proper TOML section spacing - Added special handling for .toml files to insert two newlines between sections - Added tests to prevent regression - Fixes #1679 --- alembic/script/base.py | 56 +++++++++++++++++++++++++++++++---- tests/test_append_template.py | 41 +++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 tests/test_append_template.py diff --git a/alembic/script/base.py b/alembic/script/base.py index 16c014b8..6cf9660b 100644 --- a/alembic/script/base.py +++ b/alembic/script/base.py @@ -554,13 +554,57 @@ def run_env(self) -> None: def env_py_location(self) -> str: return str(Path(self.dir, "env.py")) - def _append_template(self, src: Path, dest: Path, **kw: Any) -> None: - with util.status( - f"Appending to existing {dest.absolute()}", - **self.messaging_opts, - ): + + def _append_template( + self, template_path: Path, output_path: Union[str, Path], **kw: Any + ) -> None: + """ + Append content from a Mako template to an existing file. + + Special handling for TOML files to ensure proper formatting between + sections, addressing issue #1679. + + Args: + template_path: Path to the template file + output_path: Path to the output file to append to + **kw: Keyword arguments for template rendering + """ + output_path = Path(output_path) + + # For TOML files, we need special handling to ensure proper spacing + if output_path.suffix.lower() == '.toml' and output_path.exists(): + # Read existing content + with open(output_path, 'r', encoding=self.output_encoding) as f: + existing_content = f.read() + + # Check if we need to add newlines before appending + existing_content = existing_content.rstrip() + + # Save the modified content back + with open(output_path, 'w', encoding=self.output_encoding) as f: + f.write(existing_content) + + # TOML convention: ensure 2 newlines between top-level sections + # The template should start with [tool.alembic], so we need proper spacing + if existing_content: + f.write('\n\n') + + # Now append the template content + util.template_to_file( + template_path, + output_path, + self.output_encoding, + append=True, + **kw + ) + else: + # For non-TOML files or new files, just use template_to_file directly util.template_to_file( - src, dest, self.output_encoding, append=True, **kw + template_path, + output_path, + self.output_encoding, + append=True, + **kw ) def _generate_template(self, src: Path, dest: Path, **kw: Any) -> None: diff --git a/tests/test_append_template.py b/tests/test_append_template.py new file mode 100644 index 00000000..0eb948f5 --- /dev/null +++ b/tests/test_append_template.py @@ -0,0 +1,41 @@ +"""Tests for pyproject.toml template functionality.""" +import os +import tempfile +from pathlib import Path + +from alembic import command +from alembic.config import Config +from alembic.testing import TestBase + + +class TestPyprojectTemplate(TestBase): + """Test the pyproject template, particularly issue #1679.""" + + def test_init_pyproject_existing_file_formatting(self): + """Test that appending to existing pyproject.toml maintains proper spacing.""" + with tempfile.TemporaryDirectory() as tmpdir: + os.chdir(tmpdir) + + # Create existing pyproject.toml + pyproject_path = Path("pyproject.toml") + pyproject_path.write_text( + '[tool.black]\ntarget-version = [\'py37\']\n\n' + '[tool.mypy]\npython_version = "3.10"' + ) + + # Run init + config = Config("pyproject.toml") + command.init(config, "alembic", template="pyproject") + + # Check result + result = pyproject_path.read_text() + + # Verify no concatenation (issue #1679) + assert 'python_version = "3.10"[tool.alembic]' not in result + + # Verify proper spacing + assert '\n\n[tool.alembic]' in result + + # Verify content preserved + assert '[tool.black]' in result + assert '[tool.mypy]' in result \ No newline at end of file