This is an automated email from the ASF dual-hosted git repository.
eschutho pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 8955593b858 fix: close gaps in pkg_resources/sqlalchemy-redshift
warning suppression (#41935)
8955593b858 is described below
commit 8955593b858f40e6ebafc30bc4199edee24e9e1b
Author: Elizabeth Thompson <[email protected]>
AuthorDate: Wed Jul 29 15:05:18 2026 -0700
fix: close gaps in pkg_resources/sqlalchemy-redshift warning suppression
(#41935)
Co-authored-by: Claude Fable 5 <[email protected]>
---
superset/db_engine_specs/redshift.py | 16 +++++++++
superset/initialization/__init__.py | 15 +++++++++
superset/mcp_service/__init__.py | 12 +++++++
superset/mcp_service/server.py | 15 +++++++++
tests/unit_tests/initialization_test.py | 45 +++++++++++++++++++++++++
tests/unit_tests/mcp_service/test_mcp_server.py | 18 ++++++++++
6 files changed, 121 insertions(+)
diff --git a/superset/db_engine_specs/redshift.py
b/superset/db_engine_specs/redshift.py
index de6a2a5b514..c4c266ab6e8 100644
--- a/superset/db_engine_specs/redshift.py
+++ b/superset/db_engine_specs/redshift.py
@@ -46,9 +46,25 @@ from superset.utils import json
# Setuptools 80.x (pinned in requirements/base.txt) raises this as a plain
# UserWarning, not DeprecationWarning -- don't add category=DeprecationWarning
# here, it would silently stop matching.
+#
+# Scoped to the sqlalchemy_redshift module (via stacklevel=2 in setuptools'
+# own warn() call, the warning is attributed to whatever imports
+# pkg_resources, i.e. sqlalchemy_redshift/__init__.py) so this doesn't also
+# swallow the same deprecation warning from unrelated dependencies.
+#
+# The same filter is also registered unconditionally in
+# SupersetAppInitializer.configure_logging() (superset/initialization/
+# __init__.py), before it dispatches to the (deployment-replaceable)
+# LOGGING_CONFIGURATOR. That's now the primary suppression point for the
+# web app and celery workers; this one remains as a fallback for standalone
+# scripts that import this module without going through create_app()
+# (filterwarnings() calls are idempotent, so registering it twice is
+# harmless).
warnings.filterwarnings(
"ignore",
message=r"pkg_resources is deprecated as an API",
+ category=UserWarning,
+ module=r"sqlalchemy_redshift(?:\..*)?",
)
logger = logging.getLogger()
diff --git a/superset/initialization/__init__.py
b/superset/initialization/__init__.py
index 9bd94bd54ef..851dc04a775 100644
--- a/superset/initialization/__init__.py
+++ b/superset/initialization/__init__.py
@@ -20,6 +20,7 @@ import contextlib
import logging
import os
import sys
+import warnings
from typing import Any, Callable, TYPE_CHECKING
import wtforms_json
@@ -1392,6 +1393,20 @@ class SupersetAppInitializer: # pylint:
disable=too-many-public-methods
)
def configure_logging(self) -> None:
+ # sqlalchemy-redshift's own __init__ still imports pkg_resources (see
+ # superset/db_engine_specs/redshift.py for the full rationale). This
+ # filter used to live in
DefaultLoggingConfigurator.configure_logging(),
+ # but LOGGING_CONFIGURATOR is a deployment-replaceable hook -- any
+ # custom configurator skipped it entirely and still hit the warning.
+ # Registering it here, before LOGGING_CONFIGURATOR runs, guarantees
+ # it's installed regardless of which configurator is configured.
+ warnings.filterwarnings(
+ "ignore",
+ message=r"pkg_resources is deprecated as an API",
+ category=UserWarning,
+ module=r"sqlalchemy_redshift(?:\..*)?",
+ )
+
self.config["LOGGING_CONFIGURATOR"].configure_logging(
self.config, self.superset_app.debug
)
diff --git a/superset/mcp_service/__init__.py b/superset/mcp_service/__init__.py
index 75949faa7c9..419ed13fe49 100644
--- a/superset/mcp_service/__init__.py
+++ b/superset/mcp_service/__init__.py
@@ -47,6 +47,18 @@ warnings.filterwarnings(
message=r"authlib\.jose module is deprecated",
)
+# sqlalchemy-redshift's own __init__ still imports pkg_resources (see the
+# equivalent filter and rationale in superset/db_engine_specs/redshift.py).
+# MCP tools that touch a Redshift-backed database trigger this import via a
+# separate process from the main web/worker app, so it needs its own filter
+# here rather than relying on db_engine_specs/redshift.py having been loaded.
+warnings.filterwarnings(
+ "ignore",
+ message=r"pkg_resources is deprecated as an API",
+ category=UserWarning,
+ module=r"sqlalchemy_redshift(?:\..*)?",
+)
+
__version__ = "1.0.0"
# Tools are auto-registered when imported by the MCP service
diff --git a/superset/mcp_service/server.py b/superset/mcp_service/server.py
index 37101cc0011..630141d712a 100644
--- a/superset/mcp_service/server.py
+++ b/superset/mcp_service/server.py
@@ -63,6 +63,8 @@ def _suppress_third_party_warnings() -> None:
- marshmallow ``RemovedInMarshmallow4Warning`` (triggered during
database engine schema instantiation)
- google.api_core ``FutureWarning`` (Python version support notices)
+ - sqlalchemy-redshift ``pkg_resources`` UserWarning (see
+ superset/db_engine_specs/redshift.py for details)
"""
import warnings
@@ -83,6 +85,19 @@ def _suppress_third_party_warnings() -> None:
"ignore",
message=r"authlib\.jose module is deprecated",
)
+ # Same treatment for the pkg_resources warning suppressed at package
+ # init time. Confirmed non-redundant: warnings.filters can be reset
+ # between the package import and this call (e.g. pytest's warnings
+ # plugin resets it around every test -- test_suppress_third_party_warnings
+ # below fails without this line, proving the reset scenario is real,
+ # not hypothetical), so re-registering here is load-bearing, not
+ # belt-and-suspenders.
+ warnings.filterwarnings(
+ "ignore",
+ message=r"pkg_resources is deprecated as an API",
+ category=UserWarning,
+ module=r"sqlalchemy_redshift(?:\..*)?",
+ )
def _downgrade_to_warning(record: logging.LogRecord) -> None:
diff --git a/tests/unit_tests/initialization_test.py
b/tests/unit_tests/initialization_test.py
index 860063905a0..e68560906ce 100644
--- a/tests/unit_tests/initialization_test.py
+++ b/tests/unit_tests/initialization_test.py
@@ -226,6 +226,51 @@ class TestSupersetAppInitializer:
assert "secretpass" not in output
assert "postgresql://user:***@localhost:5432/db" in output
+ @patch("superset.initialization.logger")
+ def
test_configure_logging_installs_pkg_resources_filter_before_configurator(
+ self, mock_logger
+ ) -> None:
+ """The pkg_resources warning filter must be installed before
+ LOGGING_CONFIGURATOR.configure_logging() dispatches, so a deployment's
+ custom configurator (which may skip DefaultLoggingConfigurator's own
+ filter) still benefits from it."""
+ import re
+ import warnings
+
+ def has_pkg_resources_filter() -> bool:
+ return any(
+ f[0] == "ignore"
+ and isinstance(f[1], re.Pattern)
+ and f[1].pattern == r"pkg_resources is deprecated as an API"
+ and f[2] is UserWarning
+ and isinstance(f[3], re.Pattern)
+ and f[3].pattern == r"sqlalchemy_redshift(?:\..*)?"
+ for f in warnings.filters
+ )
+
+ seen_during_dispatch = []
+
+ class RecordingConfigurator:
+ def configure_logging(self, app_config, debug_mode):
+ seen_during_dispatch.append(has_pkg_resources_filter())
+
+ mock_app = MagicMock()
+ mock_app.config = {"LOGGING_CONFIGURATOR": RecordingConfigurator()}
+ mock_app.debug = False
+ app_initializer = SupersetAppInitializer(mock_app)
+
+ with warnings.catch_warnings():
+ # Isolate from filters registered by other tests/import side
effects.
+ warnings.resetwarnings()
+ assert not has_pkg_resources_filter()
+
+ app_initializer.configure_logging()
+
+ assert seen_during_dispatch == [True], (
+ "pkg_resources filter must already be installed by the time "
+ "LOGGING_CONFIGURATOR.configure_logging() runs"
+ )
+
def test_check_and_warn_database_connection_invalid_uri(self) -> None:
"""Test that invalid URIs are handled safely without crashing."""
mock_app = MagicMock()
diff --git a/tests/unit_tests/mcp_service/test_mcp_server.py
b/tests/unit_tests/mcp_service/test_mcp_server.py
index 9b2de2c74ca..4ab76afecaa 100644
--- a/tests/unit_tests/mcp_service/test_mcp_server.py
+++ b/tests/unit_tests/mcp_service/test_mcp_server.py
@@ -151,6 +151,24 @@ def test_suppress_third_party_warnings():
]
assert len(google_filters) >= 1, "Expected google FutureWarning filter"
+ # Verify pkg_resources UserWarning filter is installed, scoped to
+ # sqlalchemy_redshift (sqlalchemy-redshift triggers this via a late
+ # import on Redshift-backed connections; see
+ # superset/db_engine_specs/redshift.py for the full rationale). Scoping
+ # by category+module keeps this from also swallowing the same
+ # deprecation message from unrelated dependencies.
+ pkg_resources_filters = [
+ f
+ for f in warnings.filters
+ if f[0] == "ignore"
+ and f[2] is UserWarning
+ and isinstance(f[1], re.Pattern)
+ and f[1].pattern == r"pkg_resources is deprecated as an API"
+ and isinstance(f[3], re.Pattern)
+ and f[3].pattern == r"sqlalchemy_redshift(?:\..*)?"
+ ]
+ assert len(pkg_resources_filters) >= 1, "Expected pkg_resources warning
filter"
+
def test_create_event_store_returns_none_when_redis_store_fails():
"""EventStore returns None when Redis store creation fails."""