Skip to content

Commit

Permalink
ENH: Delay assembling of the error message until __str__ is called
Browse files Browse the repository at this point in the history
Co-Authored-By: Eric Wieser <425260+eric-wieser@users.noreply.github.com>
  • Loading branch information
BvB93 and eric-wieser committed Jul 19, 2021
1 parent 16964ae commit 11f7edf
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 42 deletions.
47 changes: 15 additions & 32 deletions numpy/core/_exceptions.py
Expand Up @@ -180,47 +180,30 @@ class AxisError(ValueError, IndexError):
"""

__slots__ = ("axis", "ndim")
__slots__ = ("axis", "ndim", "_msg")

def __init__(self, axis, ndim=None, msg_prefix=None):
# single-argument form just delegates to base class
if ndim is None and msg_prefix is None:
msg = axis
if ndim is msg_prefix is None:
# single-argument form: directly set the error message
self._msg = axis
self.axis = None
self.ndim = None

# do the string formatting here, to save work in the C code
else:
msg = ("axis {} is out of bounds for array of dimension {}"
.format(axis, ndim))
if msg_prefix is not None:
msg = "{}: {}".format(msg_prefix, msg)
self._msg = msg_prefix
self.axis = axis
self.ndim = ndim

super().__init__(msg)

@classmethod
def _reconstruct(cls, axis, ndim, args):
"""Auxiliary instance constructor used by `__reduce__`.
Directly assign all instance attributes without further sanitization.
This method avoids the `__init__` constructor in order to:
* Circumvent dealing with its axis-based overload.
* Avoid extracting the message prefix from ``self.args`` if applicable.
"""
self = super().__new__(cls)
self.axis = axis
self.ndim = ndim
self.args = args
return self
def __str__(self):
axis = self.axis
ndim = self.ndim

def __reduce__(self):
"""Return state information for `pickle`."""
cls = type(self)
return cls._reconstruct, (self.axis, self.ndim, self.args)
if axis is ndim is None:
return self._msg
else:
msg = f"axis {axis} is out of bounds for array of dimension {ndim}"
if self._msg is not None:
msg = f"{self._msg}: {msg}"
return msg


@_display_as_base
Expand Down
10 changes: 0 additions & 10 deletions numpy/core/tests/test__exceptions.py
Expand Up @@ -86,13 +86,3 @@ def test_pickling(self, args):
attr1 = getattr(exc, name)
attr2 = getattr(exc2, name)
assert attr1 == attr2, name

def test_pickling_compat(self, args):
"""Test that `AxisError` instances pickled prior to NumPy 1.22
can still be unpickled.
"""

This comment has been minimized.

Copy link
@eric-wieser

eric-wieser Jul 19, 2021

Author Member

I think this is still a good test to have - you could just dump the byte output of pickling in the test.

# Attach a `__reduce__` method equivalent to the one used prior to 1.22
exc = np.AxisError(*args)
exc.__reduce__ = super(np.AxisError, exc).__reduce__

assert pickle.loads(pickle.dumps(exc))

0 comments on commit 11f7edf

Please sign in to comment.