Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
test_callback.py264 linesDownload Raw Back to tests
1import math
2import platform
3import sys
4import textwrap
5import threading
6import time
7import traceback
8
9import pytest
10
11import numpy as np
12from numpy.testing import IS_PYPY
13
14from . import util
15
16
17class TestF77Callback(util.F2PyTest):
18    sources = [util.getpath("tests", "src", "callback", "foo.f")]
19
20    @pytest.mark.parametrize("name", ["t", "t2"])
21    @pytest.mark.slow
22    def test_all(self, name):
23        self.check_function(name)
24
25    @pytest.mark.xfail(IS_PYPY,
26                       reason="PyPy cannot modify tp_doc after PyType_Ready")
27    def test_docstring(self):
28        expected = textwrap.dedent("""\
29        a = t(fun,[fun_extra_args])
30
31        Wrapper for ``t``.
32
33        Parameters
34        ----------
35        fun : call-back function
36
37        Other Parameters
38        ----------------
39        fun_extra_args : input tuple, optional
40            Default: ()
41
42        Returns
43        -------
44        a : int
45
46        Notes
47        -----
48        Call-back functions::
49
50            def fun(): return a
51            Return objects:
52                a : int
53        """)
54        assert self.module.t.__doc__ == expected
55
56    def check_function(self, name):
57        t = getattr(self.module, name)
58        r = t(lambda: 4)
59        assert r == 4
60        r = t(lambda a: 5, fun_extra_args=(6, ))
61        assert r == 5
62        r = t(lambda a: a, fun_extra_args=(6, ))
63        assert r == 6
64        r = t(lambda a: 5 + a, fun_extra_args=(7, ))
65        assert r == 12
66        r = t(math.degrees, fun_extra_args=(math.pi, ))
67        assert r == 180
68        r = t(math.degrees, fun_extra_args=(math.pi, ))
69        assert r == 180
70
71        r = t(self.module.func, fun_extra_args=(6, ))
72        assert r == 17
73        r = t(self.module.func0)
74        assert r == 11
75        r = t(self.module.func0._cpointer)
76        assert r == 11
77
78        class A:
79            def __call__(self):
80                return 7
81
82            def mth(self):
83                return 9
84
85        a = A()
86        r = t(a)
87        assert r == 7
88        r = t(a.mth)
89        assert r == 9
90
91    @pytest.mark.skipif(sys.platform == 'win32',
92                        reason='Fails with MinGW64 Gfortran (Issue #9673)')
93    def test_string_callback(self):
94        def callback(code):
95            if code == "r":
96                return 0
97            else:
98                return 1
99
100        f = self.module.string_callback
101        r = f(callback)
102        assert r == 0
103
104    @pytest.mark.skipif(sys.platform == 'win32',
105                        reason='Fails with MinGW64 Gfortran (Issue #9673)')
106    def test_string_callback_array(self):
107        # See gh-10027
108        cu1 = np.zeros((1, ), "S8")
109        cu2 = np.zeros((1, 8), "c")
110        cu3 = np.array([""], "S8")
111
112        def callback(cu, lencu):
113            if cu.shape != (lencu,):
114                return 1
115            if cu.dtype != "S8":
116                return 2
117            if not np.all(cu == b""):
118                return 3
119            return 0
120
121        f = self.module.string_callback_array
122        for cu in [cu1, cu2, cu3]:
123            res = f(callback, cu, cu.size)
124            assert res == 0
125
126    def test_threadsafety(self):
127        # Segfaults if the callback handling is not threadsafe
128
129        errors = []
130
131        def cb():
132            # Sleep here to make it more likely for another thread
133            # to call their callback at the same time.
134            time.sleep(1e-3)
135
136            # Check reentrancy
137            r = self.module.t(lambda: 123)
138            assert r == 123
139
140            return 42
141
142        def runner(name):
143            try:
144                for j in range(50):
145                    r = self.module.t(cb)
146                    assert r == 42
147                    self.check_function(name)
148            except Exception:
149                errors.append(traceback.format_exc())
150
151        threads = [
152            threading.Thread(target=runner, args=(arg, ))
153            for arg in ("t", "t2") for n in range(20)
154        ]
155
156        for t in threads:
157            t.start()
158
159        for t in threads:
160            t.join()
161
162        errors = "\n\n".join(errors)
163        if errors:
164            raise AssertionError(errors)
165
166    def test_hidden_callback(self):
167        try:
168            self.module.hidden_callback(2)
169        except Exception as msg:
170            assert str(msg).startswith("Callback global_f not defined")
171
172        try:
173            self.module.hidden_callback2(2)
174        except Exception as msg:
175            assert str(msg).startswith("cb: Callback global_f not defined")
176
177        self.module.global_f = lambda x: x + 1
178        r = self.module.hidden_callback(2)
179        assert r == 3
180
181        self.module.global_f = lambda x: x + 2
182        r = self.module.hidden_callback(2)
183        assert r == 4
184
185        del self.module.global_f
186        try:
187            self.module.hidden_callback(2)
188        except Exception as msg:
189            assert str(msg).startswith("Callback global_f not defined")
190
191        self.module.global_f = lambda x=0: x + 3
192        r = self.module.hidden_callback(2)
193        assert r == 5
194
195        # reproducer of gh18341
196        r = self.module.hidden_callback2(2)
197        assert r == 3
198
199
200class TestF77CallbackPythonTLS(TestF77Callback):
201    """
202    Callback tests using Python thread-local storage instead of
203    compiler-provided
204    """
205
206    options = ["-DF2PY_USE_PYTHON_TLS"]
207
208
209class TestF90Callback(util.F2PyTest):
210    sources = [util.getpath("tests", "src", "callback", "gh17797.f90")]
211
212    @pytest.mark.slow
213    def test_gh17797(self):
214        def incr(x):
215            return x + 123
216
217        y = np.array([1, 2, 3], dtype=np.int64)
218        r = self.module.gh17797(incr, y)
219        assert r == 123 + 1 + 2 + 3
220
221
222class TestGH18335(util.F2PyTest):
223    """The reproduction of the reported issue requires specific input that
224    extensions may break the issue conditions, so the reproducer is
225    implemented as a separate test class. Do not extend this test with
226    other tests!
227    """
228    sources = [util.getpath("tests", "src", "callback", "gh18335.f90")]
229
230    @pytest.mark.slow
231    def test_gh18335(self):
232        def foo(x):
233            x[0] += 1
234
235        r = self.module.gh18335(foo)
236        assert r == 123 + 1
237
238
239class TestGH25211(util.F2PyTest):
240    sources = [util.getpath("tests", "src", "callback", "gh25211.f"),
241               util.getpath("tests", "src", "callback", "gh25211.pyf")]
242    module_name = "callback2"
243
244    def test_gh25211(self):
245        def bar(x):
246            return x * x
247
248        res = self.module.foo(bar)
249        assert res == 110
250
251
252@pytest.mark.slow
253@pytest.mark.xfail(condition=(platform.system().lower() == 'darwin'),
254                   run=False,
255                   reason="Callback aborts cause CI failures on macOS")
256class TestCBFortranCallstatement(util.F2PyTest):
257    sources = [util.getpath("tests", "src", "callback", "gh26681.f90")]
258    options = ['--lower']
259
260    def test_callstatement_fortran(self):
261        with pytest.raises(ValueError, match='helpme') as exc:
262            self.module.mypy_abort = self.module.utils.my_abort
263            self.module.utils.do_something('helpme')
264 
codekingpro/portable-devtools · Team Ai