Skip to content

Commit 407f7f4

Browse files
committed
separate out invalid.py
1 parent 66ada8c commit 407f7f4

File tree

6 files changed

+87
-77
lines changed

6 files changed

+87
-77
lines changed

pandas/core/arrays/datetimelike.py

+14-13
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,10 @@
4444
from pandas.core.dtypes.missing import is_valid_nat_for_dtype, isna
4545

4646
from pandas._typing import DatetimeLikeScalar
47-
from pandas.core import missing, nanops, ops
47+
from pandas.core import missing, nanops
4848
from pandas.core.algorithms import checked_add_with_arr, take, unique1d, value_counts
4949
import pandas.core.common as com
50+
from pandas.core.ops.invalid import make_invalid_op
5051

5152
from pandas.tseries import frequencies
5253
from pandas.tseries.offsets import DateOffset, Tick
@@ -930,18 +931,18 @@ def _is_unique(self):
930931

931932
# pow is invalid for all three subclasses; TimedeltaArray will override
932933
# the multiplication and division ops
933-
__pow__ = ops.make_invalid_op("__pow__")
934-
__rpow__ = ops.make_invalid_op("__rpow__")
935-
__mul__ = ops.make_invalid_op("__mul__")
936-
__rmul__ = ops.make_invalid_op("__rmul__")
937-
__truediv__ = ops.make_invalid_op("__truediv__")
938-
__rtruediv__ = ops.make_invalid_op("__rtruediv__")
939-
__floordiv__ = ops.make_invalid_op("__floordiv__")
940-
__rfloordiv__ = ops.make_invalid_op("__rfloordiv__")
941-
__mod__ = ops.make_invalid_op("__mod__")
942-
__rmod__ = ops.make_invalid_op("__rmod__")
943-
__divmod__ = ops.make_invalid_op("__divmod__")
944-
__rdivmod__ = ops.make_invalid_op("__rdivmod__")
934+
__pow__ = make_invalid_op("__pow__")
935+
__rpow__ = make_invalid_op("__rpow__")
936+
__mul__ = make_invalid_op("__mul__")
937+
__rmul__ = make_invalid_op("__rmul__")
938+
__truediv__ = make_invalid_op("__truediv__")
939+
__rtruediv__ = make_invalid_op("__rtruediv__")
940+
__floordiv__ = make_invalid_op("__floordiv__")
941+
__rfloordiv__ = make_invalid_op("__rfloordiv__")
942+
__mod__ = make_invalid_op("__mod__")
943+
__rmod__ = make_invalid_op("__rmod__")
944+
__divmod__ = make_invalid_op("__divmod__")
945+
__rdivmod__ = make_invalid_op("__rdivmod__")
945946

946947
def _add_datetimelike_scalar(self, other):
947948
# Overriden by TimedeltaArray

pandas/core/arrays/datetimes.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
from pandas.core.arrays import datetimelike as dtl
5454
from pandas.core.arrays._ranges import generate_regular_range
5555
import pandas.core.common as com
56+
from pandas.core.ops.invalid import invalid_comparison
5657

5758
from pandas.tseries.frequencies import get_period_alias, to_offset
5859
from pandas.tseries.offsets import Day, Tick
@@ -171,13 +172,13 @@ def wrapper(self, other):
171172
other = _to_M8(other, tz=self.tz)
172173
except ValueError:
173174
# string that cannot be parsed to Timestamp
174-
return ops.invalid_comparison(self, other, op)
175+
return invalid_comparison(self, other, op)
175176

176177
result = op(self.asi8, other.view("i8"))
177178
if isna(other):
178179
result.fill(nat_result)
179180
elif lib.is_scalar(other) or np.ndim(other) == 0:
180-
return ops.invalid_comparison(self, other, op)
181+
return invalid_comparison(self, other, op)
181182
elif len(other) != len(self):
182183
raise ValueError("Lengths must match")
183184
else:
@@ -191,7 +192,7 @@ def wrapper(self, other):
191192
):
192193
# Following Timestamp convention, __eq__ is all-False
193194
# and __ne__ is all True, others raise TypeError.
194-
return ops.invalid_comparison(self, other, op)
195+
return invalid_comparison(self, other, op)
195196

196197
if is_object_dtype(other):
197198
# We have to use _comp_method_OBJECT_ARRAY instead of numpy
@@ -204,7 +205,7 @@ def wrapper(self, other):
204205
o_mask = isna(other)
205206
elif not (is_datetime64_dtype(other) or is_datetime64tz_dtype(other)):
206207
# e.g. is_timedelta64_dtype(other)
207-
return ops.invalid_comparison(self, other, op)
208+
return invalid_comparison(self, other, op)
208209
else:
209210
self._assert_tzawareness_compat(other)
210211
if isinstance(other, (ABCIndexClass, ABCSeries)):

pandas/core/arrays/timedeltas.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@
4141
)
4242
from pandas.core.dtypes.missing import isna
4343

44-
from pandas.core import ops
4544
from pandas.core.algorithms import checked_add_with_arr
4645
import pandas.core.common as com
46+
from pandas.core.ops.invalid import invalid_comparison
4747

4848
from pandas.tseries.frequencies import to_offset
4949
from pandas.tseries.offsets import Tick
@@ -90,14 +90,14 @@ def wrapper(self, other):
9090
other = Timedelta(other)
9191
except ValueError:
9292
# failed to parse as timedelta
93-
return ops.invalid_comparison(self, other, op)
93+
return invalid_comparison(self, other, op)
9494

9595
result = op(self.view("i8"), other.value)
9696
if isna(other):
9797
result.fill(nat_result)
9898

9999
elif not is_list_like(other):
100-
return ops.invalid_comparison(self, other, op)
100+
return invalid_comparison(self, other, op)
101101

102102
elif len(other) != len(self):
103103
raise ValueError("Lengths must match")
@@ -106,7 +106,7 @@ def wrapper(self, other):
106106
try:
107107
other = type(self)._from_sequence(other)._data
108108
except (ValueError, TypeError):
109-
return ops.invalid_comparison(self, other, op)
109+
return invalid_comparison(self, other, op)
110110

111111
result = op(self.view("i8"), other.view("i8"))
112112
result = com.values_from_object(result)

pandas/core/indexes/base.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,8 @@
6969
from pandas.core.indexers import maybe_convert_indices
7070
from pandas.core.indexes.frozen import FrozenList
7171
import pandas.core.missing as missing
72-
from pandas.core.ops import get_op_result_name, make_invalid_op
72+
from pandas.core.ops import get_op_result_name
73+
from pandas.core.ops.invalid import make_invalid_op
7374
import pandas.core.sorting as sorting
7475
from pandas.core.strings import StringMethods
7576

pandas/core/ops/__init__.py

+1-55
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
_make_flex_doc,
5858
_op_descriptions,
5959
)
60+
from .invalid import invalid_comparison
6061
from .roperator import ( # noqa:F401
6162
radd,
6263
rand_,
@@ -175,29 +176,6 @@ def maybe_upcast_for_op(obj):
175176
# -----------------------------------------------------------------------------
176177

177178

178-
def make_invalid_op(name):
179-
"""
180-
Return a binary method that always raises a TypeError.
181-
182-
Parameters
183-
----------
184-
name : str
185-
186-
Returns
187-
-------
188-
invalid_op : function
189-
"""
190-
191-
def invalid_op(self, other=None):
192-
raise TypeError(
193-
"cannot perform {name} with this index type: "
194-
"{typ}".format(name=name, typ=type(self).__name__)
195-
)
196-
197-
invalid_op.__name__ = name
198-
return invalid_op
199-
200-
201179
def _gen_eval_kwargs(name):
202180
"""
203181
Find the keyword arguments to pass to numexpr for the given operation.
@@ -466,38 +444,6 @@ def masked_arith_op(x, y, op):
466444
return result
467445

468446

469-
def invalid_comparison(left, right, op):
470-
"""
471-
If a comparison has mismatched types and is not necessarily meaningful,
472-
follow python3 conventions by:
473-
474-
- returning all-False for equality
475-
- returning all-True for inequality
476-
- raising TypeError otherwise
477-
478-
Parameters
479-
----------
480-
left : array-like
481-
right : scalar, array-like
482-
op : operator.{eq, ne, lt, le, gt}
483-
484-
Raises
485-
------
486-
TypeError : on inequality comparisons
487-
"""
488-
if op is operator.eq:
489-
res_values = np.zeros(left.shape, dtype=bool)
490-
elif op is operator.ne:
491-
res_values = np.ones(left.shape, dtype=bool)
492-
else:
493-
raise TypeError(
494-
"Invalid comparison between dtype={dtype} and {typ}".format(
495-
dtype=left.dtype, typ=type(right).__name__
496-
)
497-
)
498-
return res_values
499-
500-
501447
# -----------------------------------------------------------------------------
502448
# Dispatch logic
503449

pandas/core/ops/invalid.py

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""
2+
Templates for invalid operations.
3+
"""
4+
import operator
5+
6+
import numpy as np
7+
8+
9+
def invalid_comparison(left, right, op):
10+
"""
11+
If a comparison has mismatched types and is not necessarily meaningful,
12+
follow python3 conventions by:
13+
14+
- returning all-False for equality
15+
- returning all-True for inequality
16+
- raising TypeError otherwise
17+
18+
Parameters
19+
----------
20+
left : array-like
21+
right : scalar, array-like
22+
op : operator.{eq, ne, lt, le, gt}
23+
24+
Raises
25+
------
26+
TypeError : on inequality comparisons
27+
"""
28+
if op is operator.eq:
29+
res_values = np.zeros(left.shape, dtype=bool)
30+
elif op is operator.ne:
31+
res_values = np.ones(left.shape, dtype=bool)
32+
else:
33+
raise TypeError(
34+
"Invalid comparison between dtype={dtype} and {typ}".format(
35+
dtype=left.dtype, typ=type(right).__name__
36+
)
37+
)
38+
return res_values
39+
40+
41+
def make_invalid_op(name: str):
42+
"""
43+
Return a binary method that always raises a TypeError.
44+
45+
Parameters
46+
----------
47+
name : str
48+
49+
Returns
50+
-------
51+
invalid_op : function
52+
"""
53+
54+
def invalid_op(self, other=None):
55+
raise TypeError(
56+
"cannot perform {name} with this index type: "
57+
"{typ}".format(name=name, typ=type(self).__name__)
58+
)
59+
60+
invalid_op.__name__ = name
61+
return invalid_op

0 commit comments

Comments
 (0)