Repository navigation
Report kept env.py failures and skip empty autogenerated revisions - #7809
Conversation
[Medium impact] The PR appears safe to merge; no new actionable issues were found. SummaryThis PR reports failures from kept
Reviews (7) · Last reviewed commit: "Build the expected revision path with Pa..." · Reviewed by Greptile |
There was a problem hiding this comment.
All reported issues were addressed across 18 files
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
Merging this PR will improve performance by 4.64%1 warning
Performance Changes
Tip Curious why performance improved? Comment Comparing |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dxjadq3qkhKnqZR2b9WBFi
71cefac to
e5930a6
Compare
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dxjadq3qkhKnqZR2b9WBFi
There was a problem hiding this comment.
1 issue found across 3 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="news/+detect-declarative-bases.breaking.md">
<violation number="1" location="news/+detect-declarative-bases.breaking.md:1">
P3: This breaking note overstates auto-detection: Reflex does not migrate every loaded `DeclarativeBase`; it only detects eligible module-level bases and excludes function-local and legacy `declarative_base()` bases. Say “every eligible module-level `DeclarativeBase`” so users are not promised migration for bases they still need to register.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
View guided diff | Turn on auto-fix | Re-trigger cubic
8464b7c to
75b8f29
Compare
A hand-edited 0.10.x alembic/env.py, kept by 0.11 because it no longer has the stock `target_metadata = None` line, is now executed by alembic. With the placeholder URL of alembic.ini it raised NoSuchModuleError, which crashed `reflex run` and gave `reflex db` a raw traceback, and without the template's empty-revision skip every makemigrations and `migrate(autogenerate=True)` wrote an empty revision. - Classify env.py as template (docstring marker present since 0.11.0a1), legacy (stock alembic) or custom, and warn once per process about a custom one. - Report any exception an alembic run raises: the startup schema check logs it and lets the app start, `reflex db` exits 1 with it, and `migrate()` logs it and returns False when env.py is custom (it still raises under the template). The custom-env hint says to move env.py aside and run `reflex db init`; the traceback is logged at debug. - Restore the root handlers and re-enable the loggers a custom env.py disables through fileConfig, so Reflex's own messages still show. - makemigrations and migrate(autogenerate=True) run `alembic check` first and write a revision only when it reports changes. - A second legacy replacement keeps the earlier env.old, backing up to the first free name of env.old, env.old.1, ... Fixes FINDING-005, FINDING-006 and FINDING-017 of the 0.11.0a1 pre-release testing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dxjadq3qkhKnqZR2b9WBFi
test_custom_env_writes_no_empty_revision made t1 nullable and added a NOT NULL t2 under a stock alembic env.py, which has no render_as_batch. The revision then holds a plain `ALTER TABLE ... ALTER COLUMN ... DROP NOT NULL`, which only recent SQLite builds accept: it passed with the SQLite 3.53 of uv's Pythons and failed on the system Pythons of the Windows runners and ubuntu 3.14 (SQLite 3.45 reproduces it). The step now adds a nullable column, and the test asserts the revision has no alter_column. `reflex db makemigrations` also says when it writes no revision. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dxjadq3qkhKnqZR2b9WBFi
75b8f29 to
38150cb
Compare
…onto main Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dxjadq3qkhKnqZR2b9WBFi
|
CodSpeed on 38150cb flags
Generated by Claude Code |
A failing revision script was reported in one line, without its file or line, even under the template env.py; only REFLEX_LOGLEVEL=debug showed the traceback. With a hand-edited env.py that works, the same failure also got the "move env.py aside" hint, which points at the wrong file. report_failure now hints and keeps the traceback at debug level only when the innermost frame in the alembic script directory is a custom env.py. Any other failure is logged with its traceback.
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
A SyntaxError in a hand-edited env.py is raised while alembic compiles it, so no traceback frame is in the script directory and the "move env.py aside" hint was lost. Its filename is now taken as the innermost location. The frame is compared with the script directory's env.py itself, so a revision script named env.py under versions/ keeps its traceback and gets no hint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dxjadq3qkhKnqZR2b9WBFi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dxjadq3qkhKnqZR2b9WBFi
There was a problem hiding this comment.
All reported issues were addressed across 2 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
View guided diff | Turn on auto-fix | Re-trigger cubic
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dxjadq3qkhKnqZR2b9WBFi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dxjadq3qkhKnqZR2b9WBFi
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dxjadq3qkhKnqZR2b9WBFi
…7809) * Report kept env.py failures and skip empty revisions A hand-edited 0.10.x alembic/env.py, kept by 0.11 because it no longer has the stock `target_metadata = None` line, is now executed by alembic. With the placeholder URL of alembic.ini it raised NoSuchModuleError, which crashed `reflex run` and gave `reflex db` a raw traceback, and without the template's empty-revision skip every makemigrations and `migrate(autogenerate=True)` wrote an empty revision. - Classify env.py as template (docstring marker present since 0.11.0a1), legacy (stock alembic) or custom, and warn once per process about a custom one. - Report any exception an alembic run raises: the startup schema check logs it and lets the app start, `reflex db` exits 1 with it, and `migrate()` logs it and returns False when env.py is custom (it still raises under the template). The custom-env hint says to move env.py aside and run `reflex db init`; the traceback is logged at debug. - Restore the root handlers and re-enable the loggers a custom env.py disables through fileConfig, so Reflex's own messages still show. - makemigrations and migrate(autogenerate=True) run `alembic check` first and write a revision only when it reports changes. - A second legacy replacement keeps the earlier env.old, backing up to the first free name of env.old, env.old.1, ... Fixes FINDING-005, FINDING-006 and FINDING-017 of the 0.11.0a1 pre-release testing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dxjadq3qkhKnqZR2b9WBFi * Keep the custom env.py test within what every SQLite runs test_custom_env_writes_no_empty_revision made t1 nullable and added a NOT NULL t2 under a stock alembic env.py, which has no render_as_batch. The revision then holds a plain `ALTER TABLE ... ALTER COLUMN ... DROP NOT NULL`, which only recent SQLite builds accept: it passed with the SQLite 3.53 of uv's Pythons and failed on the system Pythons of the Windows runners and ubuntu 3.14 (SQLite 3.45 reproduces it). The step now adds a nullable column, and the test asserts the revision has no alter_column. `reflex db makemigrations` also says when it writes no revision. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dxjadq3qkhKnqZR2b9WBFi * Keep revision script tracebacks and hint only at a failing env.py A failing revision script was reported in one line, without its file or line, even under the template env.py; only REFLEX_LOGLEVEL=debug showed the traceback. With a hand-edited env.py that works, the same failure also got the "move env.py aside" hint, which points at the wrong file. report_failure now hints and keeps the traceback at debug level only when the innermost frame in the alembic script directory is a custom env.py. Any other failure is logged with its traceback. * Hint at a custom env.py that fails to compile; match env.py by its path A SyntaxError in a hand-edited env.py is raised while alembic compiles it, so no traceback frame is in the script directory and the "move env.py aside" hint was lost. Its filename is now taken as the innermost location. The frame is compared with the script directory's env.py itself, so a revision script named env.py under versions/ keeps its traceback and gets no hint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dxjadq3qkhKnqZR2b9WBFi * Build the expected revision path with Path in the traceback test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dxjadq3qkhKnqZR2b9WBFi --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Farhan <www.mfarvirus@gmail.com> (cherry picked from commit 1e20c11)
…last open Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Dxjadq3qkhKnqZR2b9WBFi
All Submissions:
Type of change
Changes To Core Features:
Fixes FINDING-005, FINDING-006 and FINDING-017 from the reflex 0.11.0a1 pre-release testing (artifacts on branch
claude/vibrant-pasteur-3921g3,prerelease-testing/runs/2026-11-11-v0.11.0a1/). FINDING-012 is fixed separately in #7815, which is also stacked on #7797.Defect
alembic/env.pyby hand (target_metadata = SQLModel.metadata) and left thedriver://placeholder URL in alembic.ini. 0.11 keeps that file, because legacy detection only looks for the literaltarget_metadata = None, and then runs it. The result:reflex rundies at startup withNoSuchModuleError: Can't load plugin: sqlalchemy.dialects:driver,reflex db status/migrate/makemigrationsexit 1 with a raw traceback, andreflex db initonly says "already initialized". 0.10.1 never ran env.py and worked. The stock env.py also callsfileConfig(alembic.ini), which disables everyreflex.*logger, so Reflex's own messages vanished for the rest of the process.reflex db makemigrationswrote an emptypassrevision.rx.model.migrate(autogenerate=True)at import chained several of them on each dev start, because only the template env.py skipped empty revisions.alembic/env.old.Fix
migrations.env_kind()sorts env.py into one of three kinds:template: it contains the docstring markerGenerated byreflex db init. Every template since 0.11.0a1 has it, so 0.11.0a1 templates count as the template.legacy: it containstarget_metadata = None.custom: anything else.check_env()logs a warning once per process (via thededupelog filter) when env.py is custom: "alembic/env.py is not the Reflex template, so what the template adds may be missing: ... Move it aside and run reflex db init to adopt the template; existing revisions are kept."Any exception from an alembic run, not only
CommandError, is now reported bymigrations.report_failure(). The message isAlembic failed with <Type>: <message>. When the failure comes from a custom env.py itself (the innermost traceback frame in the alembic script directory isenv.py), it adds the hint "alembic/env.py is not the Reflex template: move it aside and run reflex db init to write the current template." and the traceback goes to the debug log. Any other failure, such as a failing revision script under either env.py, is logged with its traceback and without the hint. Each caller handles it differently:check_schema_up_to_date(startup and afterdb migrate) logs it, and the app still starts. This restores the 0.10.1 behaviour of starting._alembic_errors(reflex db ...) exits 1 with the message.migrate()logs it and returnsFalsewhen env.py is custom. Under the template it still raises: there the error is real, and a script callingmigrate()must see it.migrations.run()restores the root handlers and level after each run, and re-enables the loggers that an env.py'sfileConfigdisabled.migrations.autogenerate()runsalembic checkfirst and writes a revision only onAutogenerateDiffsDetected. This is what 0.10.1 and the deprecatedalembic_autogeneratealready did.reflex db makemigrations,migrate(autogenerate=True)andalembic_autogeneratenow all use it, so the result no longer depends on env.py. When nothing changed,makemigrationslogs "No model changes detected; no revision was written."update_legacy_env()saves the backup under the first free name:env.old, thenenv.old.1, and so on.Docs:
docs/database/overview.md(upgrade note and the empty-revision sentence).Known behaviour
These were confirmed by the release owner.
migrate()logs and continues. An import-timerx.model.migrate()logs any non-CommandErrorfailure of a custom env.py and returnsFalseinstead of raising, so the app still starts. That includes a failing revision script, which is logged with its traceback. Explicit callers who need the error can check theFalsereturn. Under the template env.py,migrate()raises as before.reflex db initwould just write the same file again.Test plan
New tests in
tests/units/utils/test_migrations.pyuse real alembic and sqlite. They are parametrized over sqlmodel and sqlalchemy where the fixture supports it. All of them failed before the fix:All of them pass after the fix.
test_revision_script_failure_keeps_its_traceback[template|custom]covers a revision script whoseupgrade()raises: the error is logged with its traceback and without the hint. Both cases failed before the third commit.CI fix (second commit): the first push failed
test_custom_env_writes_no_empty_revisionon the Windows legs and on ubuntu 3.14, withnear "ALTER": syntax error. Under the stock env.py, which has norender_as_batch, that test's model change produced a plainALTER TABLE thing ALTER COLUMN t1 DROP NOT NULL. Only recent SQLite builds accept that statement: the SQLite 3.53 in uv's Pythons does, while the system Pythons on those runners have an older build. I reproduced it locally with SQLite 3.45.1 (/usr/bin/python3.12). The test now adds a nullable column and asserts the revision has noalter_column. It passes on SQLite 3.45 and 3.53. Product code is unaffected: the Reflex template uses batch mode.uv run pytest tests/units --cov(withouttests/units/reflex_bench/drivers/test_browser.py, since there is no Chromium in this sandbox): 15652 passed, 66 skipped, coverage 81.45%. After the rebase onto 93beac9, the migration and model tests pass again: 50/50 locally, and test_migrations 37/37 on SQLite 3.45.uv run ruff check .anduv run ruff format --check .are clean.uv run pyright reflex testsgives 0 errors.verify_dbu/scripts/run_all.shto an editable install of this branch, using form-designer seeded on 0.10.1:reflex runcomes up, env.old matches 0.10.1, and no revision is written.migrate():reflex runcomes UP (8–10 s) with no traceback, and logs the warning plusAlembic failed with NoSuchModuleError ... move it aside and run reflex db init.db status/migrate/makemigrationsexit 1 with the same message and no traceback.db initshows the warning before "already initialized". The remedy (move it aside, thendb init) works.makemigrationsadd 0 revisions (before: 2).migrate(autogenerate=True)at import plusreflex runadds 0 (before: 3). The railway-style env.py adds 0 (before: 2).env.oldkeeps the custom edit andenv.old.1holds the stock file.Notes
makemigrationsnow runs env.py twice when there are changes (check, then revision), as 0.10.1 did.reflex runshows it in the main process and again in the backend worker.🤖 Generated with Claude Code
https://claude.ai/code/session_01Dxjadq3qkhKnqZR2b9WBFi