Skip to content

Commit 7714e79

Browse files
committed
Always return ndarray
1 parent 1921c6f commit 7714e79

File tree

3 files changed

+38
-11
lines changed

3 files changed

+38
-11
lines changed

pandas/core/arrays/base.py

+18-6
Original file line numberDiff line numberDiff line change
@@ -739,14 +739,22 @@ def _create_method(cls, op, coerce_to_dtype=True):
739739
----------
740740
op : function
741741
An operator that takes arguments op(a, b)
742-
coerce_to_dtype : bool
742+
coerce_to_dtype : bool, default True
743743
boolean indicating whether to attempt to convert
744-
the result to the underlying ExtensionArray dtype
745-
(default True)
744+
the result to the underlying ExtensionArray dtype.
745+
If it's not possible to create a new ExtensionArray with the
746+
values, an ndarray is returned instead.
746747
747748
Returns
748749
-------
749-
A method that can be bound to a method of a class
750+
Callable[[Any, Any], Union[ndarray, ExtensionArray]]
751+
A method that can be bound to a class. When used, the method
752+
receives the two arguments, one of which is the instance of
753+
this class, and should return an ExtensionArray or an ndarray.
754+
755+
Returning an ndarray may be necessary when the result of the
756+
`op` cannot be stored in the ExtensionArray. The dtype of the
757+
ndarray uses NumPy's normal inference rules.
750758
751759
Example
752760
-------
@@ -757,7 +765,6 @@ def _create_method(cls, op, coerce_to_dtype=True):
757765
in the class definition of MyExtensionArray to create the operator
758766
for addition, that will be based on the operator implementation
759767
of the underlying elements of the ExtensionArray
760-
761768
"""
762769

763770
def _binop(self, other):
@@ -778,7 +785,12 @@ def convert_values(param):
778785
try:
779786
res = self._from_sequence(res)
780787
except Exception:
781-
res = np.asarray(res, dtype=object)
788+
# https://github.com/pandas-dev/pandas/issues/22850
789+
# We catch all regular exceptions here, and fall back
790+
# to an ndarray.
791+
res = np.asarray(res)
792+
else:
793+
res = np.asarray(res)
782794

783795
return res
784796

pandas/core/series.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -2323,12 +2323,14 @@ def combine(self, other, func, fill_value=None):
23232323
pass
23242324
elif is_extension_array_dtype(self.values):
23252325
# The function can return something of any type, so check
2326-
# if the type is compatible with the calling EA
2327-
# ExtensionArray._from_sequence can raise anything, so we
2328-
# have to catch everything.
2326+
# if the type is compatible with the calling EA.
23292327
try:
23302328
new_values = self._values._from_sequence(new_values)
23312329
except Exception:
2330+
# https://github.com/pandas-dev/pandas/issues/22850
2331+
# pandas has no control over what 3rd-party ExtensionArrays
2332+
# do in _values_from_sequence. We still want ops to work
2333+
# though, so we catch any regular Exception.
23322334
pass
23332335

23342336
return self._constructor(new_values, index=new_index, name=new_name)

pandas/tests/extension/decimal/test_decimal.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,15 @@ def _from_sequence(cls, scalars, dtype=None, copy=False):
279279
raise KeyError("For the test")
280280

281281

282+
class DecimalArrayWithoutCoercion(DecimalArrayWithoutFromSequence):
283+
@classmethod
284+
def _create_arithmetic_method(cls, op):
285+
return cls._create_method(op, coerce_to_dtype=False)
286+
287+
288+
DecimalArrayWithoutCoercion._add_arithmetic_ops()
289+
290+
282291
def test_combine_from_sequence_raises():
283292
# https://github.com/pandas-dev/pandas/issues/22850
284293
ser = pd.Series(DecimalArrayWithoutFromSequence([
@@ -293,8 +302,12 @@ def test_combine_from_sequence_raises():
293302
tm.assert_series_equal(result, expected)
294303

295304

296-
def test_scalar_ops_from_sequence_raises():
297-
arr = DecimalArrayWithoutFromSequence([
305+
@pytest.mark.parametrize("class_", [DecimalArrayWithoutFromSequence,
306+
DecimalArrayWithoutCoercion])
307+
def test_scalar_ops_from_sequence_raises(class_):
308+
# op(EA, EA) should return an EA, or an ndarray if it's not possible
309+
# to return an EA with the return values.
310+
arr = class_([
298311
decimal.Decimal("1.0"),
299312
decimal.Decimal("2.0")
300313
])

0 commit comments

Comments
 (0)