import sysconfig
|
|
import pytest
|
|
import numpy as np
|
from numpy.testing import IS_WASM, assert_raises
|
|
# The floating point emulation on ARM EABI systems lacking a hardware FPU is
|
# known to be buggy. This is an attempt to identify these hosts. It may not
|
# catch all possible cases, but it catches the known cases of gh-413 and
|
# gh-15562.
|
hosttype = sysconfig.get_config_var('HOST_GNU_TYPE')
|
arm_softfloat = False if hosttype is None else hosttype.endswith('gnueabi')
|
|
class TestErrstate:
|
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
|
@pytest.mark.skipif(arm_softfloat,
|
reason='platform/cpu issue with FPU (gh-413,-15562)')
|
def test_invalid(self):
|
with np.errstate(all='raise', under='ignore'):
|
a = -np.arange(3)
|
# This should work
|
with np.errstate(invalid='ignore'):
|
np.sqrt(a)
|
# While this should fail!
|
with assert_raises(FloatingPointError):
|
np.sqrt(a)
|
|
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
|
@pytest.mark.skipif(arm_softfloat,
|
reason='platform/cpu issue with FPU (gh-15562)')
|
def test_divide(self):
|
with np.errstate(all='raise', under='ignore'):
|
a = -np.arange(3)
|
# This should work
|
with np.errstate(divide='ignore'):
|
a // 0
|
# While this should fail!
|
with assert_raises(FloatingPointError):
|
a // 0
|
# As should this, see gh-15562
|
with assert_raises(FloatingPointError):
|
a // a
|
|
@pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
|
@pytest.mark.skipif(arm_softfloat,
|
reason='platform/cpu issue with FPU (gh-15562)')
|
def test_errcall(self):
|
count = 0
|
|
def foo(*args):
|
nonlocal count
|
count += 1
|
|
olderrcall = np.geterrcall()
|
with np.errstate(call=foo):
|
assert np.geterrcall() is foo
|
with np.errstate(call=None):
|
assert np.geterrcall() is None
|
assert np.geterrcall() is olderrcall
|
assert count == 0
|
|
with np.errstate(call=foo, invalid="call"):
|
np.array(np.inf) - np.array(np.inf)
|
|
assert count == 1
|
|
def test_errstate_decorator(self):
|
@np.errstate(all='ignore')
|
def foo():
|
a = -np.arange(3)
|
a // 0
|
|
foo()
|
|
def test_errstate_enter_once(self):
|
errstate = np.errstate(invalid="warn")
|
with errstate:
|
pass
|
|
# The errstate context cannot be entered twice as that would not be
|
# thread-safe
|
with pytest.raises(TypeError,
|
match="Cannot enter `np.errstate` twice"):
|
with errstate:
|
pass
|
|
@pytest.mark.skipif(IS_WASM, reason="wasm doesn't support asyncio")
|
def test_asyncio_safe(self):
|
# asyncio may not always work, lets assume its fine if missing
|
# Pyodide/wasm doesn't support it. If this test makes problems,
|
# it should just be skipped liberally (or run differently).
|
asyncio = pytest.importorskip("asyncio")
|
|
@np.errstate(invalid="ignore")
|
def decorated():
|
# Decorated non-async function (it is not safe to decorate an
|
# async one)
|
assert np.geterr()["invalid"] == "ignore"
|
|
async def func1():
|
decorated()
|
await asyncio.sleep(0.1)
|
decorated()
|
|
async def func2():
|
with np.errstate(invalid="raise"):
|
assert np.geterr()["invalid"] == "raise"
|
await asyncio.sleep(0.125)
|
assert np.geterr()["invalid"] == "raise"
|
|
# for good sport, a third one with yet another state:
|
async def func3():
|
with np.errstate(invalid="print"):
|
assert np.geterr()["invalid"] == "print"
|
await asyncio.sleep(0.11)
|
assert np.geterr()["invalid"] == "print"
|
|
async def main():
|
# simply run all three function multiple times:
|
await asyncio.gather(
|
func1(), func2(), func3(), func1(), func2(), func3(),
|
func1(), func2(), func3(), func1(), func2(), func3())
|
|
loop = asyncio.new_event_loop()
|
with np.errstate(invalid="warn"):
|
asyncio.run(main())
|
assert np.geterr()["invalid"] == "warn"
|
|
assert np.geterr()["invalid"] == "warn" # the default
|
loop.close()
|