Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
dc29a19
[SPARK-60676][CONNECT] Use Arrow columns for signed int64 pandas inputs
dbtsai Oct 29, 2026
3e79f91
[SPARK-60676][CONNECT] Validate integer upload scales against mock RPC
dbtsai Oct 29, 2026
8c705c0
[SPARK-60676][CONNECT] Cover leading null inference and non-nullable …
dbtsai Oct 29, 2026
fb996d9
[SPARK-60676][CONNECT] Exercise named schemas through mock rename rel…
dbtsai Oct 29, 2026
79d3faf
[SPARK-60676][CONNECT] Avoid Arrow configuration overhead for small N…
dbtsai Oct 29, 2026
e266e8e
[SPARK-60676][CONNECT] Retain IPC row fallback for NUL field names
dbtsai Oct 29, 2026
9942f21
[SPARK-60676][CONNECT] Include pandas regressions in the existing Arr…
dbtsai Oct 29, 2026
5edd700
[SPARK-60676][CONNECT] Preserve integer pandas schema and fallback se…
dbtsai Oct 29, 2026
080aa16
[SPARK-60676][TESTS] Preserve subclass column labels on pandas 3
dbtsai Oct 30, 2026
abbc23a
[SPARK-60676][CONNECT] Preserve customized pandas columns hooks
dbtsai Oct 30, 2026
7961bde
[SPARK-60676][CONNECT] Preserve rebound pandas index storage ordering
dbtsai Oct 30, 2026
3e3a8f5
Preserve integer pandas fallback with missing Arrow aliases
dbtsai Oct 31, 2026
f058ac9
[SPARK-60676][TESTS] Retain current master coverage after rebase
dbtsai Nov 5, 2026
121a141
Preserve current pandas label iteration and fallback order after rebase
dbtsai Nov 5, 2026
aca1d02
Keep canonical string storage guard module identity
dbtsai Nov 5, 2026
ecdd67a
[CONNECT] Reconcile remaining change with current inference architecture
dbtsai Nov 6, 2026
e7ae71a
[CONNECT] Reconcile remaining change with current inference architecture
dbtsai Nov 6, 2026
40e5266
[CONNECT] Reconcile remaining change with current inference architecture
dbtsai Nov 6, 2026
7b40f32
[SPARK-60685][CONNECT] Fast-path exact builtin date inputs
dbtsai Oct 29, 2026
4a50058
[SPARK-60685][CONNECT] Include date tests in the CI offline suite
dbtsai Oct 29, 2026
fc7373c
[SPARK-60698][CONNECT] Batch Arrow input normalization config reads
dbtsai Oct 29, 2026
1fc2bab
[SPARK-60716][CONNECT] Convert explicit pandas date columns through A…
dbtsai Oct 29, 2026
5727e18
[SPARK-60716][CONNECT] Keep small date frames on the row path
dbtsai Oct 29, 2026
bd725f9
[SPARK-60716][CONNECT] Respect the optional protoc test fixture
dbtsai Oct 29, 2026
21ca8f1
[SPARK-60716][CONNECT] Preserve custom pandas date fallback
dbtsai Oct 29, 2026
bbd3c15
[SPARK-60716][CONNECT] Retain direct pandas date normalization hooks
dbtsai Oct 29, 2026
b9d1a5d
[SPARK-60716][CONNECT] Preserve date column-label conversion hooks
dbtsai Oct 30, 2026
1d9869c
[SPARK-60716][CONNECT] Preserve current Arrow alias fallback semantics
dbtsai Nov 5, 2026
5380896
Align date label regression tests with current native iteration
dbtsai Nov 5, 2026
280bdee
[SPARK-60727][CONNECT] Keep canonical double input on inline transport
dbtsai Oct 30, 2026
11ff3b7
[SPARK-60727][CONNECT] Normalize Float helper terminal newline
dbtsai Oct 30, 2026
b9498d1
Reconcile current master interfaces and formatting after rebase
dbtsai Nov 5, 2026
2bd4638
Align float label regression tests with current native iteration
dbtsai Nov 5, 2026
ccc1b48
[CONNECT] Reconcile remaining change with current inference architecture
dbtsai Nov 6, 2026
26be6ab
[SPARK-60720][CONNECT] Encode guarded pandas String input inline
dbtsai Oct 30, 2026
09d0b13
[SPARK-60720][CONNECT] Remove extra EOF blank lines
dbtsai Oct 30, 2026
4304062
Reconcile current master interfaces and formatting after rebase
dbtsai Nov 5, 2026
b2274e4
Align string label regression tests with current native iteration
dbtsai Nov 5, 2026
cfde2b6
[CONNECT] Reconcile remaining change with current inference architecture
dbtsai Nov 6, 2026
e31aad6
[CONNECT] Reconcile remaining change with current inference architecture
dbtsai Nov 6, 2026
053bbcf
[CONNECT] Reconcile remaining change with current inference architecture
dbtsai Nov 6, 2026
9135b9b
[CONNECT] Reconcile remaining change with current inference architecture
dbtsai Nov 6, 2026
b415494
Merge remote-tracking branch 'apache/master' into SPARK-60727-pandas-…
HyukjinKwon Nov 6, 2026
489a663
[SPARK-60727][CONNECT] Drop the converter guards that the float64 pat…
HyukjinKwon Nov 6, 2026
9fbcd15
[SPARK-60727][CONNECT] Do not pin the reads of master's pandas row pa…
HyukjinKwon Nov 6, 2026
121a7a8
Merge branch 'SPARK-60727-pandas-float64' into SPARK-60720-pandas-str…
HyukjinKwon Nov 6, 2026
6eca4a4
[SPARK-60720][CONNECT] Encode each pandas string once, and drop the c…
HyukjinKwon Nov 6, 2026
6104f54
[SPARK-60720][CONNECT] Do not pin the reads of master's pandas row pa…
HyukjinKwon Nov 6, 2026
a31846b
Merge remote-tracking branch 'apache/master' into SPARK-60727-pandas-…
HyukjinKwon Nov 6, 2026
74bf14b
Merge branch 'SPARK-60727-pandas-float64' into SPARK-60720-pandas-str…
HyukjinKwon Nov 6, 2026
1aa7100
Merge branch 'SPARK-60720-pandas-strings' into upd-138
HyukjinKwon Nov 6, 2026
adc009b
[SPARK-60716][CONNECT] Serve the Arrow importer's settings in the exa…
HyukjinKwon Nov 6, 2026
281188e
Merge remote-tracking branch 'apache/master' into upd-138
HyukjinKwon Nov 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions crates/pyspark-rs/src/session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1028,6 +1028,24 @@ impl PySparkSession {
frame.to_df(fields.iter().map(|field| field.name.as_str()).collect()),
));
}
let table = py
.import("pyspark.sql._pandas_input")?
.getattr("prepare_date_table")?
.call1((data, schema))?;
if !table.is_none() {
let frame =
crate::pyarrow_import::create_dataframe(py, &self.session, &table, schema)?;
return Ok(match &spec {
Spec::Struct(fields) if !fields.is_empty() && table.len()? > 0 => {
PyDataFrame::new(
frame
.dataframe
.to_df(fields.iter().map(|field| field.name.as_str()).collect()),
)
}
_ => frame,
});
}
return self.create_from_pandas(py, data, spec);
}
// Preserve schema parsing, ignored-argument warnings, and pandas/Arrow routing.
Expand Down
140 changes: 140 additions & 0 deletions python/pyspark/sql/_pandas_input.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,3 +629,143 @@ def prepare_string_input(frame, schema):
for array in arrays):
return None
return _pandas_string_ipc(arrays, schema)


def prepare_date_table(frame, schema):
"""Accept only explicit date columns whose cells are exact dates or None.

Inspect every object before Arrow conversion: date subclasses can override
conversion hooks, and pandas treats several other objects as missing values.
Those cases must keep the existing row conversion and its error ordering.
"""
from datetime import date
import sys

import numpy as np
import pandas as pd
from pandas.core import frame as frame_module, generic as generic_module
from pandas.core.frame import DataFrame
from pandas.core.series import Series
from types import FunctionType, GetSetDescriptorType, WrapperDescriptorType

from pyspark._pyspark import (
AtomicType, BooleanType, ByteType, ShortType, IntegerType, LongType, FloatType,
DoubleType, DecimalType, StringType, BinaryType, DateType, StructField,
StructType as NativeStructType,
)
from pyspark.sql import types as sql_types

# Keep smaller frames on the row path to avoid the importer's config RPC overhead.
if (pd.DataFrame is not DataFrame or type(frame) is not DataFrame
or type(schema) is not NativeStructType
or len(frame) < 10000
or any(name in vars(frame) for name in ("astype", "notna", "where"))):
return None
if not _has_canonical_columns(frame.columns):
return None
# The row path calls these normalization methods. Custom class or instance
# hooks must retain those calls and the values they produce before Arrow RPCs.
for name, module, qualname in (
("astype", generic_module, "NDFrame.astype"),
("notna", frame_module, "DataFrame.notna"),
("where", generic_module, "NDFrame.where"),
):
method = getattr(DataFrame, name)
if (type(method) is not FunctionType or method.__name__ != name
or method.__qualname__ != qualname or method.__module__ != module.__name__
or method.__globals__ is not vars(module)):
return None
if (DataFrame.astype is not generic_module.NDFrame.astype
or DataFrame.where is not generic_module.NDFrame.where):
return None
# Earlier converter branches must not recognize a date as another type.
# Check loaded converters and globals captured by older importer functions.
if sys.modules.get("pyspark.sql.types") is not sql_types:
return None
importer = sys.modules.get("pyspark.sql._arrow_input")
if vars(sql_types).get("AtomicType") is not AtomicType:
return None
if importer is not None:
if getattr(importer, "StructType", None) is not NativeStructType:
return None
if vars(importer).get("AtomicType") is not AtomicType:
return None
converter = sys.modules.get("pyspark.sql.pandas.types")
namespaces = [vars(sql_types)]
if converter is not None:
namespaces.append(vars(converter))
if importer is not None:
for name in ("from_arrow_schema", "to_arrow_schema"):
# Newer importers resolve these functions locally in prepare_table.
if name not in vars(importer):
continue
namespace = getattr(getattr(importer, name, None), "__globals__", None)
if type(namespace) is not dict:
return None
namespaces.append(namespace)
for name, cls in (
("BooleanType", BooleanType), ("ByteType", ByteType), ("ShortType", ShortType),
("IntegerType", IntegerType), ("LongType", LongType), ("FloatType", FloatType),
("DoubleType", DoubleType), ("DecimalType", DecimalType), ("StringType", StringType),
("BinaryType", BinaryType), ("DateType", DateType), ("StructField", StructField),
("StructType", NativeStructType),
):
if any(namespace.get(name) is not cls for namespace in namespaces):
return None
# The row path reads native fields directly. Class attributes are mutable,
# so customized Python schema access must not select a different Arrow path.
for name, cls in (("DateType", DateType), ("StructField", StructField),
("StructType", NativeStructType)):
if (getattr(sql_types, name, None) is not cls
or cls.__getattribute__ is not object.__getattribute__
or any("__class__" in vars(base) for base in cls.__mro__ if base is not object)):
return None
for cls, names, descriptor_type in (
(NativeStructType, ("fields", "names"), GetSetDescriptorType),
(NativeStructType, ("__iter__",), WrapperDescriptorType),
(StructField, ("name", "dataType", "nullable", "metadata"), GetSetDescriptorType),
):
for name in names:
descriptor = vars(cls).get(name)
if (type(descriptor) is not descriptor_type
or descriptor.__objclass__ is not cls or descriptor.__name__ != name):
return None
width = len(frame.columns)
if not width or len(schema.fields) != width or not frame.columns.is_unique:
return None
if not all(type(name) is str and "\x00" not in name for name in frame.columns):
return None
if not all(
type(field) is StructField and type(field.dataType) is DateType
and type(field.name) is str and "\x00" not in field.name
for field in schema.fields
) or len(set(schema.names)) != width:
return None
if not all(type(dtype) is type(np.dtype("object")) and dtype == np.dtype("object")
for dtype in frame.dtypes):
return None
# A custom ExtensionArray can report an object dtype while its Arrow hook
# emits different cells. Trust only pandas' concrete NumPy backing array.
try:
from pandas.core.arrays.numpy_ import NumpyExtensionArray
except ImportError:
try:
from pandas.core.arrays.numpy_ import PandasArray as NumpyExtensionArray
except ImportError:
return None
columns = [frame.iloc[:, i] for i in range(width)]
for column, field in zip(columns, schema.fields):
if type(column) is not Series or type(column.array) is not NumpyExtensionArray:
return None
# Arrow rejects nulls in non-nullable fields; let the row path retain
# its existing behavior for that input instead.
if not all(type(value) is date or (value is None and field.nullable)
for value in column.to_numpy(copy=False)):
return None

import pyarrow as pa

return pa.Table.from_arrays(
[pa.array(column, type=pa.date32(), from_pandas=False) for column in columns],
names=schema.names,
)
Loading
Loading