You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/07/18 22:42:08 UTC

(superset) branch master updated: chore(deps): bump pandas from 2.1.4 to 2.3.3 (#42192)

This is an automated email from the ASF dual-hosted git repository.

rusackas 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 42a2aede78d chore(deps): bump pandas from 2.1.4 to 2.3.3 (#42192)
42a2aede78d is described below

commit 42a2aede78d0f279fe3ea2ea641f612da5c1af50
Author: dependabot[bot] <49...@users.noreply.github.com>
AuthorDate: Sat Jul 18 15:42:02 2026 -0700

    chore(deps): bump pandas from 2.1.4 to 2.3.3 (#42192)
    
    Signed-off-by: dependabot[bot] <su...@github.com>
    Signed-off-by: github-actions[bot] <41...@users.noreply.github.com>
    Co-authored-by: dependabot[bot] <49...@users.noreply.github.com>
    Co-authored-by: github-actions[bot] <41...@users.noreply.github.com>
    Co-authored-by: Amin Ghadersohi <am...@gmail.com>
    Co-authored-by: Claude Opus 4.8 (1M context) <no...@anthropic.com>
---
 pyproject.toml                                     |  2 +-
 requirements/base.txt                              |  4 +-
 requirements/development.txt                       |  6 +-
 superset/__init__.py                               | 10 +++
 superset/utils/pandas_sqlalchemy_compat.py         | 80 ++++++++++++++++++++++
 .../utils/pandas_sqlalchemy_compat_test.py         | 67 ++++++++++++++++++
 6 files changed, 166 insertions(+), 3 deletions(-)

diff --git a/pyproject.toml b/pyproject.toml
index 5ec62d33de4..1ebde2cf4e9 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -84,7 +84,7 @@ dependencies = [
     "packaging",
     # --------------------------
     # pandas and related (wanting pandas[performance] without numba as it's 100+MB and not needed)
-    "pandas[excel]>=2.1.4, <2.4",
+    "pandas[excel]>=2.3.3, <2.4",
     "bottleneck", # recommended performance dependency for pandas, see https://pandas.pydata.org/docs/getting_started/install.html#performance-dependencies-recommended
     # --------------------------
     "parsedatetime",
diff --git a/requirements/base.txt b/requirements/base.txt
index 01cdee65bd2..fe594e917aa 100644
--- a/requirements/base.txt
+++ b/requirements/base.txt
@@ -275,7 +275,7 @@ packaging==25.0
     #   kombu
     #   limits
     #   shillelagh
-pandas==2.1.4
+pandas==2.3.3
     # via apache-superset (pyproject.toml)
 paramiko==3.5.1
     # via
@@ -331,6 +331,8 @@ pyparsing==3.2.3
     # via apache-superset (pyproject.toml)
 pysocks==1.7.1
     # via urllib3
+python-calamine==0.8.2
+    # via pandas
 python-dateutil==2.9.0.post0
     # via
     #   apache-superset (pyproject.toml)
diff --git a/requirements/development.txt b/requirements/development.txt
index 78c9ded0690..6092f1de445 100644
--- a/requirements/development.txt
+++ b/requirements/development.txt
@@ -638,7 +638,7 @@ packaging==25.0
     #   pytest
     #   shillelagh
     #   sqlalchemy-bigquery
-pandas==2.1.4
+pandas==2.3.3
     # via
     #   -c requirements/base-constraint.txt
     #   apache-superset
@@ -819,6 +819,10 @@ pytest-mock==3.10.0
     # via
     #   apache-superset
     #   apache-superset-extensions-cli
+python-calamine==0.8.2
+    # via
+    #   -c requirements/base-constraint.txt
+    #   pandas
 python-dateutil==2.9.0.post0
     # via
     #   -c requirements/base-constraint.txt
diff --git a/superset/__init__.py b/superset/__init__.py
index e3ab53c46b3..1cecdeed034 100644
--- a/superset/__init__.py
+++ b/superset/__init__.py
@@ -28,6 +28,16 @@ from werkzeug.local import LocalProxy
 # form.
 flask_appbuilder.Model.__allow_unmapped__ = True
 
+# pandas >= 2.2 advertises a minimum SQLAlchemy of 2.0 and silently ignores
+# older installations, breaking DataFrame.to_sql / read_sql with SQLAlchemy
+# 1.4 engines. Its SQL layer still works with 1.4, so restore support until
+# Superset itself requires SQLAlchemy >= 2. Must run before any pandas SQL IO.
+from superset.utils.pandas_sqlalchemy_compat import (  # noqa: E402
+    restore_pandas_sqlalchemy_support,
+)
+
+restore_pandas_sqlalchemy_support()
+
 from superset.app import create_app  # noqa: E402, F401
 from superset.extensions import (  # noqa: E402
     appbuilder,  # noqa: F401
diff --git a/superset/utils/pandas_sqlalchemy_compat.py b/superset/utils/pandas_sqlalchemy_compat.py
new file mode 100644
index 00000000000..3f3291418a7
--- /dev/null
+++ b/superset/utils/pandas_sqlalchemy_compat.py
@@ -0,0 +1,80 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+"""Compatibility shim letting pandas >= 2.2 use SQLAlchemy 1.4 engines.
+
+pandas 2.2 raised its advertised minimum SQLAlchemy version to 2.0 as a
+support-policy change. When an older SQLAlchemy is installed, pandas does not
+fail loudly: ``pandas.io.sql`` silently pretends SQLAlchemy is absent, treats
+Engine/Connection arguments as raw DBAPI connections, and falls back to its
+sqlite-only code path, breaking every ``DataFrame.to_sql`` / ``read_sql``
+call site (dataset uploads, example data loading, annotation queries, filter
+values).
+
+The pandas SQL layer itself still works with SQLAlchemy 1.4 because it only
+uses the API subset common to SQLAlchemy 1.4 and 2.x. Lowering the advertised
+minimum back to the pandas 2.1 value restores the working behavior.
+
+This module is obsolete once Superset requires SQLAlchemy >= 2; at that point
+the patch becomes a no-op and the module (and its call site in
+``superset/__init__.py``) can be deleted.
+"""
+
+import logging
+
+import sqlalchemy
+from packaging.version import Version
+
+logger = logging.getLogger(__name__)
+
+# The last pandas release line to support SQLAlchemy 1.4 (pandas 2.1)
+# required at least this version.
+_SQLALCHEMY_MINIMUM = "1.4.16"
+
+
+def restore_pandas_sqlalchemy_support() -> None:
+    """Lower pandas' advertised SQLAlchemy minimum so 1.4 engines work.
+
+    Only applies when the installed SQLAlchemy predates 2.0 and pandas
+    advertises a 2.x minimum; in every other combination this is a no-op.
+    Safe to call multiple times.
+    """
+    if Version(sqlalchemy.__version__) >= Version("2.0.0"):
+        # pandas supports SQLAlchemy 2.x natively; nothing to patch.
+        return
+
+    try:
+        from pandas.compat import _optional
+    except ImportError:
+        # The private module moved in a newer pandas; SQL IO with a pre-2.0
+        # SQLAlchemy will misbehave, so make the situation diagnosable.
+        logger.warning(
+            "Could not adjust pandas' minimum SQLAlchemy version; "
+            "DataFrame.to_sql/read_sql may not accept SQLAlchemy %s engines",
+            sqlalchemy.__version__,
+        )
+        return
+
+    advertised = _optional.VERSIONS.get("sqlalchemy")
+    if advertised and Version(advertised) > Version(_SQLALCHEMY_MINIMUM):
+        _optional.VERSIONS["sqlalchemy"] = _SQLALCHEMY_MINIMUM
+        logger.debug(
+            "Lowered pandas' minimum SQLAlchemy version from %s to %s so "
+            "pandas SQL IO keeps working with the installed SQLAlchemy %s",
+            advertised,
+            _SQLALCHEMY_MINIMUM,
+            sqlalchemy.__version__,
+        )
diff --git a/tests/unit_tests/utils/pandas_sqlalchemy_compat_test.py b/tests/unit_tests/utils/pandas_sqlalchemy_compat_test.py
new file mode 100644
index 00000000000..980f3123546
--- /dev/null
+++ b/tests/unit_tests/utils/pandas_sqlalchemy_compat_test.py
@@ -0,0 +1,67 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+import pandas as pd
+from sqlalchemy import create_engine, types
+
+from superset.utils.pandas_sqlalchemy_compat import (
+    restore_pandas_sqlalchemy_support,
+)
+
+
+def test_to_sql_accepts_sqlalchemy_engine_and_dtypes() -> None:
+    """
+    ``DataFrame.to_sql`` must accept a SQLAlchemy engine plus SQLAlchemy
+    ``dtype`` objects regardless of the installed pandas/SQLAlchemy combo.
+
+    This is the exact call shape used by dataset uploads
+    (``BaseEngineSpec.df_to_sql``), example data loading, and the test data
+    loaders; it breaks when pandas silently rejects the installed SQLAlchemy
+    as too old (pandas >= 2.2 with SQLAlchemy 1.x) and no compat shim is
+    applied.
+    """
+    restore_pandas_sqlalchemy_support()
+
+    engine = create_engine("sqlite://")
+    df = pd.DataFrame(
+        {
+            "name": ["a", "b"],
+            "num": [1, 2],
+            "ds": pd.to_datetime(["2021-01-01", "2021-01-02"]),
+        }
+    )
+    df.to_sql(
+        "birth_names",
+        engine,
+        index=False,
+        dtype={"ds": types.DateTime(), "name": types.String(255)},
+        method="multi",
+        chunksize=100,
+    )
+    df.to_sql("birth_names", engine, index=False, if_exists="replace")
+
+    result = pd.read_sql_query("SELECT name, num FROM birth_names", engine)
+    assert result["name"].tolist() == ["a", "b"]
+    assert result["num"].tolist() == [1, 2]
+
+
+def test_restore_pandas_sqlalchemy_support_is_idempotent() -> None:
+    from pandas.compat import _optional
+
+    restore_pandas_sqlalchemy_support()
+    first = _optional.VERSIONS.get("sqlalchemy")
+    restore_pandas_sqlalchemy_support()
+    assert _optional.VERSIONS.get("sqlalchemy") == first