Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/numpy/_core/_exceptions.py: 41%

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

85 statements  

1""" 

2Various richly-typed exceptions, that also help us deal with string formatting 

3in python where it's easier. 

4 

5By putting the formatting in `__str__`, we also avoid paying the cost for 

6users who silence the exceptions. 

7""" 

8 

9def _unpack_tuple(tup): 

10 if len(tup) == 1: 

11 return tup[0] 

12 else: 

13 return tup 

14 

15 

16def _display_as_base(cls): 

17 """ 

18 A decorator that makes an exception class look like its base. 

19 

20 We use this to hide subclasses that are implementation details - the user 

21 should catch the base type, which is what the traceback will show them. 

22 

23 Classes decorated with this decorator are subject to removal without a 

24 deprecation warning. 

25 """ 

26 assert issubclass(cls, Exception) 

27 cls.__name__ = cls.__base__.__name__ 

28 return cls 

29 

30 

31class UFuncTypeError(TypeError): 

32 """ Base class for all ufunc exceptions """ 

33 def __init__(self, ufunc): 

34 self.ufunc = ufunc 

35 

36 

37@_display_as_base 

38class _UFuncNoLoopError(UFuncTypeError): 

39 """ Thrown when a ufunc loop cannot be found """ 

40 def __init__(self, ufunc, dtypes): 

41 super().__init__(ufunc) 

42 self.dtypes = tuple(dtypes) 

43 

44 def __str__(self): 

45 return ( 

46 f"ufunc {self.ufunc.__name__!r} did not contain a loop with signature " 

47 f"matching types {_unpack_tuple(self.dtypes[:self.ufunc.nin])!r} " 

48 f"-> {_unpack_tuple(self.dtypes[self.ufunc.nin:])!r}" 

49 ) 

50 

51 

52@_display_as_base 

53class _UFuncBinaryResolutionError(_UFuncNoLoopError): 

54 """ Thrown when a binary resolution fails """ 

55 def __init__(self, ufunc, dtypes): 

56 super().__init__(ufunc, dtypes) 

57 assert len(self.dtypes) == 2 

58 

59 def __str__(self): 

60 return ( 

61 "ufunc {!r} cannot use operands with types {!r} and {!r}" 

62 ).format( 

63 self.ufunc.__name__, *self.dtypes 

64 ) 

65 

66 

67@_display_as_base 

68class _UFuncCastingError(UFuncTypeError): 

69 def __init__(self, ufunc, casting, from_, to): 

70 super().__init__(ufunc) 

71 self.casting = casting 

72 self.from_ = from_ 

73 self.to = to 

74 

75 

76@_display_as_base 

77class _UFuncInputCastingError(_UFuncCastingError): 

78 """ Thrown when a ufunc input cannot be casted """ 

79 def __init__(self, ufunc, casting, from_, to, i): 

80 super().__init__(ufunc, casting, from_, to) 

81 self.in_i = i 

82 

83 def __str__(self): 

84 # only show the number if more than one input exists 

85 i_str = f"{self.in_i} " if self.ufunc.nin != 1 else "" 

86 return ( 

87 f"Cannot cast ufunc {self.ufunc.__name__!r} input {i_str}from " 

88 f"{self.from_!r} to {self.to!r} with casting rule {self.casting!r}" 

89 ) 

90 

91 

92@_display_as_base 

93class _UFuncOutputCastingError(_UFuncCastingError): 

94 """ Thrown when a ufunc output cannot be casted """ 

95 def __init__(self, ufunc, casting, from_, to, i): 

96 super().__init__(ufunc, casting, from_, to) 

97 self.out_i = i 

98 

99 def __str__(self): 

100 # only show the number if more than one output exists 

101 i_str = f"{self.out_i} " if self.ufunc.nout != 1 else "" 

102 return ( 

103 f"Cannot cast ufunc {self.ufunc.__name__!r} output {i_str}from " 

104 f"{self.from_!r} to {self.to!r} with casting rule {self.casting!r}" 

105 ) 

106 

107 

108@_display_as_base 

109class _ArrayMemoryError(MemoryError): 

110 """ Thrown when an array cannot be allocated""" 

111 def __init__(self, shape, dtype): 

112 self.shape = shape 

113 self.dtype = dtype 

114 

115 @property 

116 def _total_size(self): 

117 num_bytes = self.dtype.itemsize 

118 for dim in self.shape: 

119 num_bytes *= dim 

120 return num_bytes 

121 

122 @staticmethod 

123 def _size_to_string(num_bytes): 

124 """ Convert a number of bytes into a binary size string """ 

125 

126 # https://en.wikipedia.org/wiki/Binary_prefix 

127 LOG2_STEP = 10 

128 STEP = 1024 

129 units = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB'] 

130 

131 unit_i = max(num_bytes.bit_length() - 1, 1) // LOG2_STEP 

132 unit_val = 1 << (unit_i * LOG2_STEP) 

133 n_units = num_bytes / unit_val 

134 del unit_val 

135 

136 # ensure we pick a unit that is correct after rounding 

137 if round(n_units) == STEP: 

138 unit_i += 1 

139 n_units /= STEP 

140 

141 # deal with sizes so large that we don't have units for them 

142 if unit_i >= len(units): 

143 new_unit_i = len(units) - 1 

144 n_units *= 1 << ((unit_i - new_unit_i) * LOG2_STEP) 

145 unit_i = new_unit_i 

146 

147 unit_name = units[unit_i] 

148 # format with a sensible number of digits 

149 if unit_i == 0: 

150 # no decimal point on bytes 

151 return f'{n_units:.0f} {unit_name}' 

152 elif round(n_units) < 1000: 

153 # 3 significant figures, if none are dropped to the left of the . 

154 return f'{n_units:#.3g} {unit_name}' 

155 else: 

156 # just give all the digits otherwise 

157 return f'{n_units:#.0f} {unit_name}' 

158 

159 def __str__(self): 

160 size_str = self._size_to_string(self._total_size) 

161 return (f"Unable to allocate {size_str} for an array with shape " 

162 f"{self.shape} and data type {self.dtype}")