summaryrefslogtreecommitdiff
path: root/numpy/f2py/tests
diff options
context:
space:
mode:
Diffstat (limited to 'numpy/f2py/tests')
-rw-r--r--numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c7
-rw-r--r--numpy/f2py/tests/test_array_from_pyobj.py81
-rw-r--r--numpy/f2py/tests/test_callback.py22
-rw-r--r--numpy/f2py/tests/test_character.py570
-rw-r--r--numpy/f2py/tests/test_crackfortran.py31
-rw-r--r--numpy/f2py/tests/test_docs.py55
-rw-r--r--numpy/f2py/tests/test_return_character.py2
-rw-r--r--numpy/f2py/tests/util.py10
8 files changed, 749 insertions, 29 deletions
diff --git a/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c b/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c
index c8ae7b9dc..9a8b4a752 100644
--- a/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c
+++ b/numpy/f2py/tests/src/array_from_pyobj/wrapmodule.c
@@ -33,6 +33,7 @@ static PyObject *f2py_rout_wrap_call(PyObject *capi_self,
PyObject *capi_args) {
PyObject * volatile capi_buildvalue = NULL;
int type_num = 0;
+ int elsize = 0;
npy_intp *dims = NULL;
PyObject *dims_capi = Py_None;
int rank = 0;
@@ -41,8 +42,8 @@ static PyObject *f2py_rout_wrap_call(PyObject *capi_self,
PyObject *arr_capi = Py_None;
int i;
- if (!PyArg_ParseTuple(capi_args,"iOiO|:wrap.call",\
- &type_num,&dims_capi,&intent,&arr_capi))
+ if (!PyArg_ParseTuple(capi_args,"iiOiO|:wrap.call",\
+ &type_num,&elsize,&dims_capi,&intent,&arr_capi))
return NULL;
rank = PySequence_Length(dims_capi);
dims = malloc(rank*sizeof(npy_intp));
@@ -58,7 +59,7 @@ static PyObject *f2py_rout_wrap_call(PyObject *capi_self,
goto fail;
}
}
- capi_arr_tmp = array_from_pyobj(type_num,dims,rank,intent|F2PY_INTENT_OUT,arr_capi);
+ capi_arr_tmp = ndarray_from_pyobj(type_num,elsize,dims,rank,intent|F2PY_INTENT_OUT,arr_capi,"wrap.call failed");
if (capi_arr_tmp == NULL) {
free(dims);
return NULL;
diff --git a/numpy/f2py/tests/test_array_from_pyobj.py b/numpy/f2py/tests/test_array_from_pyobj.py
index 5a084bc3e..2b8c8defc 100644
--- a/numpy/f2py/tests/test_array_from_pyobj.py
+++ b/numpy/f2py/tests/test_array_from_pyobj.py
@@ -6,11 +6,18 @@ import pytest
import numpy as np
-from numpy.core.multiarray import typeinfo
+from numpy.testing import assert_, assert_equal
+from numpy.core.multiarray import typeinfo as _typeinfo
from . import util
wrap = None
+# Extend core typeinfo with CHARACTER to test dtype('c')
+_ti = _typeinfo['STRING']
+typeinfo = dict(
+ CHARACTER=type(_ti)(('c', _ti.num, 8, _ti.alignment, _ti.type)),
+ **_typeinfo)
+
def setup_module():
"""
@@ -56,6 +63,7 @@ def flags2names(flags):
"NOTSWAPPED",
"WRITEABLE",
"WRITEBACKIFCOPY",
+ "UPDATEIFCOPY",
"BEHAVED",
"BEHAVED_RO",
"CARRAY",
@@ -116,6 +124,9 @@ _type_names = [
"FLOAT",
"DOUBLE",
"CFLOAT",
+ "STRING1",
+ "STRING5",
+ "CHARACTER",
]
_cast_dict = {"BOOL": ["BOOL"]}
@@ -139,6 +150,10 @@ _cast_dict["DOUBLE"] = _cast_dict["INT"] + ["UINT", "FLOAT", "DOUBLE"]
_cast_dict["CFLOAT"] = _cast_dict["FLOAT"] + ["CFLOAT"]
+_cast_dict['STRING1'] = ['STRING1']
+_cast_dict['STRING5'] = ['STRING5']
+_cast_dict['CHARACTER'] = ['CHARACTER']
+
# 32 bit system malloc typically does not provide the alignment required by
# 16 byte long double types this means the inout intent cannot be satisfied
# and several tests fail as the alignment flag can be randomly true or fals
@@ -184,14 +199,33 @@ class Type:
def _init(self, name):
self.NAME = name.upper()
- info = typeinfo[self.NAME]
- self.type_num = getattr(wrap, "NPY_" + self.NAME)
+
+ if self.NAME == 'CHARACTER':
+ info = typeinfo[self.NAME]
+ self.type_num = getattr(wrap, 'NPY_STRING')
+ self.elsize = 1
+ self.dtype = np.dtype('c')
+ elif self.NAME.startswith('STRING'):
+ info = typeinfo[self.NAME[:6]]
+ self.type_num = getattr(wrap, 'NPY_STRING')
+ self.elsize = int(self.NAME[6:] or 0)
+ self.dtype = np.dtype(f'S{self.elsize}')
+ else:
+ info = typeinfo[self.NAME]
+ self.type_num = getattr(wrap, 'NPY_' + self.NAME)
+ self.elsize = info.bits // 8
+ self.dtype = np.dtype(info.type)
+
assert self.type_num == info.num
- self.dtype = np.dtype(info.type)
self.type = info.type
- self.elsize = info.bits / 8
self.dtypechar = info.char
+ def __repr__(self):
+ return (f"Type({self.NAME})|type_num={self.type_num},"
+ f" dtype={self.dtype},"
+ f" type={self.type}, elsize={self.elsize},"
+ f" dtypechar={self.dtypechar}")
+
def cast_types(self):
return [self.__class__(_m) for _m in _cast_dict[self.NAME]]
@@ -226,6 +260,11 @@ class Type:
class Array:
+
+ def __repr__(self):
+ return (f'Array({self.type}, {self.dims}, {self.intent},'
+ f' {self.obj})|arr={self.arr}')
+
def __init__(self, typ, dims, intent, obj):
self.type = typ
self.dims = dims
@@ -234,7 +273,9 @@ class Array:
self.obj = obj
# arr.dtypechar may be different from typ.dtypechar
- self.arr = wrap.call(typ.type_num, dims, intent.flags, obj)
+ self.arr = wrap.call(typ.type_num,
+ typ.elsize,
+ dims, intent.flags, obj)
assert isinstance(self.arr, np.ndarray)
@@ -289,7 +330,9 @@ class Array:
self.arr.tobytes(),
self.pyarr.tobytes(),
)) # strides
- assert self.arr_attr[5][-2:] == self.pyarr_attr[5][-2:] # descr
+ assert self.arr_attr[5][-2:] == self.pyarr_attr[5][-2:], repr((
+ self.arr_attr[5], self.pyarr_attr[5]
+ )) # descr
assert self.arr_attr[6] == self.pyarr_attr[6], repr((
self.arr_attr[6],
self.pyarr_attr[6],
@@ -338,8 +381,6 @@ class TestIntent:
class TestSharedMemory:
- num2seq = [1, 2]
- num23seq = [[1, 2, 3], [4, 5, 6]]
@pytest.fixture(autouse=True, scope="class", params=_type_names)
def setup_type(self, request):
@@ -347,6 +388,21 @@ class TestSharedMemory:
request.cls.array = lambda self, dims, intent, obj: Array(
Type(request.param), dims, intent, obj)
+ @property
+ def num2seq(self):
+ if self.type.NAME.startswith('STRING'):
+ elsize = self.type.elsize
+ return ['1' * elsize, '2' * elsize]
+ return [1, 2]
+
+ @property
+ def num23seq(self):
+ if self.type.NAME.startswith('STRING'):
+ elsize = self.type.elsize
+ return [['1' * elsize, '2' * elsize, '3' * elsize],
+ ['4' * elsize, '5' * elsize, '6' * elsize]]
+ return [[1, 2, 3], [4, 5, 6]]
+
def test_in_from_2seq(self):
a = self.array([2], intent.in_, self.num2seq)
assert not a.has_shared_memory()
@@ -367,9 +423,9 @@ class TestSharedMemory:
"""Test if intent(in) array can be passed without copies"""
seq = getattr(self, "num" + inp)
obj = np.array(seq, dtype=self.type.dtype, order=order)
- obj.setflags(write=(write == "w"))
+ obj.setflags(write=(write == 'w'))
a = self.array(obj.shape,
- ((order == "C" and intent.in_.c) or intent.in_), obj)
+ ((order == 'C' and intent.in_.c) or intent.in_), obj)
assert a.has_shared_memory()
def test_inout_2seq(self):
@@ -496,6 +552,9 @@ class TestSharedMemory:
def test_in_cache_from_2casttype_failure(self):
for t in self.type.all_types():
+ if t.NAME == 'STRING':
+ # string elsize is 0, so skipping the test
+ continue
if t.elsize >= self.type.elsize:
continue
obj = np.array(self.num2seq, dtype=t.dtype)
diff --git a/numpy/f2py/tests/test_callback.py b/numpy/f2py/tests/test_callback.py
index 4e91430fd..335b78413 100644
--- a/numpy/f2py/tests/test_callback.py
+++ b/numpy/f2py/tests/test_callback.py
@@ -84,8 +84,8 @@ class TestF77Callback(util.F2PyTest):
r = t(a.mth)
assert r == 9
- @pytest.mark.skipif(sys.platform == "win32",
- reason="Fails with MinGW64 Gfortran (Issue #9673)")
+ @pytest.mark.skipif(sys.platform == 'win32',
+ reason='Fails with MinGW64 Gfortran (Issue #9673)')
def test_string_callback(self):
def callback(code):
if code == "r":
@@ -97,24 +97,27 @@ class TestF77Callback(util.F2PyTest):
r = f(callback)
assert r == 0
- @pytest.mark.skipif(sys.platform == "win32",
- reason="Fails with MinGW64 Gfortran (Issue #9673)")
+ @pytest.mark.skipif(sys.platform == 'win32',
+ reason='Fails with MinGW64 Gfortran (Issue #9673)')
def test_string_callback_array(self):
# See gh-10027
- cu = np.zeros((1, 8), "S1")
+ cu1 = np.zeros((1, ), "S8")
+ cu2 = np.zeros((1, 8), "c")
+ cu3 = np.array([""], "S8")
def callback(cu, lencu):
- if cu.shape != (lencu, 8):
+ if cu.shape != (lencu,):
return 1
- if cu.dtype != "S1":
+ if cu.dtype != "S8":
return 2
if not np.all(cu == b""):
return 3
return 0
f = getattr(self.module, "string_callback_array")
- res = f(callback, cu, len(cu))
- assert res == 0
+ for cu in [cu1, cu2, cu3]:
+ res = f(callback, cu, len(cu))
+ assert res == 0
def test_threadsafety(self):
# Segfaults if the callback handling is not threadsafe
@@ -223,6 +226,5 @@ class TestGH18335(util.F2PyTest):
def foo(x):
x[0] += 1
- y = np.array([1, 2, 3], dtype=np.int8)
r = self.module.gh18335(foo)
assert r == 123 + 1
diff --git a/numpy/f2py/tests/test_character.py b/numpy/f2py/tests/test_character.py
new file mode 100644
index 000000000..b54b4d981
--- /dev/null
+++ b/numpy/f2py/tests/test_character.py
@@ -0,0 +1,570 @@
+import pytest
+import textwrap
+from numpy.testing import assert_array_equal, assert_equal, assert_raises
+import numpy as np
+from numpy.f2py.tests import util
+
+
+class TestCharacterString(util.F2PyTest):
+ # options = ['--debug-capi', '--build-dir', '/tmp/test-build-f2py']
+ suffix = '.f90'
+ fprefix = 'test_character_string'
+ length_list = ['1', '3', 'star']
+
+ code = ''
+ for length in length_list:
+ fsuffix = length
+ clength = dict(star='(*)').get(length, length)
+
+ code += textwrap.dedent(f"""
+
+ subroutine {fprefix}_input_{fsuffix}(c, o, n)
+ character*{clength}, intent(in) :: c
+ integer n
+ !f2py integer, depend(c), intent(hide) :: n = slen(c)
+ integer*1, dimension(n) :: o
+ !f2py intent(out) o
+ o = transfer(c, o)
+ end subroutine {fprefix}_input_{fsuffix}
+
+ subroutine {fprefix}_output_{fsuffix}(c, o, n)
+ character*{clength}, intent(out) :: c
+ integer n
+ integer*1, dimension(n), intent(in) :: o
+ !f2py integer, depend(o), intent(hide) :: n = len(o)
+ c = transfer(o, c)
+ end subroutine {fprefix}_output_{fsuffix}
+
+ subroutine {fprefix}_array_input_{fsuffix}(c, o, m, n)
+ integer m, i, n
+ character*{clength}, intent(in), dimension(m) :: c
+ !f2py integer, depend(c), intent(hide) :: m = len(c)
+ !f2py integer, depend(c), intent(hide) :: n = f2py_itemsize(c)
+ integer*1, dimension(m, n), intent(out) :: o
+ do i=1,m
+ o(i, :) = transfer(c(i), o(i, :))
+ end do
+ end subroutine {fprefix}_array_input_{fsuffix}
+
+ subroutine {fprefix}_array_output_{fsuffix}(c, o, m, n)
+ character*{clength}, intent(out), dimension(m) :: c
+ integer n
+ integer*1, dimension(m, n), intent(in) :: o
+ !f2py character(f2py_len=n) :: c
+ !f2py integer, depend(o), intent(hide) :: m = len(o)
+ !f2py integer, depend(o), intent(hide) :: n = shape(o, 1)
+ do i=1,m
+ c(i) = transfer(o(i, :), c(i))
+ end do
+ end subroutine {fprefix}_array_output_{fsuffix}
+
+ subroutine {fprefix}_2d_array_input_{fsuffix}(c, o, m1, m2, n)
+ integer m1, m2, i, j, n
+ character*{clength}, intent(in), dimension(m1, m2) :: c
+ !f2py integer, depend(c), intent(hide) :: m1 = len(c)
+ !f2py integer, depend(c), intent(hide) :: m2 = shape(c, 1)
+ !f2py integer, depend(c), intent(hide) :: n = f2py_itemsize(c)
+ integer*1, dimension(m1, m2, n), intent(out) :: o
+ do i=1,m1
+ do j=1,m2
+ o(i, j, :) = transfer(c(i, j), o(i, j, :))
+ end do
+ end do
+ end subroutine {fprefix}_2d_array_input_{fsuffix}
+ """)
+
+ @pytest.mark.parametrize("length", length_list)
+ def test_input(self, length):
+ fsuffix = {'(*)': 'star'}.get(length, length)
+ f = getattr(self.module, self.fprefix + '_input_' + fsuffix)
+
+ a = {'1': 'a', '3': 'abc', 'star': 'abcde' * 3}[length]
+
+ assert_array_equal(f(a), np.array(list(map(ord, a)), dtype='u1'))
+
+ @pytest.mark.parametrize("length", length_list[:-1])
+ def test_output(self, length):
+ fsuffix = length
+ f = getattr(self.module, self.fprefix + '_output_' + fsuffix)
+
+ a = {'1': 'a', '3': 'abc'}[length]
+
+ assert_array_equal(f(np.array(list(map(ord, a)), dtype='u1')),
+ a.encode())
+
+ @pytest.mark.parametrize("length", length_list)
+ def test_array_input(self, length):
+ fsuffix = length
+ f = getattr(self.module, self.fprefix + '_array_input_' + fsuffix)
+
+ a = np.array([{'1': 'a', '3': 'abc', 'star': 'abcde' * 3}[length],
+ {'1': 'A', '3': 'ABC', 'star': 'ABCDE' * 3}[length],
+ ], dtype='S')
+
+ expected = np.array([[c for c in s] for s in a], dtype='u1')
+ assert_array_equal(f(a), expected)
+
+ @pytest.mark.parametrize("length", length_list)
+ def test_array_output(self, length):
+ fsuffix = length
+ f = getattr(self.module, self.fprefix + '_array_output_' + fsuffix)
+
+ expected = np.array(
+ [{'1': 'a', '3': 'abc', 'star': 'abcde' * 3}[length],
+ {'1': 'A', '3': 'ABC', 'star': 'ABCDE' * 3}[length]], dtype='S')
+
+ a = np.array([[c for c in s] for s in expected], dtype='u1')
+ assert_array_equal(f(a), expected)
+
+ @pytest.mark.parametrize("length", length_list)
+ def test_2d_array_input(self, length):
+ fsuffix = length
+ f = getattr(self.module, self.fprefix + '_2d_array_input_' + fsuffix)
+
+ a = np.array([[{'1': 'a', '3': 'abc', 'star': 'abcde' * 3}[length],
+ {'1': 'A', '3': 'ABC', 'star': 'ABCDE' * 3}[length]],
+ [{'1': 'f', '3': 'fgh', 'star': 'fghij' * 3}[length],
+ {'1': 'F', '3': 'FGH', 'star': 'FGHIJ' * 3}[length]]],
+ dtype='S')
+ expected = np.array([[[c for c in item] for item in row] for row in a],
+ dtype='u1', order='F')
+ assert_array_equal(f(a), expected)
+
+
+class TestCharacter(util.F2PyTest):
+ # options = ['--debug-capi', '--build-dir', '/tmp/test-build-f2py']
+ suffix = '.f90'
+ fprefix = 'test_character'
+
+ code = textwrap.dedent(f"""
+ subroutine {fprefix}_input(c, o)
+ character, intent(in) :: c
+ integer*1 o
+ !f2py intent(out) o
+ o = transfer(c, o)
+ end subroutine {fprefix}_input
+
+ subroutine {fprefix}_output(c, o)
+ character :: c
+ integer*1, intent(in) :: o
+ !f2py intent(out) c
+ c = transfer(o, c)
+ end subroutine {fprefix}_output
+
+ subroutine {fprefix}_input_output(c, o)
+ character, intent(in) :: c
+ character o
+ !f2py intent(out) o
+ o = c
+ end subroutine {fprefix}_input_output
+
+ subroutine {fprefix}_inout(c, n)
+ character :: c, n
+ !f2py intent(in) n
+ !f2py intent(inout) c
+ c = n
+ end subroutine {fprefix}_inout
+
+ function {fprefix}_return(o) result (c)
+ character :: c
+ character, intent(in) :: o
+ c = transfer(o, c)
+ end function {fprefix}_return
+
+ subroutine {fprefix}_array_input(c, o)
+ character, intent(in) :: c(3)
+ integer*1 o(3)
+ !f2py intent(out) o
+ integer i
+ do i=1,3
+ o(i) = transfer(c(i), o(i))
+ end do
+ end subroutine {fprefix}_array_input
+
+ subroutine {fprefix}_2d_array_input(c, o)
+ character, intent(in) :: c(2, 3)
+ integer*1 o(2, 3)
+ !f2py intent(out) o
+ integer i, j
+ do i=1,2
+ do j=1,3
+ o(i, j) = transfer(c(i, j), o(i, j))
+ end do
+ end do
+ end subroutine {fprefix}_2d_array_input
+
+ subroutine {fprefix}_array_output(c, o)
+ character :: c(3)
+ integer*1, intent(in) :: o(3)
+ !f2py intent(out) c
+ do i=1,3
+ c(i) = transfer(o(i), c(i))
+ end do
+ end subroutine {fprefix}_array_output
+
+ subroutine {fprefix}_array_inout(c, n)
+ character :: c(3), n(3)
+ !f2py intent(in) n(3)
+ !f2py intent(inout) c(3)
+ do i=1,3
+ c(i) = n(i)
+ end do
+ end subroutine {fprefix}_array_inout
+
+ subroutine {fprefix}_2d_array_inout(c, n)
+ character :: c(2, 3), n(2, 3)
+ !f2py intent(in) n(2, 3)
+ !f2py intent(inout) c(2. 3)
+ integer i, j
+ do i=1,2
+ do j=1,3
+ c(i, j) = n(i, j)
+ end do
+ end do
+ end subroutine {fprefix}_2d_array_inout
+
+ function {fprefix}_array_return(o) result (c)
+ character, dimension(3) :: c
+ character, intent(in) :: o(3)
+ do i=1,3
+ c(i) = o(i)
+ end do
+ end function {fprefix}_array_return
+
+ function {fprefix}_optional(o) result (c)
+ character, intent(in) :: o
+ !f2py character o = "a"
+ character :: c
+ c = o
+ end function {fprefix}_optional
+ """)
+
+ @pytest.mark.parametrize("dtype", ['c', 'S1'])
+ def test_input(self, dtype):
+ f = getattr(self.module, self.fprefix + '_input')
+
+ assert_equal(f(np.array('a', dtype=dtype)), ord('a'))
+ assert_equal(f(np.array(b'a', dtype=dtype)), ord('a'))
+ assert_equal(f(np.array(['a'], dtype=dtype)), ord('a'))
+ assert_equal(f(np.array('abc', dtype=dtype)), ord('a'))
+ assert_equal(f(np.array([['a']], dtype=dtype)), ord('a'))
+
+ def test_input_varia(self):
+ f = getattr(self.module, self.fprefix + '_input')
+
+ assert_equal(f('a'), ord('a'))
+ assert_equal(f(b'a'), ord(b'a'))
+ assert_equal(f(''), 0)
+ assert_equal(f(b''), 0)
+ assert_equal(f(b'\0'), 0)
+ assert_equal(f('ab'), ord('a'))
+ assert_equal(f(b'ab'), ord('a'))
+ assert_equal(f(['a']), ord('a'))
+
+ assert_equal(f(np.array(b'a')), ord('a'))
+ assert_equal(f(np.array([b'a'])), ord('a'))
+ a = np.array('a')
+ assert_equal(f(a), ord('a'))
+ a = np.array(['a'])
+ assert_equal(f(a), ord('a'))
+
+ try:
+ f([])
+ except IndexError as msg:
+ if not str(msg).endswith(' got 0-list'):
+ raise
+ else:
+ raise SystemError(f'{f.__name__} should have failed on empty list')
+
+ try:
+ f(97)
+ except TypeError as msg:
+ if not str(msg).endswith(' got int instance'):
+ raise
+ else:
+ raise SystemError(f'{f.__name__} should have failed on int value')
+
+ @pytest.mark.parametrize("dtype", ['c', 'S1', 'U1'])
+ def test_array_input(self, dtype):
+ f = getattr(self.module, self.fprefix + '_array_input')
+
+ assert_array_equal(f(np.array(['a', 'b', 'c'], dtype=dtype)),
+ np.array(list(map(ord, 'abc')), dtype='i1'))
+ assert_array_equal(f(np.array([b'a', b'b', b'c'], dtype=dtype)),
+ np.array(list(map(ord, 'abc')), dtype='i1'))
+
+ def test_array_input_varia(self):
+ f = getattr(self.module, self.fprefix + '_array_input')
+ assert_array_equal(f(['a', 'b', 'c']),
+ np.array(list(map(ord, 'abc')), dtype='i1'))
+ assert_array_equal(f([b'a', b'b', b'c']),
+ np.array(list(map(ord, 'abc')), dtype='i1'))
+
+ try:
+ f(['a', 'b', 'c', 'd'])
+ except ValueError as msg:
+ if not str(msg).endswith(
+ 'th dimension must be fixed to 3 but got 4'):
+ raise
+ else:
+ raise SystemError(
+ f'{f.__name__} should have failed on wrong input')
+
+ @pytest.mark.parametrize("dtype", ['c', 'S1', 'U1'])
+ def test_2d_array_input(self, dtype):
+ f = getattr(self.module, self.fprefix + '_2d_array_input')
+
+ a = np.array([['a', 'b', 'c'],
+ ['d', 'e', 'f']], dtype=dtype, order='F')
+ expected = a.view(np.uint32 if dtype == 'U1' else np.uint8)
+ assert_array_equal(f(a), expected)
+
+ def test_output(self):
+ f = getattr(self.module, self.fprefix + '_output')
+
+ assert_equal(f(ord(b'a')), b'a')
+ assert_equal(f(0), b'\0')
+
+ def test_array_output(self):
+ f = getattr(self.module, self.fprefix + '_array_output')
+
+ assert_array_equal(f(list(map(ord, 'abc'))),
+ np.array(list('abc'), dtype='S1'))
+
+ def test_input_output(self):
+ f = getattr(self.module, self.fprefix + '_input_output')
+
+ assert_equal(f(b'a'), b'a')
+ assert_equal(f('a'), b'a')
+ assert_equal(f(''), b'\0')
+
+ @pytest.mark.parametrize("dtype", ['c', 'S1'])
+ def test_inout(self, dtype):
+ f = getattr(self.module, self.fprefix + '_inout')
+
+ a = np.array(list('abc'), dtype=dtype)
+ f(a, 'A')
+ assert_array_equal(a, np.array(list('Abc'), dtype=a.dtype))
+ f(a[1:], 'B')
+ assert_array_equal(a, np.array(list('ABc'), dtype=a.dtype))
+
+ a = np.array(['abc'], dtype=dtype)
+ f(a, 'A')
+ assert_array_equal(a, np.array(['Abc'], dtype=a.dtype))
+
+ def test_inout_varia(self):
+ f = getattr(self.module, self.fprefix + '_inout')
+ a = np.array('abc', dtype='S3')
+ f(a, 'A')
+ assert_array_equal(a, np.array('Abc', dtype=a.dtype))
+
+ a = np.array(['abc'], dtype='S3')
+ f(a, 'A')
+ assert_array_equal(a, np.array(['Abc'], dtype=a.dtype))
+
+ try:
+ f('abc', 'A')
+ except ValueError as msg:
+ if not str(msg).endswith(' got 3-str'):
+ raise
+ else:
+ raise SystemError(f'{f.__name__} should have failed on str value')
+
+ @pytest.mark.parametrize("dtype", ['c', 'S1'])
+ def test_array_inout(self, dtype):
+ f = getattr(self.module, self.fprefix + '_array_inout')
+ n = np.array(['A', 'B', 'C'], dtype=dtype, order='F')
+
+ a = np.array(['a', 'b', 'c'], dtype=dtype, order='F')
+ f(a, n)
+ assert_array_equal(a, n)
+
+ a = np.array(['a', 'b', 'c', 'd'], dtype=dtype)
+ f(a[1:], n)
+ assert_array_equal(a, np.array(['a', 'A', 'B', 'C'], dtype=dtype))
+
+ a = np.array([['a', 'b', 'c']], dtype=dtype, order='F')
+ f(a, n)
+ assert_array_equal(a, np.array([['A', 'B', 'C']], dtype=dtype))
+
+ a = np.array(['a', 'b', 'c', 'd'], dtype=dtype, order='F')
+ try:
+ f(a, n)
+ except ValueError as msg:
+ if not str(msg).endswith(
+ 'th dimension must be fixed to 3 but got 4'):
+ raise
+ else:
+ raise SystemError(
+ f'{f.__name__} should have failed on wrong input')
+
+ @pytest.mark.parametrize("dtype", ['c', 'S1'])
+ def test_2d_array_inout(self, dtype):
+ f = getattr(self.module, self.fprefix + '_2d_array_inout')
+ n = np.array([['A', 'B', 'C'],
+ ['D', 'E', 'F']],
+ dtype=dtype, order='F')
+ a = np.array([['a', 'b', 'c'],
+ ['d', 'e', 'f']],
+ dtype=dtype, order='F')
+ f(a, n)
+ assert_array_equal(a, n)
+
+ def test_return(self):
+ f = getattr(self.module, self.fprefix + '_return')
+
+ assert_equal(f('a'), b'a')
+
+ @pytest.mark.skip('fortran function returning array segfaults')
+ def test_array_return(self):
+ f = getattr(self.module, self.fprefix + '_array_return')
+
+ a = np.array(list('abc'), dtype='S1')
+ assert_array_equal(f(a), a)
+
+ def test_optional(self):
+ f = getattr(self.module, self.fprefix + '_optional')
+
+ assert_equal(f(), b"a")
+ assert_equal(f(b'B'), b"B")
+
+
+class TestMiscCharacter(util.F2PyTest):
+ # options = ['--debug-capi', '--build-dir', '/tmp/test-build-f2py']
+ suffix = '.f90'
+ fprefix = 'test_misc_character'
+
+ code = textwrap.dedent(f"""
+ subroutine {fprefix}_gh18684(x, y, m)
+ character(len=5), dimension(m), intent(in) :: x
+ character*5, dimension(m), intent(out) :: y
+ integer i, m
+ !f2py integer, intent(hide), depend(x) :: m = f2py_len(x)
+ do i=1,m
+ y(i) = x(i)
+ end do
+ end subroutine {fprefix}_gh18684
+
+ subroutine {fprefix}_gh6308(x, i)
+ integer i
+ !f2py check(i>=0 && i<12) i
+ character*5 name, x
+ common name(12)
+ name(i + 1) = x
+ end subroutine {fprefix}_gh6308
+
+ subroutine {fprefix}_gh4519(x)
+ character(len=*), intent(in) :: x(:)
+ !f2py intent(out) x
+ integer :: i
+ do i=1, size(x)
+ print*, "x(",i,")=", x(i)
+ end do
+ end subroutine {fprefix}_gh4519
+
+ pure function {fprefix}_gh3425(x) result (y)
+ character(len=*), intent(in) :: x
+ character(len=len(x)) :: y
+ integer :: i
+ do i = 1, len(x)
+ j = iachar(x(i:i))
+ if (j>=iachar("a") .and. j<=iachar("z") ) then
+ y(i:i) = achar(j-32)
+ else
+ y(i:i) = x(i:i)
+ endif
+ end do
+ end function {fprefix}_gh3425
+
+ subroutine {fprefix}_character_bc_new(x, y, z)
+ character, intent(in) :: x
+ character, intent(out) :: y
+ !f2py character, depend(x) :: y = x
+ !f2py character, dimension((x=='a'?1:2)), depend(x), intent(out) :: z
+ character, dimension(*) :: z
+ !f2py character, optional, check(x == 'a' || x == 'b') :: x = 'a'
+ !f2py callstatement (*f2py_func)(&x, &y, z)
+ !f2py callprotoargument character*, character*, character*
+ if (y.eq.x) then
+ y = x
+ else
+ y = 'e'
+ endif
+ z(1) = 'c'
+ end subroutine {fprefix}_character_bc_new
+
+ subroutine {fprefix}_character_bc_old(x, y, z)
+ character, intent(in) :: x
+ character, intent(out) :: y
+ !f2py character, depend(x) :: y = x[0]
+ !f2py character, dimension((*x=='a'?1:2)), depend(x), intent(out) :: z
+ character, dimension(*) :: z
+ !f2py character, optional, check(*x == 'a' || x[0] == 'b') :: x = 'a'
+ !f2py callstatement (*f2py_func)(x, y, z)
+ !f2py callprotoargument char*, char*, char*
+ if (y.eq.x) then
+ y = x
+ else
+ y = 'e'
+ endif
+ z(1) = 'c'
+ end subroutine {fprefix}_character_bc_old
+ """)
+
+ def test_gh18684(self):
+ # Test character(len=5) and character*5 usages
+ f = getattr(self.module, self.fprefix + '_gh18684')
+ x = np.array(["abcde", "fghij"], dtype='S5')
+ y = f(x)
+
+ assert_array_equal(x, y)
+
+ def test_gh6308(self):
+ # Test character string array in a common block
+ f = getattr(self.module, self.fprefix + '_gh6308')
+
+ assert_equal(self.module._BLNK_.name.dtype, np.dtype('S5'))
+ assert_equal(len(self.module._BLNK_.name), 12)
+ f("abcde", 0)
+ assert_equal(self.module._BLNK_.name[0], b"abcde")
+ f("12345", 5)
+ assert_equal(self.module._BLNK_.name[5], b"12345")
+
+ def test_gh4519(self):
+ # Test array of assumed length strings
+ f = getattr(self.module, self.fprefix + '_gh4519')
+
+ for x, expected in [
+ ('a', dict(shape=(), dtype=np.dtype('S1'))),
+ ('text', dict(shape=(), dtype=np.dtype('S4'))),
+ (np.array(['1', '2', '3'], dtype='S1'),
+ dict(shape=(3,), dtype=np.dtype('S1'))),
+ (['1', '2', '34'],
+ dict(shape=(3,), dtype=np.dtype('S2'))),
+ (['', ''], dict(shape=(2,), dtype=np.dtype('S1')))]:
+ r = f(x)
+ for k, v in expected.items():
+ assert_equal(getattr(r, k), v)
+
+ def test_gh3425(self):
+ # Test returning a copy of assumed length string
+ f = getattr(self.module, self.fprefix + '_gh3425')
+ # f is equivalent to bytes.upper
+
+ assert_equal(f('abC'), b'ABC')
+ assert_equal(f(''), b'')
+ assert_equal(f('abC12d'), b'ABC12D')
+
+ @pytest.mark.parametrize("state", ['new', 'old'])
+ def test_character_bc(self, state):
+ f = getattr(self.module, self.fprefix + '_character_bc_' + state)
+
+ c, a = f()
+ assert_equal(c, b'a')
+ assert_equal(len(a), 1)
+
+ c, a = f(b'b')
+ assert_equal(c, b'b')
+ assert_equal(len(a), 2)
+
+ assert_raises(Exception, lambda: f(b'c'))
diff --git a/numpy/f2py/tests/test_crackfortran.py b/numpy/f2py/tests/test_crackfortran.py
index ea618bf33..53edfc091 100644
--- a/numpy/f2py/tests/test_crackfortran.py
+++ b/numpy/f2py/tests/test_crackfortran.py
@@ -1,3 +1,4 @@
+import codecs
import pytest
import numpy as np
from numpy.f2py.crackfortran import markinnerspaces
@@ -18,7 +19,7 @@ class TestNoSpace(util.F2PyTest):
assert np.allclose(k, w + 1)
self.module.subc([w, k])
assert np.allclose(k, w + 1)
- assert self.module.t0(23) == b"2"
+ assert self.module.t0("23") == b"2"
class TestPublicPrivate:
@@ -122,7 +123,6 @@ class TestMarkinnerspaces:
assert markinnerspaces("a 'b c' 'd e'") == "a 'b@_@c' 'd@_@e'"
assert markinnerspaces(r'a "b c" "d e"') == r'a "b@_@c" "d@_@e"'
-
class TestDimSpec(util.F2PyTest):
"""This test suite tests various expressions that are used as dimension
specifications.
@@ -231,3 +231,30 @@ class TestModuleDeclaration:
mod = crackfortran.crackfortran([str(fpath)])
assert len(mod) == 1
assert mod[0]["vars"]["abar"]["="] == "bar('abar')"
+
+class TestEval(util.F2PyTest):
+ def test_eval_scalar(self):
+ eval_scalar = crackfortran._eval_scalar
+
+ assert eval_scalar('123', {}) == '123'
+ assert eval_scalar('12 + 3', {}) == '15'
+ assert eval_scalar('a + b', dict(a=1, b=2)) == '3'
+ assert eval_scalar('"123"', {}) == "'123'"
+
+
+class TestFortranReader(util.F2PyTest):
+ @pytest.mark.parametrize("encoding",
+ ['ascii', 'utf-8', 'utf-16', 'utf-32'])
+ def test_input_encoding(self, tmp_path, encoding):
+ # gh-635
+ f_path = tmp_path / f"input_with_{encoding}_encoding.f90"
+ # explicit BOM is required for UTF8
+ bom = {'utf-8': codecs.BOM_UTF8}.get(encoding, b'')
+ with f_path.open('w', encoding=encoding) as ff:
+ ff.write(bom.decode(encoding) +
+ """
+ subroutine foo()
+ end subroutine foo
+ """)
+ mod = crackfortran.crackfortran([str(f_path)])
+ assert mod[0]['name'] == 'foo'
diff --git a/numpy/f2py/tests/test_docs.py b/numpy/f2py/tests/test_docs.py
new file mode 100644
index 000000000..4aa5f5f5c
--- /dev/null
+++ b/numpy/f2py/tests/test_docs.py
@@ -0,0 +1,55 @@
+import os
+import pytest
+import numpy as np
+from numpy.testing import assert_array_equal, assert_equal
+from . import util
+
+
+def get_docdir():
+ # assuming that documentation tests are run from a source
+ # directory
+ return os.path.abspath(os.path.join(
+ os.path.dirname(__file__),
+ '..', '..', '..',
+ 'doc', 'source', 'f2py'))
+
+
+pytestmark = pytest.mark.skipif(
+ not os.path.isdir(get_docdir()),
+ reason=('Could not find f2py documentation sources'
+ f' ({get_docdir()} does not exists)'))
+
+
+def _path(*a):
+ return os.path.join(*((get_docdir(),) + a))
+
+
+class TestDocAdvanced(util.F2PyTest):
+ # options = ['--debug-capi', '--build-dir', '/tmp/build-f2py']
+ sources = [_path('asterisk1.f90'), _path('asterisk2.f90'),
+ _path('ftype.f')]
+
+ def test_asterisk1(self):
+ foo = getattr(self.module, 'foo1')
+ assert_equal(foo(), b'123456789A12')
+
+ def test_asterisk2(self):
+ foo = getattr(self.module, 'foo2')
+ assert_equal(foo(2), b'12')
+ assert_equal(foo(12), b'123456789A12')
+ assert_equal(foo(24), b'123456789A123456789B')
+
+ def test_ftype(self):
+ ftype = self.module
+ ftype.foo()
+ assert_equal(ftype.data.a, 0)
+ ftype.data.a = 3
+ ftype.data.x = [1, 2, 3]
+ assert_equal(ftype.data.a, 3)
+ assert_array_equal(ftype.data.x,
+ np.array([1, 2, 3], dtype=np.float32))
+ ftype.data.x[1] = 45
+ assert_array_equal(ftype.data.x,
+ np.array([1, 45, 3], dtype=np.float32))
+
+ # TODO: implement test methods for other example Fortran codes
diff --git a/numpy/f2py/tests/test_return_character.py b/numpy/f2py/tests/test_return_character.py
index 21055faef..36c1f10f4 100644
--- a/numpy/f2py/tests/test_return_character.py
+++ b/numpy/f2py/tests/test_return_character.py
@@ -10,7 +10,7 @@ IS_S390X = platform.machine() == "s390x"
class TestReturnCharacter(util.F2PyTest):
def check_function(self, t, tname):
if tname in ["t0", "t1", "s0", "s1"]:
- assert t(23) == b"2"
+ assert t("23") == b"2"
r = t("ab")
assert r == b"a"
r = t(array("ab"))
diff --git a/numpy/f2py/tests/util.py b/numpy/f2py/tests/util.py
index ae81bbfc4..5f815f9d7 100644
--- a/numpy/f2py/tests/util.py
+++ b/numpy/f2py/tests/util.py
@@ -58,7 +58,7 @@ def get_module_dir():
def get_temp_module_name():
# Assume single-threaded, and the module dir usable only by this thread
global _module_num
- d = get_module_dir()
+ get_module_dir()
name = "_test_ext_module_%d" % _module_num
_module_num += 1
if name in sys.modules:
@@ -117,6 +117,8 @@ def build_module(source_files, options=[], skip=[], only=[], module_name=None):
if ext in (".f90", ".f", ".c", ".pyf"):
f2py_sources.append(dst)
+ assert f2py_sources
+
# Prepare options
if module_name is None:
module_name = get_temp_module_name()
@@ -332,7 +334,11 @@ class F2PyTest:
only = []
suffix = ".f"
module = None
- module_name = None
+
+ @property
+ def module_name(self):
+ cls = type(self)
+ return f'_{cls.__module__.rsplit(".",1)[-1]}_{cls.__name__}_ext_module'
def setup(self):
if sys.platform == "win32":