Skip to content

feat: upgrade to Flask 3.1.3 and SQLAlchemy 1.4.53#7722

Closed
wtfiwtz wants to merge 2 commits into
getredash:masterfrom
orchestrated-io:feat/flask3-sqlalchemy14
Closed

feat: upgrade to Flask 3.1.3 and SQLAlchemy 1.4.53#7722
wtfiwtz wants to merge 2 commits into
getredash:masterfrom
orchestrated-io:feat/flask3-sqlalchemy14

Conversation

@wtfiwtz
Copy link
Copy Markdown

@wtfiwtz wtfiwtz commented Jun 1, 2026

Summary

  • Upgrade Flask 2.3 → 3.1.3, SQLAlchemy 1.3 → 1.4.53, Flask-SQLAlchemy 2.5 → 3.0.5
  • Update paginate, query ordering, metrics, and ORM compatibility code for SQLAlchemy 1.4
  • Rewrite embed CSP handling for flask-talisman compatibility with Flask 3
  • Improve test infrastructure (pytest.ini pythonpath, destinations package, JWT teardown)

Test plan

Notes

This is the significant migration PR. Depends on #7719 for authlib/Werkzeug foundation.
Does not include advocate→champion (#7721) or npm upgrades (#7720).

Made with Cursor

wtfiwtz and others added 2 commits June 1, 2026 15:30
Upgrade security-critical Python dependencies while staying on Flask 2.x.
Werkzeug 3.x is compatible with Flask 2.3.x and provides important security
fixes without requiring a Flask 3 migration.

Core dependency upgrades:
- authlib: 0.15.5 → 1.7.2 (fixes CVEs in OAuth/OIDC flows)
- cryptography: 43.0.1 → 48.0.0
- flask: 2.3.2 → 2.3.3
- flask-login: 0.6.0 → 0.6.3
- flask-wtf: 1.1.1 → 1.3.0
- itsdangerous: 2.1.2 → 2.2.0
- jinja2: 3.1.5 → 3.1.6
- pyjwt: 2.4.0 → 2.12.0
- pyopenssl: 24.2.1 → 26.2.0
- python-dotenv: 0.19.2 → 1.2.2
- requests: 2.32.3 → 2.33.0
- sqlparse: 0.5.0 → 0.5.4
- urllib3: 1.26.19 → 1.26.20
- werkzeug: 2.3.8 → 3.1.6

Added transitive dependency pins: pyasn1, mako, pynacl

Data source dependency upgrades:
- boto3/botocore: 1.28.8 → 1.43.7
- snowflake-connector-python: 3.12.3 → 4.5.0
- New additions: azure-core, grpcio, h11, httpcore, marshmallow

Dev dependency upgrades:
- pre-commit: 3.3.3 → 4.3.0
- Add filelock, pygments, virtualenv

Code changes for authlib 1.x and Werkzeug 3.x compatibility:
- Remove api_key_load_user_from_request from user_loader (belongs in request_loader)
- Add TESTING-mode reset_request_g_cache hook to prevent g leakage across test requests
- Pass explicit client_id/client_secret to oauth.register() (authlib 1.x API)
- Replace flask.globals._app_ctx_stack with current_app (Werkzeug 3 removal)

Co-authored-by: Cursor <cursoragent@cursor.com>
Migrate Redash to Flask 3.x and SQLAlchemy 1.4 with Flask-SQLAlchemy 3.0.
Builds on the Python security dependency upgrades (authlib 1.7, Werkzeug 3).

Dependency upgrades:
- flask: 2.3.3 → 3.1.3
- flask-migrate: 2.5.2 → 4.0.7
- flask-sqlalchemy: 2.5.1 → 3.0.5
- sqlalchemy: 1.3.24 → 1.4.53
- blinker: 1.6.2 → 1.9.0
- pyjwt: 2.12.0 → 2.13.0
- supervisor: 4.1.0 → 4.3.0
- nzalchemy: ^11.0.2 → ^11.1.2
- pytest: 7.4.0 → 9.0.3, coverage: 7.14.0, pytest-cov: 6.0.0

Compatibility changes:
- Flask-SQLAlchemy 3 paginate() API in handlers/base.py
- SQLAlchemy 1.4 query APIs in models, metrics, query_order
- SearchBaseQuery shim for sqlalchemy-searchable on SQLAlchemy 1.4
- ChangeTrackingMixin guards for detached/deleted ORM objects
- Rewrite csp_allows_embeding for flask-talisman dict CSP
- Soft-import supervisor_checks in cli/rq.py (no Python 3.13 wheel)
- TESTING guards for session management in auth and query execution
- Compact JSON in ConfigurationContainer.to_json()

Test infrastructure:
- pytest.ini: pythonpath, addopts, logging
- tests/destinations/__init__.py for proper package collection
- JWT test teardown fixes, CLI JSON expectation updates

Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 26 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="redash/cli/rq.py">

<violation number="1" location="redash/cli/rq.py:114">
P1: Click CLI commands do not use return values as exit codes. `return 1` in a `@manager.command()` function will be ignored and the process will exit with code 0, so monitoring scripts relying on exit status will incorrectly treat a failed healthcheck as successful.</violation>
</file>

<file name="redash/utils/query_order.py">

<violation number="1" location="redash/utils/query_order.py:104">
P1: Regression: get_mapper no longer raises on ambiguous table-to-mapper mappings</violation>
</file>

<file name="redash/models/changes.py">

<violation number="1" location="redash/models/changes.py:76">
P2: Broad `except Exception: pass` in audit change tracking can silently corrupt change history by failing to record the true previous value and providing no observability when unexpected errors occur.</violation>
</file>

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Re-trigger cubic

Comment thread redash/cli/rq.py
@manager.command()
def healthcheck():
if not SUPERVISOR_CHECKS_AVAILABLE:
print("Error: supervisor_checks not available. Cannot perform healthcheck.")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Click CLI commands do not use return values as exit codes. return 1 in a @manager.command() function will be ignored and the process will exit with code 0, so monitoring scripts relying on exit status will incorrectly treat a failed healthcheck as successful.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At redash/cli/rq.py, line 114:

<comment>Click CLI commands do not use return values as exit codes. `return 1` in a `@manager.command()` function will be ignored and the process will exit with code 0, so monitoring scripts relying on exit status will incorrectly treat a failed healthcheck as successful.</comment>

<file context>
@@ -47,47 +60,57 @@ def worker(queues):
 @manager.command()
 def healthcheck():
+    if not SUPERVISOR_CHECKS_AVAILABLE:
+        print("Error: supervisor_checks not available. Cannot perform healthcheck.")
+        return 1
     return check_runner.CheckRunner("worker_healthcheck", "worker", None, [(WorkerHealthcheck, {})]).run()
</file context>

for from_obj in query.statement.get_final_froms():
if hasattr(from_obj, "left") and hasattr(from_obj, "right"):
if isinstance(from_obj.right, sa.Table):
for registry in mapperlib._mapper_registries:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Regression: get_mapper no longer raises on ambiguous table-to-mapper mappings

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At redash/utils/query_order.py, line 104:

<comment>Regression: get_mapper no longer raises on ambiguous table-to-mapper mappings</comment>

<file context>
@@ -98,7 +95,28 @@ def get_query_entities(query):
+    for from_obj in query.statement.get_final_froms():
+        if hasattr(from_obj, "left") and hasattr(from_obj, "right"):
+            if isinstance(from_obj.right, sa.Table):
+                for registry in mapperlib._mapper_registries:
+                    for mapper in registry.mappers:
+                        if from_obj.right in mapper.tables:
</file context>

Comment thread redash/models/changes.py
try:
previous = getattr(self, attr.key, None)
self._clean_values[col.name] = previous
except Exception:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Broad except Exception: pass in audit change tracking can silently corrupt change history by failing to record the true previous value and providing no observability when unexpected errors occur.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At redash/models/changes.py, line 76:

<comment>Broad `except Exception: pass` in audit change tracking can silently corrupt change history by failing to record the true previous value and providing no observability when unexpected errors occur.</comment>

<file context>
@@ -63,10 +63,19 @@ def prep_cleanvalues(self):
+                    try:
+                        previous = getattr(self, attr.key, None)
+                        self._clean_values[col.name] = previous
+                    except Exception:
+                        # If we can't get the attribute (e.g., object deleted), skip it
+                        pass
</file context>

@wtfiwtz
Copy link
Copy Markdown
Author

wtfiwtz commented Jun 1, 2026

This PR has been split into two smaller, more reviewable PRs:

  1. feat: upgrade SQLAlchemy 1.3→1.4 and Flask-SQLAlchemy 2.5→3.0 #7723 — SQLAlchemy 1.3→1.4 + Flask-SQLAlchemy 2.5→3.0 (Flask stays at 2.3.3)
  2. feat: upgrade Flask 2.3.3 → 3.1.3 #7724 — Flask 2.3.3→3.1.3 (depends on feat: upgrade SQLAlchemy 1.3→1.4 and Flask-SQLAlchemy 2.5→3.0 #7723 merging first)

Why the split?

The key insight is that Flask-SQLAlchemy 3.0.5 requires only Flask ≥ 2.2.5 (not Flask 3). This means the ORM migration can land independently, and the Flask 3 upgrade can follow as a smaller, cleaner PR.

Benefits:

Closing this PR in favor of the split approach.

@wtfiwtz wtfiwtz closed this Jun 1, 2026
wtfiwtz added a commit to orchestrated-io/redash that referenced this pull request Jun 1, 2026
1. P1: Restore get_mapper() ambiguity check - collect all matching mappers
   and raise ValueError if multiple found (regression from original code)

2. P2: Log exceptions in ChangeTrackingMixin instead of silent pass -
   provides observability when attribute access fails during change tracking

Addresses cubic-dev-ai issues on original getredash#7722.

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant