_exceptions.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. """
  2. Various richly-typed exceptions, that also help us deal with string formatting
  3. in python where it's easier.
  4. By putting the formatting in `__str__`, we also avoid paying the cost for
  5. users who silence the exceptions.
  6. """
  7. from numpy.core.overrides import set_module
  8. def _unpack_tuple(tup):
  9. if len(tup) == 1:
  10. return tup[0]
  11. else:
  12. return tup
  13. def _display_as_base(cls):
  14. """
  15. A decorator that makes an exception class look like its base.
  16. We use this to hide subclasses that are implementation details - the user
  17. should catch the base type, which is what the traceback will show them.
  18. Classes decorated with this decorator are subject to removal without a
  19. deprecation warning.
  20. """
  21. assert issubclass(cls, Exception)
  22. cls.__name__ = cls.__base__.__name__
  23. cls.__qualname__ = cls.__base__.__qualname__
  24. set_module(cls.__base__.__module__)(cls)
  25. return cls
  26. class UFuncTypeError(TypeError):
  27. """ Base class for all ufunc exceptions """
  28. def __init__(self, ufunc):
  29. self.ufunc = ufunc
  30. @_display_as_base
  31. class _UFuncBinaryResolutionError(UFuncTypeError):
  32. """ Thrown when a binary resolution fails """
  33. def __init__(self, ufunc, dtypes):
  34. super().__init__(ufunc)
  35. self.dtypes = tuple(dtypes)
  36. assert len(self.dtypes) == 2
  37. def __str__(self):
  38. return (
  39. "ufunc {!r} cannot use operands with types {!r} and {!r}"
  40. ).format(
  41. self.ufunc.__name__, *self.dtypes
  42. )
  43. @_display_as_base
  44. class _UFuncNoLoopError(UFuncTypeError):
  45. """ Thrown when a ufunc loop cannot be found """
  46. def __init__(self, ufunc, dtypes):
  47. super().__init__(ufunc)
  48. self.dtypes = tuple(dtypes)
  49. def __str__(self):
  50. return (
  51. "ufunc {!r} did not contain a loop with signature matching types "
  52. "{!r} -> {!r}"
  53. ).format(
  54. self.ufunc.__name__,
  55. _unpack_tuple(self.dtypes[:self.ufunc.nin]),
  56. _unpack_tuple(self.dtypes[self.ufunc.nin:])
  57. )
  58. @_display_as_base
  59. class _UFuncCastingError(UFuncTypeError):
  60. def __init__(self, ufunc, casting, from_, to):
  61. super().__init__(ufunc)
  62. self.casting = casting
  63. self.from_ = from_
  64. self.to = to
  65. @_display_as_base
  66. class _UFuncInputCastingError(_UFuncCastingError):
  67. """ Thrown when a ufunc input cannot be casted """
  68. def __init__(self, ufunc, casting, from_, to, i):
  69. super().__init__(ufunc, casting, from_, to)
  70. self.in_i = i
  71. def __str__(self):
  72. # only show the number if more than one input exists
  73. i_str = "{} ".format(self.in_i) if self.ufunc.nin != 1 else ""
  74. return (
  75. "Cannot cast ufunc {!r} input {}from {!r} to {!r} with casting "
  76. "rule {!r}"
  77. ).format(
  78. self.ufunc.__name__, i_str, self.from_, self.to, self.casting
  79. )
  80. @_display_as_base
  81. class _UFuncOutputCastingError(_UFuncCastingError):
  82. """ Thrown when a ufunc output cannot be casted """
  83. def __init__(self, ufunc, casting, from_, to, i):
  84. super().__init__(ufunc, casting, from_, to)
  85. self.out_i = i
  86. def __str__(self):
  87. # only show the number if more than one output exists
  88. i_str = "{} ".format(self.out_i) if self.ufunc.nout != 1 else ""
  89. return (
  90. "Cannot cast ufunc {!r} output {}from {!r} to {!r} with casting "
  91. "rule {!r}"
  92. ).format(
  93. self.ufunc.__name__, i_str, self.from_, self.to, self.casting
  94. )
  95. # Exception used in shares_memory()
  96. @set_module('numpy')
  97. class TooHardError(RuntimeError):
  98. pass
  99. @set_module('numpy')
  100. class AxisError(ValueError, IndexError):
  101. """ Axis supplied was invalid. """
  102. def __init__(self, axis, ndim=None, msg_prefix=None):
  103. # single-argument form just delegates to base class
  104. if ndim is None and msg_prefix is None:
  105. msg = axis
  106. # do the string formatting here, to save work in the C code
  107. else:
  108. msg = ("axis {} is out of bounds for array of dimension {}"
  109. .format(axis, ndim))
  110. if msg_prefix is not None:
  111. msg = "{}: {}".format(msg_prefix, msg)
  112. super(AxisError, self).__init__(msg)
  113. @_display_as_base
  114. class _ArrayMemoryError(MemoryError):
  115. """ Thrown when an array cannot be allocated"""
  116. def __init__(self, shape, dtype):
  117. self.shape = shape
  118. self.dtype = dtype
  119. @property
  120. def _total_size(self):
  121. num_bytes = self.dtype.itemsize
  122. for dim in self.shape:
  123. num_bytes *= dim
  124. return num_bytes
  125. @staticmethod
  126. def _size_to_string(num_bytes):
  127. """ Convert a number of bytes into a binary size string """
  128. # https://en.wikipedia.org/wiki/Binary_prefix
  129. LOG2_STEP = 10
  130. STEP = 1024
  131. units = ['bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB']
  132. unit_i = max(num_bytes.bit_length() - 1, 1) // LOG2_STEP
  133. unit_val = 1 << (unit_i * LOG2_STEP)
  134. n_units = num_bytes / unit_val
  135. del unit_val
  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. # deal with sizes so large that we don't have units for them
  141. if unit_i >= len(units):
  142. new_unit_i = len(units) - 1
  143. n_units *= 1 << ((unit_i - new_unit_i) * LOG2_STEP)
  144. unit_i = new_unit_i
  145. unit_name = units[unit_i]
  146. # format with a sensible number of digits
  147. if unit_i == 0:
  148. # no decimal point on bytes
  149. return '{:.0f} {}'.format(n_units, unit_name)
  150. elif round(n_units) < 1000:
  151. # 3 significant figures, if none are dropped to the left of the .
  152. return '{:#.3g} {}'.format(n_units, unit_name)
  153. else:
  154. # just give all the digits otherwise
  155. return '{:#.0f} {}'.format(n_units, unit_name)
  156. def __str__(self):
  157. size_str = self._size_to_string(self._total_size)
  158. return (
  159. "Unable to allocate {} for an array with shape {} and data type {}"
  160. .format(size_str, self.shape, self.dtype)
  161. )