Fix: preserve quoted_name(quote=...) semantics when rendering schema in autogenerate - #1843
Fix: preserve quoted_name(quote=...) semantics when rendering schema in autogenerate#1843pranjalm37 wants to merge 1 commit into
Conversation
…in autogenerate When a table's schema is set using an explicit quoted_name(name, quote=True/False), the autogenerate render code collapsed it down to a plain string literal, silently dropping the caller's explicit quoting preference in the generated migration file. Adds _render_schema_ident() which preserves the quoted_name(...) wrapper when quote is explicitly set, and otherwise preserves prior behavior (including support for custom repr()-able schema objects). Applied consistently across all schema= render sites: create/drop table, index, column, constraints, and the batch_alter_table header. Fixes sqlalchemy#1526
MohammedAlkindi
left a comment
There was a problem hiding this comment.
Ran this on Windows 11 / Python 3.13.13, since alembic's CI matrix is ubuntu-22.04 / ubuntu-latest only.
No new failures. Full suite on this branch:
$ python -m pytest tests/ -q
5 failed, 1814 passed, 132 skipped in 39.87s
The 5 are all ScriptNamingTest timezone tests (test_custom_tz, test_custom_tz_lowercase, test_custom_tz_utc, test_custom_tzdata_tz, test_generates_a_date) failing on zoneinfo with no system tzdata, which is a Windows-only environment gap. They fail identically on a clean upstream/main checkout on this machine — same count, same five names — so the delta from this PR is zero.
The new test has teeth. Reverting only the source file and keeping the test:
$ git checkout upstream/main -- alembic/autogenerate/render.py
$ python -m pytest tests/test_autogen_render.py -q
FAILED tests/test_autogen_render.py::AutogenRenderTest::test_render_table_w_quoted_name_schema
AssertionError:
"...schema='database.schema')"
!= "...schema=quoted_name('database.schema', False))"
1 failed, 178 passed, 2 skipped
and with render.py restored, 179 passed, 2 skipped. So it fails before and passes after rather than only covering the happy path — the rendered output genuinely loses the quote=False marker without the change, which is the reported bug.
Not checked: PostgreSQL/MySQL/Oracle behaviour (I ran the default sqlite configuration), and I did not review the render.py changes for design intent — this is a platform and regression-coverage report, not a design review.
MohammedAlkindi
left a comment
There was a problem hiding this comment.
Checked this out and ran it on Windows 11 (10.0.26200), Python 3.13.13, against main at c116cbc0.
Test evidence:
pytest tests/test_autogen_render.py |
passed | skipped |
|---|---|---|
main |
178 | 2 |
| this PR | 179 | 2 |
The +1 is the PR's own test, and no existing test changes state.
Fail-before confirmed, which is the part worth having: with alembic/autogenerate/render.py reverted to main and the new test kept, test_render_table_w_quoted_name_schema fails. So the test genuinely discriminates the fix rather than just documenting current behaviour.
The diagnosis looks right to me. quoted_name is a str subclass, so %r collapses it to a plain string literal and the quote= flag simply disappears from the generated migration — the rendered file then re-renders with default quoting rules, which is exactly the #1526 symptom. Routing through _render_schema_ident and emitting the explicit wrapper preserves the caller's intent.
Coverage check: I grepped render.py for any remaining schema render that still goes through %r, and found none — _add_table, _drop_table, _render_modify_table and the index path all go through the new helper now. The change looks complete rather than partial.
One thing I checked rather than flagged. _render_schema_ident returns str | None, and _render_modify_table interpolates it with %s without an if op.schema: guard, unlike _add_table and _drop_table. That looked like it might emit a literal schema=None, but it's fine and unchanged: %r of None and %s of None both produce None, so a schema-less batch op renders schema=None exactly as it did before. Noting it only so the asymmetry between guarded and unguarded call sites doesn't read as an oversight later.
I ran only test_autogen_render.py rather than the full suite, since that is the file this touches — worth saying explicitly so the numbers above aren't read as a whole-suite result.
Disclosure: I used an AI assistant while reviewing. The before/after runs, the fail-before check, and the render.py grep were executed by me on the machine described.
Summary
Fixes #1526.
When a table/index/constraint's schema is set using an explicit
quoted_name(name, quote=True)orquoted_name(name, quote=False), theautogenerate render code (
_ident()inalembic/autogenerate/render.py)collapsed the value down to a plain string before formatting it with
%r,so the caller's explicit quoting preference was silently dropped from the
generated migration file.
Fix
Adds
_render_schema_ident(), used at everyschema=render site(create/drop table, index, column, check/unique constraint, drop_constraint,
alter_column, and the
batch_alter_tableheader):quoted_namewithquoteexplicitly set (True/False),render it as
quoted_name('name', True/False)and add the necessary importto the generated migration file.
repr()of the original value, preserving priorbehavior — including for objects with a custom
__repr__(there's anexisting test,
test_drop_unique_constraint_schema_reprobj, covering thatcase, which still passes).
Test plan
test_render_table_w_quoted_name_schema, matching the repro fromMigration autogeneration should respect schemas defined using quoted_name #1526, asserting both the rendered code and that the
quoted_nameimportis added.
pytest tests/ --ignore=tests/test_post_write.py --ignore=tests/test_mssql.py --ignore=tests/test_oracle.py --ignore=tests/test_mysql.py --ignore=tests/test_postgresql.py→ 1562 passed, 133 skipped (DB-specific suites skipped, no local DB available;test_post_write.pyfailures are pre-existing/unrelated — they fail identically onmainwithout this change, due toblacknot being installed in the hook environment).black --checkandflake8pass on both changed files.docs/build/unreleased/1526.rst.