GH-35650: [Python] Cast array fill values in fill_null instead of as_py() - #50890
Open
Nishuuzz wants to merge 1 commit into
Open
GH-35650: [Python] Cast array fill values in fill_null instead of as_py()#50890Nishuuzz wants to merge 1 commit into
Nishuuzz wants to merge 1 commit into
Conversation
…of as_py()
When the fill value's type differs from the values', `fill_null` converted
it with `pa.scalar(fill_value.as_py(), type=values.type)`. Only `Scalar`
has an `as_py()` method, so an `Array` or `ChunkedArray` fill value of a
different type failed with an AttributeError rather than being cast, which
is what the docstring says happens:
>>> s1 = pa.array(["ab", None], pa.string())
>>> s2 = pa.array([bytearray([97, 98]), None], type=pa.binary(2))
>>> pa.compute.fill_null(s1, s2)
AttributeError: 'pyarrow.lib.FixedSizeBinaryArray' object has no
attribute 'as_py'
Cast the fill value instead. `cast` is available on `Scalar`, `Array` and
`ChunkedArray` alike, so the three cases are handled the same way, and the
result keeps the type of `values` rather than promoting to a common type
the way passing both to `coalesce` directly would.
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rationale for this change
Fixes #35650, which has been open since May 2023. Passing an
ArrayorChunkedArrayas the fill value tofill_nullblows up with anAttributeErrorwhenever its type doesn't match the values:It isn't specific to fixed size binary. Any mismatch does it, including the plainest possible one:
and it comes through
Array.fill_nullandChunkedArray.fill_nulltoo, since both route here.The cause is this line:
as_py()only exists onScalar. The branch is reached by arrays and chunked arrays as well, which the parameter is documented to accept — "fill_value : Array, ChunkedArray, or Scalar-like object. If not same type as values, will attempt to cast." So the documented cast never happens for the array cases; they just raise.What changes are included in this PR?
Cast the fill value instead of routing it through Python objects.
castexists onScalar,ArrayandChunkedArrayalike, so all three go down the same path and the documented behaviour actually happens.I did consider just dropping the branch and letting
coalescereconcile the types itself, which it is perfectly capable of. I didn't, because it reconciles them to a common type rather than to the values' type, sofill_nullon a string array with a binary fill value would hand back a binary array, and filling an int64 array from a float64 one would turn the untouched int64 values into floats. Casting the fill value keeps the result the type ofvalues, which is what the docstring promises and, I think, what anyone callingfill_nullexpects.For scalar fill values this is almost entirely the same operation as before — I compared the old and new paths across int/float/null/string/timestamp combinations and they agree, including which ones raise. The one difference is that a case like an int32 scalar into a string array now casts to
'5'where it used to raiseArrowTypeError. That is a cast the docstring invites, and it only widens what's accepted, but it is a behaviour change so I'd rather flag it than leave you to find it.Are these changes tested?
Yes,
test_fill_null_array_different_typecovers a mismatchedArrayfill value, a mismatchedChunkedArrayone, the exact reproducer from #35650, and the case that must still fail — a float64 fill value into an int64 array, which raises rather than quietly promoting the result.It fails on main with the
AttributeErrorabove and passes with the change.I don't have a local C++ build, so I ran the updated
test_compute.pyagainst an installed pyarrow 25.0.0 with the same one-line change applied. Comparing the two runs, exactly one test moved and it was this one — 21 failures before, 20 after, and no test that passed before fails now. The remaining failures and errors are identical in both runs and unrelated to this: thetest_round_temporal_durationgroup and three pickle fixture errors that come from my environment.Are there any user-facing changes?
Yes.
fill_nullwith a differently-typedArrayorChunkedArrayfill value now works instead of raisingAttributeError, and the scalar case above accepts one more conversion than it used to.#35624 looks like the same root cause. I've left it alone rather than claim it here, because the case in that report already works on current main and I couldn't reproduce it as written.