about summary refs log tree commit diff
path: root/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_callback.py
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_callback.py')
-rw-r--r--.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_callback.py243
1 files changed, 243 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_callback.py b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_callback.py
new file mode 100644
index 00000000..5b6c294d
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/numpy/f2py/tests/test_callback.py
@@ -0,0 +1,243 @@
+import math
+import textwrap
+import sys
+import pytest
+import threading
+import traceback
+import time
+
+import numpy as np
+from numpy.testing import IS_PYPY
+from . import util
+
+
+class TestF77Callback(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "callback", "foo.f")]
+
+    @pytest.mark.parametrize("name", "t,t2".split(","))
+    def test_all(self, name):
+        self.check_function(name)
+
+    @pytest.mark.xfail(IS_PYPY,
+                       reason="PyPy cannot modify tp_doc after PyType_Ready")
+    def test_docstring(self):
+        expected = textwrap.dedent("""\
+        a = t(fun,[fun_extra_args])
+
+        Wrapper for ``t``.
+
+        Parameters
+        ----------
+        fun : call-back function
+
+        Other Parameters
+        ----------------
+        fun_extra_args : input tuple, optional
+            Default: ()
+
+        Returns
+        -------
+        a : int
+
+        Notes
+        -----
+        Call-back functions::
+
+            def fun(): return a
+            Return objects:
+                a : int
+        """)
+        assert self.module.t.__doc__ == expected
+
+    def check_function(self, name):
+        t = getattr(self.module, name)
+        r = t(lambda: 4)
+        assert r == 4
+        r = t(lambda a: 5, fun_extra_args=(6, ))
+        assert r == 5
+        r = t(lambda a: a, fun_extra_args=(6, ))
+        assert r == 6
+        r = t(lambda a: 5 + a, fun_extra_args=(7, ))
+        assert r == 12
+        r = t(lambda a: math.degrees(a), fun_extra_args=(math.pi, ))
+        assert r == 180
+        r = t(math.degrees, fun_extra_args=(math.pi, ))
+        assert r == 180
+
+        r = t(self.module.func, fun_extra_args=(6, ))
+        assert r == 17
+        r = t(self.module.func0)
+        assert r == 11
+        r = t(self.module.func0._cpointer)
+        assert r == 11
+
+        class A:
+            def __call__(self):
+                return 7
+
+            def mth(self):
+                return 9
+
+        a = A()
+        r = t(a)
+        assert r == 7
+        r = t(a.mth)
+        assert r == 9
+
+    @pytest.mark.skipif(sys.platform == 'win32',
+                        reason='Fails with MinGW64 Gfortran (Issue #9673)')
+    def test_string_callback(self):
+        def callback(code):
+            if code == "r":
+                return 0
+            else:
+                return 1
+
+        f = getattr(self.module, "string_callback")
+        r = f(callback)
+        assert r == 0
+
+    @pytest.mark.skipif(sys.platform == 'win32',
+                        reason='Fails with MinGW64 Gfortran (Issue #9673)')
+    def test_string_callback_array(self):
+        # See gh-10027
+        cu1 = np.zeros((1, ), "S8")
+        cu2 = np.zeros((1, 8), "c")
+        cu3 = np.array([""], "S8")
+
+        def callback(cu, lencu):
+            if cu.shape != (lencu,):
+                return 1
+            if cu.dtype != "S8":
+                return 2
+            if not np.all(cu == b""):
+                return 3
+            return 0
+
+        f = getattr(self.module, "string_callback_array")
+        for cu in [cu1, cu2, cu3]:
+            res = f(callback, cu, cu.size)
+            assert res == 0
+
+    def test_threadsafety(self):
+        # Segfaults if the callback handling is not threadsafe
+
+        errors = []
+
+        def cb():
+            # Sleep here to make it more likely for another thread
+            # to call their callback at the same time.
+            time.sleep(1e-3)
+
+            # Check reentrancy
+            r = self.module.t(lambda: 123)
+            assert r == 123
+
+            return 42
+
+        def runner(name):
+            try:
+                for j in range(50):
+                    r = self.module.t(cb)
+                    assert r == 42
+                    self.check_function(name)
+            except Exception:
+                errors.append(traceback.format_exc())
+
+        threads = [
+            threading.Thread(target=runner, args=(arg, ))
+            for arg in ("t", "t2") for n in range(20)
+        ]
+
+        for t in threads:
+            t.start()
+
+        for t in threads:
+            t.join()
+
+        errors = "\n\n".join(errors)
+        if errors:
+            raise AssertionError(errors)
+
+    def test_hidden_callback(self):
+        try:
+            self.module.hidden_callback(2)
+        except Exception as msg:
+            assert str(msg).startswith("Callback global_f not defined")
+
+        try:
+            self.module.hidden_callback2(2)
+        except Exception as msg:
+            assert str(msg).startswith("cb: Callback global_f not defined")
+
+        self.module.global_f = lambda x: x + 1
+        r = self.module.hidden_callback(2)
+        assert r == 3
+
+        self.module.global_f = lambda x: x + 2
+        r = self.module.hidden_callback(2)
+        assert r == 4
+
+        del self.module.global_f
+        try:
+            self.module.hidden_callback(2)
+        except Exception as msg:
+            assert str(msg).startswith("Callback global_f not defined")
+
+        self.module.global_f = lambda x=0: x + 3
+        r = self.module.hidden_callback(2)
+        assert r == 5
+
+        # reproducer of gh18341
+        r = self.module.hidden_callback2(2)
+        assert r == 3
+
+
+class TestF77CallbackPythonTLS(TestF77Callback):
+    """
+    Callback tests using Python thread-local storage instead of
+    compiler-provided
+    """
+
+    options = ["-DF2PY_USE_PYTHON_TLS"]
+
+
+class TestF90Callback(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "callback", "gh17797.f90")]
+
+    def test_gh17797(self):
+        def incr(x):
+            return x + 123
+
+        y = np.array([1, 2, 3], dtype=np.int64)
+        r = self.module.gh17797(incr, y)
+        assert r == 123 + 1 + 2 + 3
+
+
+class TestGH18335(util.F2PyTest):
+    """The reproduction of the reported issue requires specific input that
+    extensions may break the issue conditions, so the reproducer is
+    implemented as a separate test class. Do not extend this test with
+    other tests!
+    """
+    sources = [util.getpath("tests", "src", "callback", "gh18335.f90")]
+
+    def test_gh18335(self):
+        def foo(x):
+            x[0] += 1
+
+        r = self.module.gh18335(foo)
+        assert r == 123 + 1
+
+
+class TestGH25211(util.F2PyTest):
+    sources = [util.getpath("tests", "src", "callback", "gh25211.f"),
+               util.getpath("tests", "src", "callback", "gh25211.pyf")]
+    module_name = "callback2"
+
+    def test_gh18335(self):
+        def bar(x):
+            return x*x
+
+        res = self.module.foo(bar)
+        assert res == 110