Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.9/dist-packages/pandas/core/ops/common.py: 71%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

52 statements  

1""" 

2Boilerplate functions used in defining binary operations. 

3""" 

4from __future__ import annotations 

5 

6from functools import wraps 

7from typing import ( 

8 TYPE_CHECKING, 

9 Callable, 

10) 

11 

12from pandas._libs.lib import item_from_zerodim 

13from pandas._libs.missing import is_matching_na 

14 

15from pandas.core.dtypes.generic import ( 

16 ABCIndex, 

17 ABCSeries, 

18) 

19 

20if TYPE_CHECKING: 

21 from pandas._typing import F 

22 

23 

24def unpack_zerodim_and_defer(name: str) -> Callable[[F], F]: 

25 """ 

26 Boilerplate for pandas conventions in arithmetic and comparison methods. 

27 

28 Parameters 

29 ---------- 

30 name : str 

31 

32 Returns 

33 ------- 

34 decorator 

35 """ 

36 

37 def wrapper(method: F) -> F: 

38 return _unpack_zerodim_and_defer(method, name) 

39 

40 return wrapper 

41 

42 

43def _unpack_zerodim_and_defer(method, name: str): 

44 """ 

45 Boilerplate for pandas conventions in arithmetic and comparison methods. 

46 

47 Ensure method returns NotImplemented when operating against "senior" 

48 classes. Ensure zero-dimensional ndarrays are always unpacked. 

49 

50 Parameters 

51 ---------- 

52 method : binary method 

53 name : str 

54 

55 Returns 

56 ------- 

57 method 

58 """ 

59 stripped_name = name.removeprefix("__").removesuffix("__") 

60 is_cmp = stripped_name in {"eq", "ne", "lt", "le", "gt", "ge"} 

61 

62 @wraps(method) 

63 def new_method(self, other): 

64 if is_cmp and isinstance(self, ABCIndex) and isinstance(other, ABCSeries): 

65 # For comparison ops, Index does *not* defer to Series 

66 pass 

67 else: 

68 prio = getattr(other, "__pandas_priority__", None) 

69 if prio is not None: 

70 if prio > self.__pandas_priority__: 

71 # e.g. other is DataFrame while self is Index/Series/EA 

72 return NotImplemented 

73 

74 other = item_from_zerodim(other) 

75 

76 return method(self, other) 

77 

78 return new_method 

79 

80 

81def get_op_result_name(left, right): 

82 """ 

83 Find the appropriate name to pin to an operation result. This result 

84 should always be either an Index or a Series. 

85 

86 Parameters 

87 ---------- 

88 left : {Series, Index} 

89 right : object 

90 

91 Returns 

92 ------- 

93 name : object 

94 Usually a string 

95 """ 

96 if isinstance(right, (ABCSeries, ABCIndex)): 

97 name = _maybe_match_name(left, right) 

98 else: 

99 name = left.name 

100 return name 

101 

102 

103def _maybe_match_name(a, b): 

104 """ 

105 Try to find a name to attach to the result of an operation between 

106 a and b. If only one of these has a `name` attribute, return that 

107 name. Otherwise return a consensus name if they match or None if 

108 they have different names. 

109 

110 Parameters 

111 ---------- 

112 a : object 

113 b : object 

114 

115 Returns 

116 ------- 

117 name : str or None 

118 

119 See Also 

120 -------- 

121 pandas.core.common.consensus_name_attr 

122 """ 

123 a_has = hasattr(a, "name") 

124 b_has = hasattr(b, "name") 

125 if a_has and b_has: 

126 try: 

127 if a.name == b.name: 

128 return a.name 

129 elif is_matching_na(a.name, b.name): 

130 # e.g. both are np.nan 

131 return a.name 

132 else: 

133 return None 

134 except TypeError: 

135 # pd.NA 

136 if is_matching_na(a.name, b.name): 

137 return a.name 

138 return None 

139 except ValueError: 

140 # e.g. np.int64(1) vs (np.int64(1), np.int64(2)) 

141 return None 

142 elif a_has: 

143 return a.name 

144 elif b_has: 

145 return b.name 

146 return None