codekingpro/portable-devtools
115k
1import sysconfig
2
3import pytest
4
5import numpy as np
6from numpy.testing import IS_WASM, assert_raises
7
8# The floating point emulation on ARM EABI systems lacking a hardware FPU is
9# known to be buggy. This is an attempt to identify these hosts. It may not
10# catch all possible cases, but it catches the known cases of gh-413 and
11# gh-15562.
12hosttype = sysconfig.get_config_var('HOST_GNU_TYPE')
13arm_softfloat = False if hosttype is None else hosttype.endswith('gnueabi')
14
15class TestErrstate:
16 @pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
17 @pytest.mark.skipif(arm_softfloat,
18 reason='platform/cpu issue with FPU (gh-413,-15562)')
19 def test_invalid(self):
20 with np.errstate(all='raise', under='ignore'):
21 a = -np.arange(3)
22 # This should work
23 with np.errstate(invalid='ignore'):
24 np.sqrt(a)
25 # While this should fail!
26 with assert_raises(FloatingPointError):
27 np.sqrt(a)
28
29 @pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
30 @pytest.mark.skipif(arm_softfloat,
31 reason='platform/cpu issue with FPU (gh-15562)')
32 def test_divide(self):
33 with np.errstate(all='raise', under='ignore'):
34 a = -np.arange(3)
35 # This should work
36 with np.errstate(divide='ignore'):
37 a // 0
38 # While this should fail!
39 with assert_raises(FloatingPointError):
40 a // 0
41 # As should this, see gh-15562
42 with assert_raises(FloatingPointError):
43 a // a
44
45 @pytest.mark.skipif(IS_WASM, reason="fp errors don't work in wasm")
46 @pytest.mark.skipif(arm_softfloat,
47 reason='platform/cpu issue with FPU (gh-15562)')
48 def test_errcall(self):
49 count = 0
50
51 def foo(*args):
52 nonlocal count
53 count += 1
54
55 olderrcall = np.geterrcall()
56 with np.errstate(call=foo):
57 assert np.geterrcall() is foo
58 with np.errstate(call=None):
59 assert np.geterrcall() is None
60 assert np.geterrcall() is olderrcall
61 assert count == 0
62
63 with np.errstate(call=foo, invalid="call"):
64 np.array(np.inf) - np.array(np.inf)
65
66 assert count == 1
67
68 def test_errstate_decorator(self):
69 @np.errstate(all='ignore')
70 def foo():
71 a = -np.arange(3)
72 a // 0
73
74 foo()
75
76 def test_errstate_enter_once(self):
77 errstate = np.errstate(invalid="warn")
78 with errstate:
79 pass
80
81 # The errstate context cannot be entered twice as that would not be
82 # thread-safe
83 with pytest.raises(TypeError,
84 match="Cannot enter `np.errstate` twice"):
85 with errstate:
86 pass
87
88 @pytest.mark.skipif(IS_WASM, reason="wasm doesn't support asyncio")
89 def test_asyncio_safe(self):
90 # asyncio may not always work, let's assume its fine if missing
91 # Pyodide/wasm doesn't support it. If this test makes problems,
92 # it should just be skipped liberally (or run differently).
93 asyncio = pytest.importorskip("asyncio")
94
95 @np.errstate(invalid="ignore")
96 def decorated():
97 # Decorated non-async function (it is not safe to decorate an
98 # async one)
99 assert np.geterr()["invalid"] == "ignore"
100
101 async def func1():
102 decorated()
103 await asyncio.sleep(0.1)
104 decorated()
105
106 async def func2():
107 with np.errstate(invalid="raise"):
108 assert np.geterr()["invalid"] == "raise"
109 await asyncio.sleep(0.125)
110 assert np.geterr()["invalid"] == "raise"
111
112 # for good sport, a third one with yet another state:
113 async def func3():
114 with np.errstate(invalid="print"):
115 assert np.geterr()["invalid"] == "print"
116 await asyncio.sleep(0.11)
117 assert np.geterr()["invalid"] == "print"
118
119 async def main():
120 # simply run all three function multiple times:
121 await asyncio.gather(
122 func1(), func2(), func3(), func1(), func2(), func3(),
123 func1(), func2(), func3(), func1(), func2(), func3())
124
125 loop = asyncio.new_event_loop()
126 with np.errstate(invalid="warn"):
127 asyncio.run(main())
128 assert np.geterr()["invalid"] == "warn"
129
130 assert np.geterr()["invalid"] == "warn" # the default
131 loop.close()
132 