sadpandajoe commented on code in PR #45823:
URL: https://github.com/apache/superset/pull/45823#discussion_r4422909325


##########
superset/utils/core.py:
##########
@@ -2131,13 +2131,24 @@ def _process_datetime_column(
 
         # Parse with or without format (suppress warning if no format)
         if format_to_use:
-            converted = pd.to_datetime(
-                df[col.col_label],
-                utc=False,
-                format=format_to_use,
-                errors="coerce",
-                exact=False,
-            )
+            try:
+                converted = pd.to_datetime(
+                    df[col.col_label],
+                    utc=False,
+                    format=format_to_use,
+                    errors="coerce",
+                    exact=False,
+                )
+            except ValueError as ex:
+                if "Mixed timezones" not in str(ex):
+                    raise
+                converted = pd.to_datetime(
+                    df[col.col_label],
+                    utc=True,

Review Comment:
   On the `Mixed timezones` fallback the column is converted to UTC, and 
`_coerce_temporal_join_series` later strips the tz, so the join compares UTC 
wall clocks. A comparison series with a single offset keeps its local wall 
clock through the same path. For a `%z` column spanning a DST change (noon at 
`-05:00` and noon at `-04:00`), the source becomes 17:00/16:00 while the 
single-offset side stays 12:00, so rows that matched by local time no longer 
join. Is UTC the intended basis for these joins, or should each value keep its 
own local wall clock?



##########
tests/unit_tests/commands/databases/columnar_reader_test.py:
##########
@@ -141,7 +141,7 @@ def test_excel_reader_wrong_columns_to_read():
     with pytest.raises(DatabaseUploadFailed) as ex:
         reader.file_to_dataframe(create_columnar_file(COLUMNAR_DATA))
     assert (
-        str(ex.value)
+        str(ex.value).replace(": large_string", ": string")

Review Comment:
   The actual message is normalised with `.replace(": large_string", ": 
string")`, but the expected literal below still contains `: large_string` for 
`Name`, `City` and `Birth`. After the replace the left side can never contain 
`large_string`, so this equality fails whichever Arrow type name the reader 
reports, including when it correctly raises `DatabaseUploadFailed` for `xpto`. 
Should the same normalisation be applied to the expected string (or the 
assertion pinned to the stable `No match for FieldRef.Name(xpto)` prefix)?



##########
superset/commands/dataset/importers/v1/utils.py:
##########
@@ -986,32 +986,31 @@ def _convert_temporal_columns(df: pd.DataFrame, dtype: 
dict[str, Any]) -> None:
     for column_name, sqla_type in dtype.items():
         if isinstance(sqla_type, (Date, DateTime)):
             try:
-                df[column_name] = pd.to_datetime(df[column_name])
-            except OutOfBoundsDatetime:
-                # Row-level fallback: coerce only OOB values; re-raise for 
malformed
-                # strings. Whole-column errors="coerce" would silently swallow
-                # malformed values that happen to share a column with an OOB 
date.
-                original = df[column_name].copy()
-                result = []
-                for val in original:
-                    if pd.isna(val):
-                        result.append(pd.NaT)
-                        continue
-                    try:
-                        result.append(pd.to_datetime(val))
-                    except OutOfBoundsDatetime:
-                        result.append(pd.NaT)
-                    # Other exceptions (e.g. malformed strings) propagate
-                converted = pd.Series(result, index=original.index)
-                n_coerced = int(converted.isna().sum() - original.isna().sum())
-                if n_coerced > 0:
-                    logger.warning(
-                        "Coerced %d out-of-bounds datetime value(s) "
-                        "in column '%s' to NaT",
-                        n_coerced,
-                        column_name,
-                    )
-                df[column_name] = converted
+                converted = pd.to_datetime(df[column_name])
+                if pd.api.types.is_datetime64_any_dtype(converted):
+                    df[column_name] = converted.dt.as_unit("ns")

Review Comment:
   Forcing `as_unit("ns")` here (and in the per-value path below) turns a valid 
far-future date such as `3118-01-01` into `NaT`: the nanosecond cast overflows, 
the `ValueError` is swallowed, the fallback overflows again, and `load_data()` 
then writes NULL for that cell. The neighbouring 
`test_far_future_date_strings_convert_successfully` asserts the timestamp 
survives with no warning, so that test should fail against this code. Could the 
conversion keep the coarser resolution pandas already returned instead of 
casting to ns?



##########
.github/workflows/python-metadata-compatibility.yml:
##########
@@ -0,0 +1,124 @@
+name: Python-Metadata-Compatibility
+
+permissions:
+  contents: read
+
+on:
+  pull_request:
+    paths:
+    - .github/workflows/python-metadata-compatibility.yml
+    - superset/db_engine_specs/**
+    - tests/unit_tests/db_engine_specs/test_lint_metadata.py
+    - pyproject.toml
+    - superset/result_set.py
+    - superset/common/utils/dataframe_utils.py
+    - tests/unit_tests/common/test_dataframe_utils.py
+    - superset/utils/pandas_postprocessing/**
+    - tests/unit_tests/pandas_postprocessing/**
+    - tests/unit_tests/result_set_test.py
+    - superset/commands/importers/v1/utils.py
+    - superset/commands/dataset/importers/v1/utils.py
+    - tests/unit_tests/commands/importers/v1/utils_test.py
+    - superset/models/helpers.py
+    - tests/unit_tests/models/helpers_test.py
+    - superset/superset_typing.py
+    - tests/unit_tests/db_engine_specs/test_base.py
+    - superset/extensions/metadb.py
+    - tests/unit_tests/extensions/test_sqlalchemy.py
+    - tests/unit_tests/conftest.py
+    - Dockerfile
+    - docker/**
+    - docker-compose-image-tag.yml
+    - scripts/docker_release_target.py
+    - tests/unit_tests/docker_app_dependencies_test.py
+    - tests/unit_tests/docker_compose_image_tag_test.py
+    - tests/unit_tests/scripts/docker_release_target_test.py
+    - superset-core/**
+  workflow_dispatch: {}
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.event.pull_request.number || 
github.run_id }}
+  cancel-in-progress: true
+
+jobs:
+  metadata-parser:
+    runs-on: ubuntu-26.04
+    timeout-minutes: 10
+    strategy:
+      fail-fast: false
+      matrix:
+        python-version: [ "3.11", "3.12", "3.13", "3.14", "3.15" ]
+    env:
+      PYTEST_DISABLE_PLUGIN_AUTOLOAD: "1"
+    steps:
+    - name: Checkout
+      uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+      with:
+        persist-credentials: false
+    - name: Setup Python
+      uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # 
v7.0.0
+      with:
+        python-version: ${{ matrix.python-version }}
+        allow-prereleases: true
+    - name: Install standalone test dependencies
+      run: python -m pip install pytest==7.4.4
+    - name: Test metadata parser without application dependencies
+      run: python -m pytest --noconftest -o addopts= 
tests/unit_tests/db_engine_specs/test_lint_metadata.py -q -W 
default::DeprecationWarning
+
+  runtime-compatibility:
+    runs-on: ubuntu-26.04
+    timeout-minutes: 25
+    strategy:
+      fail-fast: false
+      matrix:
+        python-version: [ "3.11", "3.12", "3.13", "3.14", "3.15" ]
+    env:
+      SUPERSET_SECRET_KEY: python-compatibility-ci-only-not-for-production
+      FLASK_APP: superset
+    steps:
+    - name: Checkout
+      uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+      with:
+        persist-credentials: false
+    - name: Setup Python
+      uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # 
v7.0.0
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install application and focused test dependencies
+      run: >-
+        python -m pip install -e ./superset-core -e . pytest==7.4.4 
pytest-mock freezegun parameterized responses rich prophet
+    - name: Check dependency consistency
+      run: python -m pip check
+    - name: Configure metadata directory
+      run: echo "SUPERSET_HOME=$RUNNER_TEMP/superset-python-compatibility" >> 
"$GITHUB_ENV"
+    - name: Migrate a fresh metadata database
+      run: python -m flask db upgrade
+    - name: Test application health and runtime compatibility
+      run: |
+        python - <<'PY'
+        import pytest
+        from superset.app import create_app
+
+        app = create_app()
+        assert app.test_client().get('/health').status_code == 200
+        with app.app_context():
+            raise SystemExit(pytest.main([
+                '--confcutdir=tests/unit_tests', '-p', 'pytest_mock',
+                '-o', 'addopts=',
+                'tests/unit_tests/db_engine_specs/test_lint_metadata.py',
+                'tests/unit_tests/pandas_postprocessing',
+                'tests/unit_tests/result_set_test.py',
+                'tests/unit_tests/common/test_dataframe_utils.py',
+                'tests/unit_tests/commands/importers/v1/utils_test.py',
+                'tests/unit_tests/models/helpers_test.py',

Review Comment:
   `tests/unit_tests/utils/test_date_parsing.py` is not in this pytest target 
list, so the new mixed-offset regression for `normalize_dttm_col` (the Berlin 
repeated-hour case, where both timestamps must normalise to local 02:30 without 
raising) never runs on the Python 3.15 / pandas 3.1 combination this job exists 
to cover. Regular unit CI runs on 3.11 and cannot exercise it. Could that file 
be added here and to the `paths:` triggers?



##########
docs/admin_docs/installation/pypi.mdx:
##########
@@ -15,6 +15,75 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
 
 This page describes how to install Superset using the `apache_superset` 
package [published on PyPI](https://pypi.org/project/apache_superset/).
 
+## Python Compatibility
+
+The minimum Python version is 3.11. The Python version classifiers in
+[pyproject.toml](https://github.com/apache/superset/blob/master/pyproject.toml)
+list the officially supported versions; the open-ended `requires-python` lower
+bound does not certify every newer interpreter.
+
+The source checkout includes experimental Python 3.15 compatibility. Its 
package
+requirements select NumPy `>=2.5.4,<2.6` and pandas `>=3.1.0,<3.2` on Python 
3.15
+and newer. Python 3.11 through 3.14 retain NumPy `>=1.23.5,<2.5` and pandas
+`>=2.3.3,<2.4`; the minimum Python version has not changed. These source 
changes

Review Comment:
   This says Python 3.11-3.14 keep pandas `>=2.3.3,<2.4`, but `pyproject.toml` 
now requires `pandas[excel]>=3.0.6,<3.1` for `python_version < '3.15'`, and the 
generated requirements pin 3.0.6. Someone constraining pandas to the documented 
range gets an unsatisfiable install. Can this read `>=3.0.6,<3.1`?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to