Let approx() work on more generic sequences
approx() was updated in 9f3122fe
to work better with numpy arrays,
however at the same time the requirements were tightened from
requiring an Iterable to requiring a Sequence - the former being
tested only on interface, while the latter requires subclassing or
registration with the abc.
Since the ApproxSequence only used __iter__ and __len__ this commit
reduces the requirement to only what's used, and allows unregistered
Sequence-like containers to be used.
Since numpy arrays qualify for the new criteria, reorder the checks so
that generic sequences are checked for after numpy arrays.
This commit is contained in:
parent
62967b3110
commit
1a8d9bf254
1
AUTHORS
1
AUTHORS
|
@ -163,6 +163,7 @@ Miro Hrončok
|
||||||
Nathaniel Waisbrot
|
Nathaniel Waisbrot
|
||||||
Ned Batchelder
|
Ned Batchelder
|
||||||
Neven Mundar
|
Neven Mundar
|
||||||
|
Nicholas Devenish
|
||||||
Niclas Olofsson
|
Niclas Olofsson
|
||||||
Nicolas Delaby
|
Nicolas Delaby
|
||||||
Oleg Pidsadnyi
|
Oleg Pidsadnyi
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Loosen the definition of what ``approx`` considers a sequence
|
|
@ -45,11 +45,11 @@ MODULE_NOT_FOUND_ERROR = "ModuleNotFoundError" if PY36 else "ImportError"
|
||||||
|
|
||||||
if _PY3:
|
if _PY3:
|
||||||
from collections.abc import MutableMapping as MappingMixin
|
from collections.abc import MutableMapping as MappingMixin
|
||||||
from collections.abc import Mapping, Sequence
|
from collections.abc import Iterable, Mapping, Sequence, Sized
|
||||||
else:
|
else:
|
||||||
# those raise DeprecationWarnings in Python >=3.7
|
# those raise DeprecationWarnings in Python >=3.7
|
||||||
from collections import MutableMapping as MappingMixin # noqa
|
from collections import MutableMapping as MappingMixin # noqa
|
||||||
from collections import Mapping, Sequence # noqa
|
from collections import Iterable, Mapping, Sequence, Sized # noqa
|
||||||
|
|
||||||
|
|
||||||
if sys.version_info >= (3, 4):
|
if sys.version_info >= (3, 4):
|
||||||
|
|
|
@ -11,8 +11,9 @@ from six.moves import zip
|
||||||
|
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
from _pytest.compat import isclass
|
from _pytest.compat import isclass
|
||||||
|
from _pytest.compat import Iterable
|
||||||
from _pytest.compat import Mapping
|
from _pytest.compat import Mapping
|
||||||
from _pytest.compat import Sequence
|
from _pytest.compat import Sized
|
||||||
from _pytest.compat import STRING_TYPES
|
from _pytest.compat import STRING_TYPES
|
||||||
from _pytest.outcomes import fail
|
from _pytest.outcomes import fail
|
||||||
|
|
||||||
|
@ -182,7 +183,7 @@ class ApproxMapping(ApproxBase):
|
||||||
raise _non_numeric_type_error(self.expected, at="key={!r}".format(key))
|
raise _non_numeric_type_error(self.expected, at="key={!r}".format(key))
|
||||||
|
|
||||||
|
|
||||||
class ApproxSequence(ApproxBase):
|
class ApproxSequencelike(ApproxBase):
|
||||||
"""
|
"""
|
||||||
Perform approximate comparisons where the expected value is a sequence of
|
Perform approximate comparisons where the expected value is a sequence of
|
||||||
numbers.
|
numbers.
|
||||||
|
@ -518,10 +519,14 @@ def approx(expected, rel=None, abs=None, nan_ok=False):
|
||||||
cls = ApproxScalar
|
cls = ApproxScalar
|
||||||
elif isinstance(expected, Mapping):
|
elif isinstance(expected, Mapping):
|
||||||
cls = ApproxMapping
|
cls = ApproxMapping
|
||||||
elif isinstance(expected, Sequence) and not isinstance(expected, STRING_TYPES):
|
|
||||||
cls = ApproxSequence
|
|
||||||
elif _is_numpy_array(expected):
|
elif _is_numpy_array(expected):
|
||||||
cls = ApproxNumpy
|
cls = ApproxNumpy
|
||||||
|
elif (
|
||||||
|
isinstance(expected, Iterable)
|
||||||
|
and isinstance(expected, Sized)
|
||||||
|
and not isinstance(expected, STRING_TYPES)
|
||||||
|
):
|
||||||
|
cls = ApproxSequencelike
|
||||||
else:
|
else:
|
||||||
raise _non_numeric_type_error(expected, at=None)
|
raise _non_numeric_type_error(expected, at=None)
|
||||||
|
|
||||||
|
|
|
@ -496,3 +496,13 @@ class TestApprox(object):
|
||||||
assert actual != approx(expected, rel=5e-8, abs=0)
|
assert actual != approx(expected, rel=5e-8, abs=0)
|
||||||
assert approx(expected, rel=5e-7, abs=0) == actual
|
assert approx(expected, rel=5e-7, abs=0) == actual
|
||||||
assert approx(expected, rel=5e-8, abs=0) != actual
|
assert approx(expected, rel=5e-8, abs=0) != actual
|
||||||
|
|
||||||
|
def test_generic_iterable_sized_object(self):
|
||||||
|
class newIterable(object):
|
||||||
|
def __iter__(self):
|
||||||
|
return iter([1, 2, 3, 4])
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return 4
|
||||||
|
|
||||||
|
assert [1, 2, 3, 4] == approx(newIterable())
|
||||||
|
|
Loading…
Reference in New Issue