From c111e9dac31664fd8398b9231bd3dbf87ad6646f Mon Sep 17 00:00:00 2001 From: Kale Kundert Date: Mon, 3 Jul 2017 22:45:24 -0700 Subject: [PATCH] Avoid making multiple ApproxNumpy types. --- _pytest/python_api.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/_pytest/python_api.py b/_pytest/python_api.py index ab7a0bc5ddd..264d925c308 100644 --- a/_pytest/python_api.py +++ b/_pytest/python_api.py @@ -49,13 +49,9 @@ class ApproxNumpyBase(ApproxBase): """ Perform approximate comparisons for numpy arrays. - This class should not be used directly. Instead, it should be used to make - a subclass that also inherits from `np.ndarray`, e.g.:: - - import numpy as np - ApproxNumpy = type('ApproxNumpy', (ApproxNumpyBase, np.ndarray), {}) - - This bizarre invocation is necessary because the object doing the + This class should not be used directly. Instead, the `inherit_ndarray()` + class method should be used to make a subclass that also inherits from + `np.ndarray`. This indirection is necessary because the object doing the approximate comparison must inherit from `np.ndarray`, or it will only work on the left side of the `==` operator. But importing numpy is relatively expensive, so we also want to avoid that unless we actually have a numpy @@ -81,6 +77,18 @@ class ApproxNumpyBase(ApproxBase): it appears on. """ + subclass = None + + @classmethod + def inherit_ndarray(cls): + import numpy as np + assert not isinstance(cls, np.ndarray) + + if cls.subclass is None: + cls.subclass = type('ApproxNumpy', (ApproxNumpyBase, np.ndarray), {}) + + return cls.subclass + def __new__(cls, expected, rel=None, abs=None, nan_ok=False): """ Numpy uses __new__ (rather than __init__) to initialize objects. @@ -416,8 +424,7 @@ def approx(expected, rel=None, abs=None, nan_ok=False): if _is_numpy_array(expected): # Create the delegate class on the fly. This allow us to inherit from # ``np.ndarray`` while still not importing numpy unless we need to. - import numpy as np - cls = type('ApproxNumpy', (ApproxNumpyBase, np.ndarray), {}) + cls = ApproxNumpyBase.inherit_ndarray() elif isinstance(expected, Mapping): cls = ApproxMapping elif isinstance(expected, Sequence) and not isinstance(expected, String):