Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/pandas/core/ops/common.py: 36%

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

55 statements  

1""" 

2Boilerplate functions used in defining binary operations. 

3""" 

4 

5from __future__ import annotations 

6 

7from functools import wraps 

8from typing import TYPE_CHECKING 

9 

10from pandas._libs.lib import item_from_zerodim 

11from pandas._libs.missing import is_matching_na 

12 

13from pandas.core.dtypes.generic import ( 

14 ABCExtensionArray, 

15 ABCIndex, 

16 ABCSeries, 

17) 

18 

19from pandas.core.construction import ( 

20 ensure_wrapped_if_datetimelike, 

21 sanitize_array, 

22) 

23 

24if TYPE_CHECKING: 

25 from collections.abc import Callable 

26 

27 from pandas._typing import F 

28 

29 

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

31 """ 

32 Boilerplate for pandas conventions in arithmetic and comparison methods. 

33 

34 Parameters 

35 ---------- 

36 name : str 

37 

38 Returns 

39 ------- 

40 decorator 

41 """ 

42 

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

44 return _unpack_zerodim_and_defer(method, name) 

45 

46 return wrapper 

47 

48 

49def _unpack_zerodim_and_defer(method: F, name: str) -> F: 

50 """ 

51 Boilerplate for pandas conventions in arithmetic and comparison methods. 

52 

53 Ensure method returns NotImplemented when operating against "senior" 

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

55 

56 Parameters 

57 ---------- 

58 method : binary method 

59 name : str 

60 

61 Returns 

62 ------- 

63 method 

64 """ 

65 is_logical = name.strip("_") in ["or", "xor", "and", "ror", "rxor", "rand"] 

66 

67 @wraps(method) 

68 def new_method(self, other): 

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

70 if prio is not None: 

71 if prio > self.__pandas_priority__: 

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

73 return NotImplemented 

74 

75 other = item_from_zerodim(other) 

76 if ( 

77 isinstance(self, ABCExtensionArray) 

78 and isinstance(other, list) 

79 and not is_logical 

80 ): 

81 # See GH#62423 

82 other = sanitize_array(other, None) 

83 other = ensure_wrapped_if_datetimelike(other) 

84 

85 return method(self, other) 

86 

87 # error: Incompatible return value type (got "Callable[[Any, Any], Any]", 

88 # expected "F") 

89 return new_method # type: ignore[return-value] 

90 

91 

92def get_op_result_name(left, right): 

93 """ 

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

95 should always be either an Index or a Series. 

96 

97 Parameters 

98 ---------- 

99 left : {Series, Index} 

100 right : object 

101 

102 Returns 

103 ------- 

104 name : object 

105 Usually a string 

106 """ 

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

108 name = _maybe_match_name(left, right) 

109 else: 

110 name = left.name 

111 return name 

112 

113 

114def _maybe_match_name(a, b): 

115 """ 

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

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

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

119 they have different names. 

120 

121 Parameters 

122 ---------- 

123 a : object 

124 b : object 

125 

126 Returns 

127 ------- 

128 name : str or None 

129 

130 See Also 

131 -------- 

132 pandas.core.common.consensus_name_attr 

133 """ 

134 a_has = hasattr(a, "name") 

135 b_has = hasattr(b, "name") 

136 if a_has and b_has: 

137 try: 

138 if a.name == b.name: 

139 return a.name 

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

141 # e.g. both are np.nan 

142 return a.name 

143 else: 

144 return None 

145 except TypeError: 

146 # pd.NA 

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

148 return a.name 

149 return None 

150 except ValueError: 

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

152 return None 

153 elif a_has: 

154 return a.name 

155 elif b_has: 

156 return b.name 

157 return None