viirya opened a new pull request, #89:
URL: https://github.com/apache/spark-connect-rust/pull/89

   ### What changes were proposed in this pull request?
   
   Add an explicit `module = "..."` to every `#[pyclass]` in 
`crates/pyspark-rs/src` (99
   of them, including the `abstract_type!` macro that generates the 8 
intermediate
   DataType bases), and set the module on `create_exception!` for
   `SkipRestOfInputTableException`.
   
   PyO3 defaults a `#[pyclass]`'s `__module__` to `builtins` when no `module` 
argument is
   given, and none of ours set it.
   
   The class-to-module mapping is derived from where our own drop-in skin
   (`python/pyspark/**`) already re-exports each class, cross-checked against 
Apache Spark
   v4.2.0 (commit `32f72996011` — the same commit pinned in
   `crates/spark-connect-proto/proto/PROTO_SHA.txt`).
   
   Two mapping notes:
   
   - `DataFrame` and `Column` map to `pyspark.sql.connect.*`, **not** 
`pyspark.sql.*`.
     Upstream's `pyspark/sql/dataframe.py:78` and `pyspark/sql/column.py:41` 
hold the
     *abstract* parent; the concrete class a Connect session hands the user is
     `pyspark/sql/connect/dataframe.py:112` / `.../connect/column.py:109`, 
which is what
     `type(df).__module__` reports on a real Connect session.
   - `RustRpcError` deliberately keeps `_pyspark`: it is an internal transport 
error with
     no upstream counterpart, so asserting a pyspark path for it would be false.
   
   The hand-written `__reduce__` impls in `types.rs` are **kept**. They carry 
the type's
   parameters (`DecimalType(12,3)`, `ArrayType(...)`), which a by-reference 
pickle of the
   bare class would not restore.
   
   ### Why are the changes needed?
   
   `__module__ == "builtins"` is a behavioral divergence from the reference 
client, not
   just a cosmetic one:
   
   1. **The class itself was unpicklable.** pickle serializes a class *by 
reference* —
      writing `__module__` + `__qualname__` and re-importing on load — so the 
lookup
      landed in `builtins` and failed:
   
      ```
      >>> import pickle
      >>> from pyspark.sql.types import IntegerType
      >>> pickle.dumps(IntegerType)
      PicklingError: Can't pickle <class 'builtins.IntegerType'>:
                     attribute lookup IntegerType on builtins failed
      ```
   
      Real pyspark pickles these fine. Reproduced for `DataFrame`, `Column`, 
and every
      `DataType` class.
   
   2. **Error messages, reprs, and `__module__`-based dispatch showed 
`builtins.DataFrame`**
      instead of the pyspark path — user-visible, and it undercuts the drop-in 
premise.
   
   Instance pickling already worked, but only because each DataType hand-writes 
a
   `__reduce__` routing through 
`pyspark.sql.types._parse_datatype_json_string`. The cost
   of the wrong `__module__` was therefore already being paid, worked around 
per class
   rather than fixed at the root.
   
   This is the same "declared but never applied" gap as SPARK-59032 (parsed 
gRPC keepalive
   never applied to the channel) and SPARK-59037 (128 MiB max message size 
declared but
   unused): the intent existed, but the wiring that makes it take effect was 
missing.
   
   ### Does this PR introduce _any_ user-facing change?
   
   Yes, and it is the point of the change: the drop-in's classes now report the 
same
   `__module__` as the reference client (`pyspark.sql.types.IntegerType` rather 
than
   `builtins.IntegerType`), so pickling a class works and error messages/reprs 
show the
   pyspark path. No API surface is added or removed.
   
   ### How was this patch tested?
   
   Four new offline tests in `python/tests/test_dropin_offline.py`:
   
   - the module paths for `DataFrame` / `Column` / `SparkSession` / `DataType` /
     `IntegerType` / `StructType` / `NumericType` (macro-generated base) / 
`Row`,
   - that the DataType classes pickle by reference and round-trip to the 
*identical*
     class object,
   - and, as a guard against a future `#[pyclass]` landing without `module`, 
that no class
     exported by `pyspark._pyspark` reports `builtins`.
   
   Verified manually against a locally built extension: all **108** exported 
classes are
   covered, none left on `builtins`. Regression-checked that the existing 
`__reduce__`
   instance path is unaffected — `DecimalType(12,3)` → `decimal(12,3)`, nested
   `StructType`, the UDF cloudpickle payload, `isinstance`, and the MRO all 
behave as
   before.
   
   `cargo test` passes (93 core tests + all suites, 0 failures). `cargo clippy 
-p
   pyspark-rs` reports **389 warnings both before and after** this change — i.e.
   unchanged from master, nothing introduced (all pre-existing 
`Default::default()` /
   large-`Err` lints). `cargo fmt --check` is clean.
   
   Not run in my local environment (no `pytest`/`pandas` on the system Python): 
the full
   `test_dropin_offline.py` suite and the parity gates — those need CI. I 
verified the new
   tests' assertions by executing their logic directly against the built 
extension.
   


-- 
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