1"""
2Templates for invalid operations.
3"""
4
5from __future__ import annotations
6
7import operator
8from typing import (
9 TYPE_CHECKING,
10 Any,
11 NoReturn,
12)
13
14import numpy as np
15
16if TYPE_CHECKING:
17 from collections.abc import Callable
18
19 from pandas._typing import (
20 ArrayLike,
21 Scalar,
22 npt,
23 )
24
25
26def invalid_comparison(
27 left: ArrayLike,
28 right: ArrayLike | list | range | Scalar,
29 op: Callable[[Any, Any], bool],
30) -> npt.NDArray[np.bool_]:
31 """
32 If a comparison has mismatched types and is not necessarily meaningful,
33 follow python3 conventions by:
34
35 - returning all-False for equality
36 - returning all-True for inequality
37 - raising TypeError otherwise
38
39 Parameters
40 ----------
41 left : array-like
42 right : scalar, array-like
43 op : operator.{eq, ne, lt, le, gt}
44
45 Raises
46 ------
47 TypeError : on inequality comparisons
48 """
49 if op is operator.eq:
50 res_values = np.zeros(left.shape, dtype=bool)
51 elif op is operator.ne:
52 res_values = np.ones(left.shape, dtype=bool)
53 else:
54 typ = type(right).__name__
55 raise TypeError(f"Invalid comparison between dtype={left.dtype} and {typ}")
56 return res_values
57
58
59def make_invalid_op(name: str) -> Callable[..., NoReturn]:
60 """
61 Return a binary method that always raises a TypeError.
62
63 Parameters
64 ----------
65 name : str
66
67 Returns
68 -------
69 invalid_op : function
70 """
71
72 def invalid_op(self: object, other: object = None) -> NoReturn:
73 typ = type(self).__name__
74 raise TypeError(f"cannot perform {name} with this index type: {typ}")
75
76 invalid_op.__name__ = name
77 return invalid_op