first commit

This commit is contained in:
Ayxan
2022-05-23 00:16:32 +04:00
commit d660f2a4ca
24786 changed files with 4428337 additions and 0 deletions

View File

@ -0,0 +1,230 @@
/*
* This file was auto-generated with f2py (version:2_1330) and hand edited by
* Pearu for testing purposes. Do not edit this file unless you know what you
* are doing!!!
*/
#ifdef __cplusplus
extern "C" {
#endif
/*********************** See f2py2e/cfuncs.py: includes ***********************/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "fortranobject.h"
#include <math.h>
static PyObject *wrap_error;
static PyObject *wrap_module;
/************************************ call ************************************/
static char doc_f2py_rout_wrap_call[] = "\
Function signature:\n\
arr = call(type_num,dims,intent,obj)\n\
Required arguments:\n"
" type_num : input int\n"
" dims : input int-sequence\n"
" intent : input int\n"
" obj : input python object\n"
"Return objects:\n"
" arr : array";
static PyObject *f2py_rout_wrap_call(PyObject *capi_self,
PyObject *capi_args) {
PyObject * volatile capi_buildvalue = NULL;
int type_num = 0;
npy_intp *dims = NULL;
PyObject *dims_capi = Py_None;
int rank = 0;
int intent = 0;
PyArrayObject *capi_arr_tmp = NULL;
PyObject *arr_capi = Py_None;
int i;
if (!PyArg_ParseTuple(capi_args,"iOiO|:wrap.call",\
&type_num,&dims_capi,&intent,&arr_capi))
return NULL;
rank = PySequence_Length(dims_capi);
dims = malloc(rank*sizeof(npy_intp));
for (i=0;i<rank;++i) {
PyObject *tmp;
tmp = PySequence_GetItem(dims_capi, i);
if (tmp == NULL) {
goto fail;
}
dims[i] = (npy_intp)PyLong_AsLong(tmp);
Py_DECREF(tmp);
if (dims[i] == -1 && PyErr_Occurred()) {
goto fail;
}
}
capi_arr_tmp = array_from_pyobj(type_num,dims,rank,intent|F2PY_INTENT_OUT,arr_capi);
if (capi_arr_tmp == NULL) {
free(dims);
return NULL;
}
capi_buildvalue = Py_BuildValue("N",capi_arr_tmp);
free(dims);
return capi_buildvalue;
fail:
free(dims);
return NULL;
}
static char doc_f2py_rout_wrap_attrs[] = "\
Function signature:\n\
arr = array_attrs(arr)\n\
Required arguments:\n"
" arr : input array object\n"
"Return objects:\n"
" data : data address in hex\n"
" nd : int\n"
" dimensions : tuple\n"
" strides : tuple\n"
" base : python object\n"
" (kind,type,type_num,elsize,alignment) : 4-tuple\n"
" flags : int\n"
" itemsize : int\n"
;
static PyObject *f2py_rout_wrap_attrs(PyObject *capi_self,
PyObject *capi_args) {
PyObject *arr_capi = Py_None;
PyArrayObject *arr = NULL;
PyObject *dimensions = NULL;
PyObject *strides = NULL;
char s[100];
int i;
memset(s,0,100);
if (!PyArg_ParseTuple(capi_args,"O!|:wrap.attrs",
&PyArray_Type,&arr_capi))
return NULL;
arr = (PyArrayObject *)arr_capi;
sprintf(s,"%p",PyArray_DATA(arr));
dimensions = PyTuple_New(PyArray_NDIM(arr));
strides = PyTuple_New(PyArray_NDIM(arr));
for (i=0;i<PyArray_NDIM(arr);++i) {
PyTuple_SetItem(dimensions,i,PyLong_FromLong(PyArray_DIM(arr,i)));
PyTuple_SetItem(strides,i,PyLong_FromLong(PyArray_STRIDE(arr,i)));
}
return Py_BuildValue("siNNO(cciii)ii",s,PyArray_NDIM(arr),
dimensions,strides,
(PyArray_BASE(arr)==NULL?Py_None:PyArray_BASE(arr)),
PyArray_DESCR(arr)->kind,
PyArray_DESCR(arr)->type,
PyArray_TYPE(arr),
PyArray_ITEMSIZE(arr),
PyArray_DESCR(arr)->alignment,
PyArray_FLAGS(arr),
PyArray_ITEMSIZE(arr));
}
static PyMethodDef f2py_module_methods[] = {
{"call",f2py_rout_wrap_call,METH_VARARGS,doc_f2py_rout_wrap_call},
{"array_attrs",f2py_rout_wrap_attrs,METH_VARARGS,doc_f2py_rout_wrap_attrs},
{NULL,NULL}
};
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"test_array_from_pyobj_ext",
NULL,
-1,
f2py_module_methods,
NULL,
NULL,
NULL,
NULL
};
PyMODINIT_FUNC PyInit_test_array_from_pyobj_ext(void) {
PyObject *m,*d, *s;
m = wrap_module = PyModule_Create(&moduledef);
Py_SET_TYPE(&PyFortran_Type, &PyType_Type);
import_array();
if (PyErr_Occurred())
Py_FatalError("can't initialize module wrap (failed to import numpy)");
d = PyModule_GetDict(m);
s = PyUnicode_FromString("This module 'wrap' is auto-generated with f2py (version:2_1330).\nFunctions:\n"
" arr = call(type_num,dims,intent,obj)\n"
".");
PyDict_SetItemString(d, "__doc__", s);
wrap_error = PyErr_NewException ("wrap.error", NULL, NULL);
Py_DECREF(s);
#define ADDCONST(NAME, CONST) \
s = PyLong_FromLong(CONST); \
PyDict_SetItemString(d, NAME, s); \
Py_DECREF(s)
ADDCONST("F2PY_INTENT_IN", F2PY_INTENT_IN);
ADDCONST("F2PY_INTENT_INOUT", F2PY_INTENT_INOUT);
ADDCONST("F2PY_INTENT_OUT", F2PY_INTENT_OUT);
ADDCONST("F2PY_INTENT_HIDE", F2PY_INTENT_HIDE);
ADDCONST("F2PY_INTENT_CACHE", F2PY_INTENT_CACHE);
ADDCONST("F2PY_INTENT_COPY", F2PY_INTENT_COPY);
ADDCONST("F2PY_INTENT_C", F2PY_INTENT_C);
ADDCONST("F2PY_OPTIONAL", F2PY_OPTIONAL);
ADDCONST("F2PY_INTENT_INPLACE", F2PY_INTENT_INPLACE);
ADDCONST("NPY_BOOL", NPY_BOOL);
ADDCONST("NPY_BYTE", NPY_BYTE);
ADDCONST("NPY_UBYTE", NPY_UBYTE);
ADDCONST("NPY_SHORT", NPY_SHORT);
ADDCONST("NPY_USHORT", NPY_USHORT);
ADDCONST("NPY_INT", NPY_INT);
ADDCONST("NPY_UINT", NPY_UINT);
ADDCONST("NPY_INTP", NPY_INTP);
ADDCONST("NPY_UINTP", NPY_UINTP);
ADDCONST("NPY_LONG", NPY_LONG);
ADDCONST("NPY_ULONG", NPY_ULONG);
ADDCONST("NPY_LONGLONG", NPY_LONGLONG);
ADDCONST("NPY_ULONGLONG", NPY_ULONGLONG);
ADDCONST("NPY_FLOAT", NPY_FLOAT);
ADDCONST("NPY_DOUBLE", NPY_DOUBLE);
ADDCONST("NPY_LONGDOUBLE", NPY_LONGDOUBLE);
ADDCONST("NPY_CFLOAT", NPY_CFLOAT);
ADDCONST("NPY_CDOUBLE", NPY_CDOUBLE);
ADDCONST("NPY_CLONGDOUBLE", NPY_CLONGDOUBLE);
ADDCONST("NPY_OBJECT", NPY_OBJECT);
ADDCONST("NPY_STRING", NPY_STRING);
ADDCONST("NPY_UNICODE", NPY_UNICODE);
ADDCONST("NPY_VOID", NPY_VOID);
ADDCONST("NPY_NTYPES", NPY_NTYPES);
ADDCONST("NPY_NOTYPE", NPY_NOTYPE);
ADDCONST("NPY_USERDEF", NPY_USERDEF);
ADDCONST("CONTIGUOUS", NPY_ARRAY_C_CONTIGUOUS);
ADDCONST("FORTRAN", NPY_ARRAY_F_CONTIGUOUS);
ADDCONST("OWNDATA", NPY_ARRAY_OWNDATA);
ADDCONST("FORCECAST", NPY_ARRAY_FORCECAST);
ADDCONST("ENSURECOPY", NPY_ARRAY_ENSURECOPY);
ADDCONST("ENSUREARRAY", NPY_ARRAY_ENSUREARRAY);
ADDCONST("ALIGNED", NPY_ARRAY_ALIGNED);
ADDCONST("WRITEABLE", NPY_ARRAY_WRITEABLE);
ADDCONST("UPDATEIFCOPY", NPY_ARRAY_UPDATEIFCOPY);
ADDCONST("WRITEBACKIFCOPY", NPY_ARRAY_WRITEBACKIFCOPY);
ADDCONST("BEHAVED", NPY_ARRAY_BEHAVED);
ADDCONST("BEHAVED_NS", NPY_ARRAY_BEHAVED_NS);
ADDCONST("CARRAY", NPY_ARRAY_CARRAY);
ADDCONST("FARRAY", NPY_ARRAY_FARRAY);
ADDCONST("CARRAY_RO", NPY_ARRAY_CARRAY_RO);
ADDCONST("FARRAY_RO", NPY_ARRAY_FARRAY_RO);
ADDCONST("DEFAULT", NPY_ARRAY_DEFAULT);
ADDCONST("UPDATE_ALL", NPY_ARRAY_UPDATE_ALL);
#undef ADDCONST(
if (PyErr_Occurred())
Py_FatalError("can't initialize module wrap");
#ifdef F2PY_REPORT_ATEXIT
on_exit(f2py_report_on_exit,(void*)"array_from_pyobj.wrap.call");
#endif
return m;
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1 @@
dict(real=dict(rk="double"))

View File

@ -0,0 +1,34 @@
subroutine sum(x, res)
implicit none
real, intent(in) :: x(:)
real, intent(out) :: res
integer :: i
!print *, "sum: size(x) = ", size(x)
res = 0.0
do i = 1, size(x)
res = res + x(i)
enddo
end subroutine sum
function fsum(x) result (res)
implicit none
real, intent(in) :: x(:)
real :: res
integer :: i
!print *, "fsum: size(x) = ", size(x)
res = 0.0
do i = 1, size(x)
res = res + x(i)
enddo
end function fsum

View File

@ -0,0 +1,41 @@
module mod
contains
subroutine sum(x, res)
implicit none
real, intent(in) :: x(:)
real, intent(out) :: res
integer :: i
!print *, "sum: size(x) = ", size(x)
res = 0.0
do i = 1, size(x)
res = res + x(i)
enddo
end subroutine sum
function fsum(x) result (res)
implicit none
real, intent(in) :: x(:)
real :: res
integer :: i
!print *, "fsum: size(x) = ", size(x)
res = 0.0
do i = 1, size(x)
res = res + x(i)
enddo
end function fsum
end module mod

View File

@ -0,0 +1,19 @@
subroutine sum_with_use(x, res)
use precision
implicit none
real(kind=rk), intent(in) :: x(:)
real(kind=rk), intent(out) :: res
integer :: i
!print *, "size(x) = ", size(x)
res = 0.0
do i = 1, size(x)
res = res + x(i)
enddo
end subroutine

View File

@ -0,0 +1,4 @@
module precision
integer, parameter :: rk = selected_real_kind(8)
integer, parameter :: ik = selected_real_kind(4)
end module

View File

@ -0,0 +1,11 @@
SUBROUTINE INITCB
DOUBLE PRECISION LONG
CHARACTER STRING
INTEGER OK
COMMON /BLOCK/ LONG, STRING, OK
LONG = 1.0
STRING = '2'
OK = 3
RETURN
END

View File

@ -0,0 +1,20 @@
subroutine selectedrealkind(p, r, res)
implicit none
integer, intent(in) :: p, r
!f2py integer :: r=0
integer, intent(out) :: res
res = selected_real_kind(p, r)
end subroutine
subroutine selectedintkind(p, res)
implicit none
integer, intent(in) :: p
integer, intent(out) :: res
res = selected_int_kind(p)
end subroutine

View File

@ -0,0 +1,5 @@
subroutine bar11(a)
cf2py intent(out) a
integer a
a = 11
end

View File

@ -0,0 +1,8 @@
module foo_fixed
contains
subroutine bar12(a)
!f2py intent(out) a
integer a
a = 12
end subroutine bar12
end module foo_fixed

View File

@ -0,0 +1,8 @@
module foo_free
contains
subroutine bar13(a)
!f2py intent(out) a
integer a
a = 13
end subroutine bar13
end module foo_free

View File

@ -0,0 +1,12 @@
module mod
integer :: i
integer :: x(4)
real, dimension(2,3) :: a
real, allocatable, dimension(:,:) :: b
contains
subroutine foo
integer :: k
k = 1
a(1,2) = a(1,2)+3
end subroutine foo
end module mod

View File

@ -0,0 +1,57 @@
! Check that parameters are correct intercepted.
! Constants with comma separations are commonly
! used, for instance Pi = 3._dp
subroutine foo(x)
implicit none
integer, parameter :: sp = selected_real_kind(6)
integer, parameter :: dp = selected_real_kind(15)
integer, parameter :: ii = selected_int_kind(9)
integer, parameter :: il = selected_int_kind(18)
real(dp), intent(inout) :: x
dimension x(3)
real(sp), parameter :: three_s = 3._sp
real(dp), parameter :: three_d = 3._dp
integer(ii), parameter :: three_i = 3_ii
integer(il), parameter :: three_l = 3_il
x(1) = x(1) + x(2) * three_s * three_i + x(3) * three_d * three_l
x(2) = x(2) * three_s
x(3) = x(3) * three_l
return
end subroutine
subroutine foo_no(x)
implicit none
integer, parameter :: sp = selected_real_kind(6)
integer, parameter :: dp = selected_real_kind(15)
integer, parameter :: ii = selected_int_kind(9)
integer, parameter :: il = selected_int_kind(18)
real(dp), intent(inout) :: x
dimension x(3)
real(sp), parameter :: three_s = 3.
real(dp), parameter :: three_d = 3.
integer(ii), parameter :: three_i = 3
integer(il), parameter :: three_l = 3
x(1) = x(1) + x(2) * three_s * three_i + x(3) * three_d * three_l
x(2) = x(2) * three_s
x(3) = x(3) * three_l
return
end subroutine
subroutine foo_sum(x)
implicit none
integer, parameter :: sp = selected_real_kind(6)
integer, parameter :: dp = selected_real_kind(15)
integer, parameter :: ii = selected_int_kind(9)
integer, parameter :: il = selected_int_kind(18)
real(dp), intent(inout) :: x
dimension x(3)
real(sp), parameter :: three_s = 2._sp + 1._sp
real(dp), parameter :: three_d = 1._dp + 2._dp
integer(ii), parameter :: three_i = 2_ii + 1_ii
integer(il), parameter :: three_l = 1_il + 2_il
x(1) = x(1) + x(2) * three_s * three_i + x(3) * three_d * three_l
x(2) = x(2) * three_s
x(3) = x(3) * three_l
return
end subroutine

View File

@ -0,0 +1,15 @@
! Check that parameters are correct intercepted.
! Constants with comma separations are commonly
! used, for instance Pi = 3._dp
subroutine foo_compound_int(x)
implicit none
integer, parameter :: ii = selected_int_kind(9)
integer(ii), intent(inout) :: x
dimension x(3)
integer(ii), parameter :: three = 3_ii
integer(ii), parameter :: two = 2_ii
integer(ii), parameter :: six = three * 1_ii * two
x(1) = x(1) + x(2) + x(3) * six
return
end subroutine

View File

@ -0,0 +1,22 @@
! Check that parameters are correct intercepted.
! Constants with comma separations are commonly
! used, for instance Pi = 3._dp
subroutine foo_int(x)
implicit none
integer, parameter :: ii = selected_int_kind(9)
integer(ii), intent(inout) :: x
dimension x(3)
integer(ii), parameter :: three = 3_ii
x(1) = x(1) + x(2) + x(3) * three
return
end subroutine
subroutine foo_long(x)
implicit none
integer, parameter :: ii = selected_int_kind(18)
integer(ii), intent(inout) :: x
dimension x(3)
integer(ii), parameter :: three = 3_ii
x(1) = x(1) + x(2) + x(3) * three
return
end subroutine

View File

@ -0,0 +1,23 @@
! Check that parameters are correct intercepted.
! Specifically that types of constants without
! compound kind specs are correctly inferred
! adapted Gibbs iteration code from pymc
! for this test case
subroutine foo_non_compound_int(x)
implicit none
integer, parameter :: ii = selected_int_kind(9)
integer(ii) maxiterates
parameter (maxiterates=2)
integer(ii) maxseries
parameter (maxseries=2)
integer(ii) wasize
parameter (wasize=maxiterates*maxseries)
integer(ii), intent(inout) :: x
dimension x(wasize)
x(1) = x(1) + x(2) + x(3) + x(4) * wasize
return
end subroutine

View File

@ -0,0 +1,23 @@
! Check that parameters are correct intercepted.
! Constants with comma separations are commonly
! used, for instance Pi = 3._dp
subroutine foo_single(x)
implicit none
integer, parameter :: rp = selected_real_kind(6)
real(rp), intent(inout) :: x
dimension x(3)
real(rp), parameter :: three = 3._rp
x(1) = x(1) + x(2) + x(3) * three
return
end subroutine
subroutine foo_double(x)
implicit none
integer, parameter :: rp = selected_real_kind(15)
real(rp), intent(inout) :: x
dimension x(3)
real(rp), parameter :: three = 3._rp
x(1) = x(1) + x(2) + x(3) * three
return
end subroutine

View File

@ -0,0 +1,9 @@
! Check that intent(in out) translates as intent(inout).
! The separation seems to be a common usage.
subroutine foo(x)
implicit none
real(4), intent(in out) :: x
dimension x(3)
x(1) = x(1) + x(2) + x(3)
return
end

View File

@ -0,0 +1,44 @@
subroutine foo(a, n, m, b)
implicit none
real, intent(in) :: a(n, m)
integer, intent(in) :: n, m
real, intent(out) :: b(size(a, 1))
integer :: i
do i = 1, size(b)
b(i) = sum(a(i,:))
enddo
end subroutine
subroutine trans(x,y)
implicit none
real, intent(in), dimension(:,:) :: x
real, intent(out), dimension( size(x,2), size(x,1) ) :: y
integer :: N, M, i, j
N = size(x,1)
M = size(x,2)
DO i=1,N
do j=1,M
y(j,i) = x(i,j)
END DO
END DO
end subroutine trans
subroutine flatten(x,y)
implicit none
real, intent(in), dimension(:,:) :: x
real, intent(out), dimension( size(x) ) :: y
integer :: N, M, i, j, k
N = size(x,1)
M = size(x,2)
k = 1
DO i=1,N
do j=1,M
y(k) = x(i,j)
k = k + 1
END DO
END DO
end subroutine flatten

View File

@ -0,0 +1,29 @@
MODULE char_test
CONTAINS
SUBROUTINE change_strings(strings, n_strs, out_strings)
IMPLICIT NONE
! Inputs
INTEGER, INTENT(IN) :: n_strs
CHARACTER, INTENT(IN), DIMENSION(2,n_strs) :: strings
CHARACTER, INTENT(OUT), DIMENSION(2,n_strs) :: out_strings
!f2py INTEGER, INTENT(IN) :: n_strs
!f2py CHARACTER, INTENT(IN), DIMENSION(2,n_strs) :: strings
!f2py CHARACTER, INTENT(OUT), DIMENSION(2,n_strs) :: strings
! Misc.
INTEGER*4 :: j
DO j=1, n_strs
out_strings(1,j) = strings(1,j)
out_strings(2,j) = 'A'
END DO
END SUBROUTINE change_strings
END MODULE char_test

View File

@ -0,0 +1,66 @@
import textwrap
from . import util
from numpy.f2py import crackfortran
class TestAbstractInterface(util.F2PyTest):
suffix = '.f90'
skip = ['add1', 'add2']
code = textwrap.dedent("""
module ops_module
abstract interface
subroutine op(x, y, z)
integer, intent(in) :: x, y
integer, intent(out) :: z
end subroutine
end interface
contains
subroutine foo(x, y, r1, r2)
integer, intent(in) :: x, y
integer, intent(out) :: r1, r2
procedure (op) add1, add2
procedure (op), pointer::p
p=>add1
call p(x, y, r1)
p=>add2
call p(x, y, r2)
end subroutine
end module
subroutine add1(x, y, z)
integer, intent(in) :: x, y
integer, intent(out) :: z
z = x + y
end subroutine
subroutine add2(x, y, z)
integer, intent(in) :: x, y
integer, intent(out) :: z
z = x + 2 * y
end subroutine
""")
def test_abstract_interface(self):
assert self.module.ops_module.foo(3, 5) == (8, 13)
def test_parse_abstract_interface(self, tmp_path):
# Test gh18403
f_path = tmp_path / "gh18403_mod.f90"
with f_path.open('w') as ff:
ff.write(textwrap.dedent("""\
module test
abstract interface
subroutine foo()
end subroutine
end interface
end module test
"""))
mod = crackfortran.crackfortran([str(f_path)])
assert len(mod) == 1
assert len(mod[0]['body']) == 1
assert mod[0]['body'][0]['block'] == 'abstract interface'

View File

@ -0,0 +1,596 @@
import os
import sys
import copy
import platform
import pytest
import numpy as np
from numpy.testing import assert_, assert_equal
from numpy.core.multiarray import typeinfo
from . import util
wrap = None
def setup_module():
"""
Build the required testing extension module
"""
global wrap
# Check compiler availability first
if not util.has_c_compiler():
pytest.skip("No C compiler available")
if wrap is None:
config_code = """
config.add_extension('test_array_from_pyobj_ext',
sources=['wrapmodule.c', 'fortranobject.c'],
define_macros=[])
"""
d = os.path.dirname(__file__)
src = [os.path.join(d, 'src', 'array_from_pyobj', 'wrapmodule.c'),
os.path.join(d, '..', 'src', 'fortranobject.c'),
os.path.join(d, '..', 'src', 'fortranobject.h')]
wrap = util.build_module_distutils(src, config_code,
'test_array_from_pyobj_ext')
def flags_info(arr):
flags = wrap.array_attrs(arr)[6]
return flags2names(flags)
def flags2names(flags):
info = []
for flagname in ['CONTIGUOUS', 'FORTRAN', 'OWNDATA', 'ENSURECOPY',
'ENSUREARRAY', 'ALIGNED', 'NOTSWAPPED', 'WRITEABLE',
'WRITEBACKIFCOPY', 'UPDATEIFCOPY', 'BEHAVED', 'BEHAVED_RO',
'CARRAY', 'FARRAY'
]:
if abs(flags) & getattr(wrap, flagname, 0):
info.append(flagname)
return info
class Intent:
def __init__(self, intent_list=[]):
self.intent_list = intent_list[:]
flags = 0
for i in intent_list:
if i == 'optional':
flags |= wrap.F2PY_OPTIONAL
else:
flags |= getattr(wrap, 'F2PY_INTENT_' + i.upper())
self.flags = flags
def __getattr__(self, name):
name = name.lower()
if name == 'in_':
name = 'in'
return self.__class__(self.intent_list + [name])
def __str__(self):
return 'intent(%s)' % (','.join(self.intent_list))
def __repr__(self):
return 'Intent(%r)' % (self.intent_list)
def is_intent(self, *names):
for name in names:
if name not in self.intent_list:
return False
return True
def is_intent_exact(self, *names):
return len(self.intent_list) == len(names) and self.is_intent(*names)
intent = Intent()
_type_names = ['BOOL', 'BYTE', 'UBYTE', 'SHORT', 'USHORT', 'INT', 'UINT',
'LONG', 'ULONG', 'LONGLONG', 'ULONGLONG',
'FLOAT', 'DOUBLE', 'CFLOAT']
_cast_dict = {'BOOL': ['BOOL']}
_cast_dict['BYTE'] = _cast_dict['BOOL'] + ['BYTE']
_cast_dict['UBYTE'] = _cast_dict['BOOL'] + ['UBYTE']
_cast_dict['BYTE'] = ['BYTE']
_cast_dict['UBYTE'] = ['UBYTE']
_cast_dict['SHORT'] = _cast_dict['BYTE'] + ['UBYTE', 'SHORT']
_cast_dict['USHORT'] = _cast_dict['UBYTE'] + ['BYTE', 'USHORT']
_cast_dict['INT'] = _cast_dict['SHORT'] + ['USHORT', 'INT']
_cast_dict['UINT'] = _cast_dict['USHORT'] + ['SHORT', 'UINT']
_cast_dict['LONG'] = _cast_dict['INT'] + ['LONG']
_cast_dict['ULONG'] = _cast_dict['UINT'] + ['ULONG']
_cast_dict['LONGLONG'] = _cast_dict['LONG'] + ['LONGLONG']
_cast_dict['ULONGLONG'] = _cast_dict['ULONG'] + ['ULONGLONG']
_cast_dict['FLOAT'] = _cast_dict['SHORT'] + ['USHORT', 'FLOAT']
_cast_dict['DOUBLE'] = _cast_dict['INT'] + ['UINT', 'FLOAT', 'DOUBLE']
_cast_dict['CFLOAT'] = _cast_dict['FLOAT'] + ['CFLOAT']
# 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
# when numpy gains an aligned allocator the tests could be enabled again
#
# Furthermore, on macOS ARM64, LONGDOUBLE is an alias for DOUBLE.
if ((np.intp().dtype.itemsize != 4 or np.clongdouble().dtype.alignment <= 8) and
sys.platform != 'win32' and
(platform.system(), platform.processor()) != ('Darwin', 'arm')):
_type_names.extend(['LONGDOUBLE', 'CDOUBLE', 'CLONGDOUBLE'])
_cast_dict['LONGDOUBLE'] = _cast_dict['LONG'] + \
['ULONG', 'FLOAT', 'DOUBLE', 'LONGDOUBLE']
_cast_dict['CLONGDOUBLE'] = _cast_dict['LONGDOUBLE'] + \
['CFLOAT', 'CDOUBLE', 'CLONGDOUBLE']
_cast_dict['CDOUBLE'] = _cast_dict['DOUBLE'] + ['CFLOAT', 'CDOUBLE']
class Type:
_type_cache = {}
def __new__(cls, name):
if isinstance(name, np.dtype):
dtype0 = name
name = None
for n, i in typeinfo.items():
if not isinstance(i, type) and dtype0.type is i.type:
name = n
break
obj = cls._type_cache.get(name.upper(), None)
if obj is not None:
return obj
obj = object.__new__(cls)
obj._init(name)
cls._type_cache[name.upper()] = obj
return obj
def _init(self, name):
self.NAME = name.upper()
info = typeinfo[self.NAME]
self.type_num = getattr(wrap, 'NPY_' + self.NAME)
assert_equal(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 cast_types(self):
return [self.__class__(_m) for _m in _cast_dict[self.NAME]]
def all_types(self):
return [self.__class__(_m) for _m in _type_names]
def smaller_types(self):
bits = typeinfo[self.NAME].alignment
types = []
for name in _type_names:
if typeinfo[name].alignment < bits:
types.append(Type(name))
return types
def equal_types(self):
bits = typeinfo[self.NAME].alignment
types = []
for name in _type_names:
if name == self.NAME:
continue
if typeinfo[name].alignment == bits:
types.append(Type(name))
return types
def larger_types(self):
bits = typeinfo[self.NAME].alignment
types = []
for name in _type_names:
if typeinfo[name].alignment > bits:
types.append(Type(name))
return types
class Array:
def __init__(self, typ, dims, intent, obj):
self.type = typ
self.dims = dims
self.intent = intent
self.obj_copy = copy.deepcopy(obj)
self.obj = obj
# arr.dtypechar may be different from typ.dtypechar
self.arr = wrap.call(typ.type_num, dims, intent.flags, obj)
assert_(isinstance(self.arr, np.ndarray), repr(type(self.arr)))
self.arr_attr = wrap.array_attrs(self.arr)
if len(dims) > 1:
if self.intent.is_intent('c'):
assert_(intent.flags & wrap.F2PY_INTENT_C)
assert_(not self.arr.flags['FORTRAN'],
repr((self.arr.flags, getattr(obj, 'flags', None))))
assert_(self.arr.flags['CONTIGUOUS'])
assert_(not self.arr_attr[6] & wrap.FORTRAN)
else:
assert_(not intent.flags & wrap.F2PY_INTENT_C)
assert_(self.arr.flags['FORTRAN'])
assert_(not self.arr.flags['CONTIGUOUS'])
assert_(self.arr_attr[6] & wrap.FORTRAN)
if obj is None:
self.pyarr = None
self.pyarr_attr = None
return
if intent.is_intent('cache'):
assert_(isinstance(obj, np.ndarray), repr(type(obj)))
self.pyarr = np.array(obj).reshape(*dims).copy()
else:
self.pyarr = np.array(
np.array(obj, dtype=typ.dtypechar).reshape(*dims),
order=self.intent.is_intent('c') and 'C' or 'F')
assert_(self.pyarr.dtype == typ,
repr((self.pyarr.dtype, typ)))
self.pyarr.setflags(write=self.arr.flags['WRITEABLE'])
assert_(self.pyarr.flags['OWNDATA'], (obj, intent))
self.pyarr_attr = wrap.array_attrs(self.pyarr)
if len(dims) > 1:
if self.intent.is_intent('c'):
assert_(not self.pyarr.flags['FORTRAN'])
assert_(self.pyarr.flags['CONTIGUOUS'])
assert_(not self.pyarr_attr[6] & wrap.FORTRAN)
else:
assert_(self.pyarr.flags['FORTRAN'])
assert_(not self.pyarr.flags['CONTIGUOUS'])
assert_(self.pyarr_attr[6] & wrap.FORTRAN)
assert_(self.arr_attr[1] == self.pyarr_attr[1]) # nd
assert_(self.arr_attr[2] == self.pyarr_attr[2]) # dimensions
if self.arr_attr[1] <= 1:
assert_(self.arr_attr[3] == self.pyarr_attr[3],
repr((self.arr_attr[3], self.pyarr_attr[3],
self.arr.tobytes(), self.pyarr.tobytes()))) # strides
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],
flags2names(0 * self.arr_attr[6] - self.pyarr_attr[6]),
flags2names(self.arr_attr[6]), intent))) # flags
if intent.is_intent('cache'):
assert_(self.arr_attr[5][3] >= self.type.elsize,
repr((self.arr_attr[5][3], self.type.elsize)))
else:
assert_(self.arr_attr[5][3] == self.type.elsize,
repr((self.arr_attr[5][3], self.type.elsize)))
assert_(self.arr_equal(self.pyarr, self.arr))
if isinstance(self.obj, np.ndarray):
if typ.elsize == Type(obj.dtype).elsize:
if not intent.is_intent('copy') and self.arr_attr[1] <= 1:
assert_(self.has_shared_memory())
def arr_equal(self, arr1, arr2):
if arr1.shape != arr2.shape:
return False
return (arr1 == arr2).all()
def __str__(self):
return str(self.arr)
def has_shared_memory(self):
"""Check that created array shares data with input array.
"""
if self.obj is self.arr:
return True
if not isinstance(self.obj, np.ndarray):
return False
obj_attr = wrap.array_attrs(self.obj)
return obj_attr[0] == self.arr_attr[0]
class TestIntent:
def test_in_out(self):
assert_equal(str(intent.in_.out), 'intent(in,out)')
assert_(intent.in_.c.is_intent('c'))
assert_(not intent.in_.c.is_intent_exact('c'))
assert_(intent.in_.c.is_intent_exact('c', 'in'))
assert_(intent.in_.c.is_intent_exact('in', 'c'))
assert_(not intent.in_.is_intent('c'))
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):
request.cls.type = Type(request.param)
request.cls.array = lambda self, dims, intent, obj: \
Array(Type(request.param), dims, intent, obj)
def test_in_from_2seq(self):
a = self.array([2], intent.in_, self.num2seq)
assert_(not a.has_shared_memory())
def test_in_from_2casttype(self):
for t in self.type.cast_types():
obj = np.array(self.num2seq, dtype=t.dtype)
a = self.array([len(self.num2seq)], intent.in_, obj)
if t.elsize == self.type.elsize:
assert_(
a.has_shared_memory(), repr((self.type.dtype, t.dtype)))
else:
assert_(not a.has_shared_memory(), repr(t.dtype))
@pytest.mark.parametrize('write', ['w', 'ro'])
@pytest.mark.parametrize('order', ['C', 'F'])
@pytest.mark.parametrize('inp', ['2seq', '23seq'])
def test_in_nocopy(self, write, order, inp):
"""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'))
a = self.array(obj.shape, ((order=='C' and intent.in_.c) or intent.in_), obj)
assert a.has_shared_memory()
def test_inout_2seq(self):
obj = np.array(self.num2seq, dtype=self.type.dtype)
a = self.array([len(self.num2seq)], intent.inout, obj)
assert_(a.has_shared_memory())
try:
a = self.array([2], intent.in_.inout, self.num2seq)
except TypeError as msg:
if not str(msg).startswith('failed to initialize intent'
'(inout|inplace|cache) array'):
raise
else:
raise SystemError('intent(inout) should have failed on sequence')
def test_f_inout_23seq(self):
obj = np.array(self.num23seq, dtype=self.type.dtype, order='F')
shape = (len(self.num23seq), len(self.num23seq[0]))
a = self.array(shape, intent.in_.inout, obj)
assert_(a.has_shared_memory())
obj = np.array(self.num23seq, dtype=self.type.dtype, order='C')
shape = (len(self.num23seq), len(self.num23seq[0]))
try:
a = self.array(shape, intent.in_.inout, obj)
except ValueError as msg:
if not str(msg).startswith('failed to initialize intent'
'(inout) array'):
raise
else:
raise SystemError(
'intent(inout) should have failed on improper array')
def test_c_inout_23seq(self):
obj = np.array(self.num23seq, dtype=self.type.dtype)
shape = (len(self.num23seq), len(self.num23seq[0]))
a = self.array(shape, intent.in_.c.inout, obj)
assert_(a.has_shared_memory())
def test_in_copy_from_2casttype(self):
for t in self.type.cast_types():
obj = np.array(self.num2seq, dtype=t.dtype)
a = self.array([len(self.num2seq)], intent.in_.copy, obj)
assert_(not a.has_shared_memory(), repr(t.dtype))
def test_c_in_from_23seq(self):
a = self.array([len(self.num23seq), len(self.num23seq[0])],
intent.in_, self.num23seq)
assert_(not a.has_shared_memory())
def test_in_from_23casttype(self):
for t in self.type.cast_types():
obj = np.array(self.num23seq, dtype=t.dtype)
a = self.array([len(self.num23seq), len(self.num23seq[0])],
intent.in_, obj)
assert_(not a.has_shared_memory(), repr(t.dtype))
def test_f_in_from_23casttype(self):
for t in self.type.cast_types():
obj = np.array(self.num23seq, dtype=t.dtype, order='F')
a = self.array([len(self.num23seq), len(self.num23seq[0])],
intent.in_, obj)
if t.elsize == self.type.elsize:
assert_(a.has_shared_memory(), repr(t.dtype))
else:
assert_(not a.has_shared_memory(), repr(t.dtype))
def test_c_in_from_23casttype(self):
for t in self.type.cast_types():
obj = np.array(self.num23seq, dtype=t.dtype)
a = self.array([len(self.num23seq), len(self.num23seq[0])],
intent.in_.c, obj)
if t.elsize == self.type.elsize:
assert_(a.has_shared_memory(), repr(t.dtype))
else:
assert_(not a.has_shared_memory(), repr(t.dtype))
def test_f_copy_in_from_23casttype(self):
for t in self.type.cast_types():
obj = np.array(self.num23seq, dtype=t.dtype, order='F')
a = self.array([len(self.num23seq), len(self.num23seq[0])],
intent.in_.copy, obj)
assert_(not a.has_shared_memory(), repr(t.dtype))
def test_c_copy_in_from_23casttype(self):
for t in self.type.cast_types():
obj = np.array(self.num23seq, dtype=t.dtype)
a = self.array([len(self.num23seq), len(self.num23seq[0])],
intent.in_.c.copy, obj)
assert_(not a.has_shared_memory(), repr(t.dtype))
def test_in_cache_from_2casttype(self):
for t in self.type.all_types():
if t.elsize != self.type.elsize:
continue
obj = np.array(self.num2seq, dtype=t.dtype)
shape = (len(self.num2seq),)
a = self.array(shape, intent.in_.c.cache, obj)
assert_(a.has_shared_memory(), repr(t.dtype))
a = self.array(shape, intent.in_.cache, obj)
assert_(a.has_shared_memory(), repr(t.dtype))
obj = np.array(self.num2seq, dtype=t.dtype, order='F')
a = self.array(shape, intent.in_.c.cache, obj)
assert_(a.has_shared_memory(), repr(t.dtype))
a = self.array(shape, intent.in_.cache, obj)
assert_(a.has_shared_memory(), repr(t.dtype))
try:
a = self.array(shape, intent.in_.cache, obj[::-1])
except ValueError as msg:
if not str(msg).startswith('failed to initialize'
' intent(cache) array'):
raise
else:
raise SystemError(
'intent(cache) should have failed on multisegmented array')
def test_in_cache_from_2casttype_failure(self):
for t in self.type.all_types():
if t.elsize >= self.type.elsize:
continue
obj = np.array(self.num2seq, dtype=t.dtype)
shape = (len(self.num2seq),)
try:
self.array(shape, intent.in_.cache, obj) # Should succeed
except ValueError as msg:
if not str(msg).startswith('failed to initialize'
' intent(cache) array'):
raise
else:
raise SystemError(
'intent(cache) should have failed on smaller array')
def test_cache_hidden(self):
shape = (2,)
a = self.array(shape, intent.cache.hide, None)
assert_(a.arr.shape == shape)
shape = (2, 3)
a = self.array(shape, intent.cache.hide, None)
assert_(a.arr.shape == shape)
shape = (-1, 3)
try:
a = self.array(shape, intent.cache.hide, None)
except ValueError as msg:
if not str(msg).startswith('failed to create intent'
'(cache|hide)|optional array'):
raise
else:
raise SystemError(
'intent(cache) should have failed on undefined dimensions')
def test_hidden(self):
shape = (2,)
a = self.array(shape, intent.hide, None)
assert_(a.arr.shape == shape)
assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype)))
shape = (2, 3)
a = self.array(shape, intent.hide, None)
assert_(a.arr.shape == shape)
assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype)))
assert_(a.arr.flags['FORTRAN'] and not a.arr.flags['CONTIGUOUS'])
shape = (2, 3)
a = self.array(shape, intent.c.hide, None)
assert_(a.arr.shape == shape)
assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype)))
assert_(not a.arr.flags['FORTRAN'] and a.arr.flags['CONTIGUOUS'])
shape = (-1, 3)
try:
a = self.array(shape, intent.hide, None)
except ValueError as msg:
if not str(msg).startswith('failed to create intent'
'(cache|hide)|optional array'):
raise
else:
raise SystemError('intent(hide) should have failed'
' on undefined dimensions')
def test_optional_none(self):
shape = (2,)
a = self.array(shape, intent.optional, None)
assert_(a.arr.shape == shape)
assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype)))
shape = (2, 3)
a = self.array(shape, intent.optional, None)
assert_(a.arr.shape == shape)
assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype)))
assert_(a.arr.flags['FORTRAN'] and not a.arr.flags['CONTIGUOUS'])
shape = (2, 3)
a = self.array(shape, intent.c.optional, None)
assert_(a.arr.shape == shape)
assert_(a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype)))
assert_(not a.arr.flags['FORTRAN'] and a.arr.flags['CONTIGUOUS'])
def test_optional_from_2seq(self):
obj = self.num2seq
shape = (len(obj),)
a = self.array(shape, intent.optional, obj)
assert_(a.arr.shape == shape)
assert_(not a.has_shared_memory())
def test_optional_from_23seq(self):
obj = self.num23seq
shape = (len(obj), len(obj[0]))
a = self.array(shape, intent.optional, obj)
assert_(a.arr.shape == shape)
assert_(not a.has_shared_memory())
a = self.array(shape, intent.optional.c, obj)
assert_(a.arr.shape == shape)
assert_(not a.has_shared_memory())
def test_inplace(self):
obj = np.array(self.num23seq, dtype=self.type.dtype)
assert_(not obj.flags['FORTRAN'] and obj.flags['CONTIGUOUS'])
shape = obj.shape
a = self.array(shape, intent.inplace, obj)
assert_(obj[1][2] == a.arr[1][2], repr((obj, a.arr)))
a.arr[1][2] = 54
assert_(obj[1][2] == a.arr[1][2] ==
np.array(54, dtype=self.type.dtype), repr((obj, a.arr)))
assert_(a.arr is obj)
assert_(obj.flags['FORTRAN']) # obj attributes are changed inplace!
assert_(not obj.flags['CONTIGUOUS'])
def test_inplace_from_casttype(self):
for t in self.type.cast_types():
if t is self.type:
continue
obj = np.array(self.num23seq, dtype=t.dtype)
assert_(obj.dtype.type == t.type)
assert_(obj.dtype.type is not self.type.type)
assert_(not obj.flags['FORTRAN'] and obj.flags['CONTIGUOUS'])
shape = obj.shape
a = self.array(shape, intent.inplace, obj)
assert_(obj[1][2] == a.arr[1][2], repr((obj, a.arr)))
a.arr[1][2] = 54
assert_(obj[1][2] == a.arr[1][2] ==
np.array(54, dtype=self.type.dtype), repr((obj, a.arr)))
assert_(a.arr is obj)
assert_(obj.flags['FORTRAN']) # obj attributes changed inplace!
assert_(not obj.flags['CONTIGUOUS'])
assert_(obj.dtype.type is self.type.type) # obj changed inplace!

View File

@ -0,0 +1,53 @@
import os
import pytest
import tempfile
from numpy.testing import assert_
from . import util
def _path(*a):
return os.path.join(*((os.path.dirname(__file__),) + a))
class TestAssumedShapeSumExample(util.F2PyTest):
sources = [_path('src', 'assumed_shape', 'foo_free.f90'),
_path('src', 'assumed_shape', 'foo_use.f90'),
_path('src', 'assumed_shape', 'precision.f90'),
_path('src', 'assumed_shape', 'foo_mod.f90'),
_path('src', 'assumed_shape', '.f2py_f2cmap'),
]
@pytest.mark.slow
def test_all(self):
r = self.module.fsum([1, 2])
assert_(r == 3, repr(r))
r = self.module.sum([1, 2])
assert_(r == 3, repr(r))
r = self.module.sum_with_use([1, 2])
assert_(r == 3, repr(r))
r = self.module.mod.sum([1, 2])
assert_(r == 3, repr(r))
r = self.module.mod.fsum([1, 2])
assert_(r == 3, repr(r))
class TestF2cmapOption(TestAssumedShapeSumExample):
def setup(self):
# Use a custom file name for .f2py_f2cmap
self.sources = list(self.sources)
f2cmap_src = self.sources.pop(-1)
self.f2cmap_file = tempfile.NamedTemporaryFile(delete=False)
with open(f2cmap_src, 'rb') as f:
self.f2cmap_file.write(f.read())
self.f2cmap_file.close()
self.sources.append(self.f2cmap_file.name)
self.options = ["--f2cmap", self.f2cmap_file.name]
super().setup()
def teardown(self):
os.unlink(self.f2cmap_file.name)

View File

@ -0,0 +1,23 @@
import sys
import pytest
from . import util
from numpy.testing import assert_equal, IS_PYPY
class TestBlockDocString(util.F2PyTest):
code = """
SUBROUTINE FOO()
INTEGER BAR(2, 3)
COMMON /BLOCK/ BAR
RETURN
END
"""
@pytest.mark.skipif(sys.platform=='win32',
reason='Fails with MinGW64 Gfortran (Issue #9673)')
@pytest.mark.xfail(IS_PYPY,
reason="PyPy cannot modify tp_doc after PyType_Ready")
def test_block_docstring(self):
expected = "bar : 'i'-array(2,3)\n"
assert_equal(self.module.block.__doc__, expected)

View File

@ -0,0 +1,325 @@
import math
import textwrap
import sys
import pytest
import threading
import traceback
import time
import numpy as np
from numpy.testing import assert_, assert_equal, IS_PYPY
from . import util
class TestF77Callback(util.F2PyTest):
code = """
subroutine t(fun,a)
integer a
cf2py intent(out) a
external fun
call fun(a)
end
subroutine func(a)
cf2py intent(in,out) a
integer a
a = a + 11
end
subroutine func0(a)
cf2py intent(out) a
integer a
a = 11
end
subroutine t2(a)
cf2py intent(callback) fun
integer a
cf2py intent(out) a
external fun
call fun(a)
end
subroutine string_callback(callback, a)
external callback
double precision callback
double precision a
character*1 r
cf2py intent(out) a
r = 'r'
a = callback(r)
end
subroutine string_callback_array(callback, cu, lencu, a)
external callback
integer callback
integer lencu
character*8 cu(lencu)
integer a
cf2py intent(out) a
a = callback(cu, lencu)
end
subroutine hidden_callback(a, r)
external global_f
cf2py intent(callback, hide) global_f
integer a, r, global_f
cf2py intent(out) r
r = global_f(a)
end
subroutine hidden_callback2(a, r)
external global_f
integer a, r, global_f
cf2py intent(out) r
r = global_f(a)
end
"""
@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_equal(self.module.t.__doc__, expected)
def check_function(self, name):
t = getattr(self.module, name)
r = t(lambda: 4)
assert_(r == 4, repr(r))
r = t(lambda a: 5, fun_extra_args=(6,))
assert_(r == 5, repr(r))
r = t(lambda a: a, fun_extra_args=(6,))
assert_(r == 6, repr(r))
r = t(lambda a: 5 + a, fun_extra_args=(7,))
assert_(r == 12, repr(r))
r = t(lambda a: math.degrees(a), fun_extra_args=(math.pi,))
assert_(r == 180, repr(r))
r = t(math.degrees, fun_extra_args=(math.pi,))
assert_(r == 180, repr(r))
r = t(self.module.func, fun_extra_args=(6,))
assert_(r == 17, repr(r))
r = t(self.module.func0)
assert_(r == 11, repr(r))
r = t(self.module.func0._cpointer)
assert_(r == 11, repr(r))
class A:
def __call__(self):
return 7
def mth(self):
return 9
a = A()
r = t(a)
assert_(r == 7, repr(r))
r = t(a.mth)
assert_(r == 9, repr(r))
@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, repr(r))
@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')
def callback(cu, lencu):
if cu.shape != (lencu, 8):
return 1
if cu.dtype != 'S1':
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, repr(res))
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):
suffix = '.f90'
code = textwrap.dedent(
"""
function gh17797(f, y) result(r)
external f
integer(8) :: r, f
integer(8), dimension(:) :: y
r = f(0)
r = r + sum(y)
end function gh17797
""")
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!
"""
suffix = '.f90'
code = textwrap.dedent(
"""
! When gh18335_workaround is defined as an extension,
! the issue cannot be reproduced.
!subroutine gh18335_workaround(f, y)
! implicit none
! external f
! integer(kind=1) :: y(1)
! call f(y)
!end subroutine gh18335_workaround
function gh18335(f) result (r)
implicit none
external f
integer(kind=1) :: y(1), r
y(1) = 123
call f(y)
r = y(1)
end function gh18335
""")
def test_gh18335(self):
def foo(x):
x[0] += 1
y = np.array([1, 2, 3], dtype=np.int8)
r = self.module.gh18335(foo)
assert r == 123 + 1

View File

@ -0,0 +1,25 @@
import os
import sys
import pytest
import numpy as np
from . import util
from numpy.testing import assert_array_equal
def _path(*a):
return os.path.join(*((os.path.dirname(__file__),) + a))
class TestCommonBlock(util.F2PyTest):
sources = [_path('src', 'common', 'block.f')]
@pytest.mark.skipif(sys.platform=='win32',
reason='Fails with MinGW64 Gfortran (Issue #9673)')
def test_common_block(self):
self.module.initcb()
assert_array_equal(self.module.block.long_bn,
np.array(1.0, dtype=np.float64))
assert_array_equal(self.module.block.string_bn,
np.array('2', dtype='|S1'))
assert_array_equal(self.module.block.ok,
np.array(3, dtype=np.int32))

View File

@ -0,0 +1,125 @@
"""See https://github.com/numpy/numpy/pull/11937.
"""
import sys
import os
import uuid
from importlib import import_module
import pytest
import numpy.f2py
from numpy.testing import assert_equal
from . import util
def setup_module():
if not util.has_c_compiler():
pytest.skip("Needs C compiler")
if not util.has_f77_compiler():
pytest.skip('Needs FORTRAN 77 compiler')
# extra_args can be a list (since gh-11937) or string.
# also test absence of extra_args
@pytest.mark.parametrize(
"extra_args", [['--noopt', '--debug'], '--noopt --debug', '']
)
@pytest.mark.leaks_references(reason="Imported module seems never deleted.")
def test_f2py_init_compile(extra_args):
# flush through the f2py __init__ compile() function code path as a
# crude test for input handling following migration from
# exec_command() to subprocess.check_output() in gh-11937
# the Fortran 77 syntax requires 6 spaces before any commands, but
# more space may be added/
fsource = """
integer function foo()
foo = 10 + 5
return
end
"""
# use various helper functions in util.py to enable robust build /
# compile and reimport cycle in test suite
moddir = util.get_module_dir()
modname = util.get_temp_module_name()
cwd = os.getcwd()
target = os.path.join(moddir, str(uuid.uuid4()) + '.f')
# try running compile() with and without a source_fn provided so
# that the code path where a temporary file for writing Fortran
# source is created is also explored
for source_fn in [target, None]:
# mimic the path changing behavior used by build_module() in
# util.py, but don't actually use build_module() because it has
# its own invocation of subprocess that circumvents the
# f2py.compile code block under test
try:
os.chdir(moddir)
ret_val = numpy.f2py.compile(
fsource,
modulename=modname,
extra_args=extra_args,
source_fn=source_fn
)
finally:
os.chdir(cwd)
# check for compile success return value
assert_equal(ret_val, 0)
# we are not currently able to import the Python-Fortran
# interface module on Windows / Appveyor, even though we do get
# successful compilation on that platform with Python 3.x
if sys.platform != 'win32':
# check for sensible result of Fortran function; that means
# we can import the module name in Python and retrieve the
# result of the sum operation
return_check = import_module(modname)
calc_result = return_check.foo()
assert_equal(calc_result, 15)
# Removal from sys.modules, is not as such necessary. Even with
# removal, the module (dict) stays alive.
del sys.modules[modname]
def test_f2py_init_compile_failure():
# verify an appropriate integer status value returned by
# f2py.compile() when invalid Fortran is provided
ret_val = numpy.f2py.compile(b"invalid")
assert_equal(ret_val, 1)
def test_f2py_init_compile_bad_cmd():
# verify that usage of invalid command in f2py.compile() returns
# status value of 127 for historic consistency with exec_command()
# error handling
# patch the sys Python exe path temporarily to induce an OSError
# downstream NOTE: how bad of an idea is this patching?
try:
temp = sys.executable
sys.executable = 'does not exist'
# the OSError should take precedence over invalid Fortran
ret_val = numpy.f2py.compile(b"invalid")
assert_equal(ret_val, 127)
finally:
sys.executable = temp
@pytest.mark.parametrize('fsource',
['program test_f2py\nend program test_f2py',
b'program test_f2py\nend program test_f2py',])
def test_compile_from_strings(tmpdir, fsource):
# Make sure we can compile str and bytes gh-12796
cwd = os.getcwd()
try:
os.chdir(str(tmpdir))
ret_val = numpy.f2py.compile(
fsource,
modulename='test_compile_from_strings',
extension='.f90')
assert_equal(ret_val, 0)
finally:
os.chdir(cwd)

View File

@ -0,0 +1,288 @@
import pytest
import numpy as np
from numpy.testing import assert_array_equal, assert_equal
from numpy.f2py.crackfortran import markinnerspaces
from . import util
from numpy.f2py import crackfortran
import textwrap
class TestNoSpace(util.F2PyTest):
# issue gh-15035: add handling for endsubroutine, endfunction with no space
# between "end" and the block name
code = """
subroutine subb(k)
real(8), intent(inout) :: k(:)
k=k+1
endsubroutine
subroutine subc(w,k)
real(8), intent(in) :: w(:)
real(8), intent(out) :: k(size(w))
k=w+1
endsubroutine
function t0(value)
character value
character t0
t0 = value
endfunction
"""
def test_module(self):
k = np.array([1, 2, 3], dtype=np.float64)
w = np.array([1, 2, 3], dtype=np.float64)
self.module.subb(k)
assert_array_equal(k, w + 1)
self.module.subc([w, k])
assert_array_equal(k, w + 1)
assert self.module.t0(23) == b'2'
class TestPublicPrivate():
def test_defaultPrivate(self, tmp_path):
f_path = tmp_path / "mod.f90"
with f_path.open('w') as ff:
ff.write(textwrap.dedent("""\
module foo
private
integer :: a
public :: setA
integer :: b
contains
subroutine setA(v)
integer, intent(in) :: v
a = v
end subroutine setA
end module foo
"""))
mod = crackfortran.crackfortran([str(f_path)])
assert len(mod) == 1
mod = mod[0]
assert 'private' in mod['vars']['a']['attrspec']
assert 'public' not in mod['vars']['a']['attrspec']
assert 'private' in mod['vars']['b']['attrspec']
assert 'public' not in mod['vars']['b']['attrspec']
assert 'private' not in mod['vars']['seta']['attrspec']
assert 'public' in mod['vars']['seta']['attrspec']
def test_defaultPublic(self, tmp_path):
f_path = tmp_path / "mod.f90"
with f_path.open('w') as ff:
ff.write(textwrap.dedent("""\
module foo
public
integer, private :: a
public :: setA
contains
subroutine setA(v)
integer, intent(in) :: v
a = v
end subroutine setA
end module foo
"""))
mod = crackfortran.crackfortran([str(f_path)])
assert len(mod) == 1
mod = mod[0]
assert 'private' in mod['vars']['a']['attrspec']
assert 'public' not in mod['vars']['a']['attrspec']
assert 'private' not in mod['vars']['seta']['attrspec']
assert 'public' in mod['vars']['seta']['attrspec']
class TestExternal(util.F2PyTest):
# issue gh-17859: add external attribute support
code = """
integer(8) function external_as_statement(fcn)
implicit none
external fcn
integer(8) :: fcn
external_as_statement = fcn(0)
end
integer(8) function external_as_attribute(fcn)
implicit none
integer(8), external :: fcn
external_as_attribute = fcn(0)
end
"""
def test_external_as_statement(self):
def incr(x):
return x + 123
r = self.module.external_as_statement(incr)
assert r == 123
def test_external_as_attribute(self):
def incr(x):
return x + 123
r = self.module.external_as_attribute(incr)
assert r == 123
class TestCrackFortran(util.F2PyTest):
suffix = '.f90'
code = textwrap.dedent("""
subroutine gh2848( &
! first 2 parameters
par1, par2,&
! last 2 parameters
par3, par4)
integer, intent(in) :: par1, par2
integer, intent(out) :: par3, par4
par3 = par1
par4 = par2
end subroutine gh2848
""")
def test_gh2848(self):
r = self.module.gh2848(1, 2)
assert r == (1, 2)
class TestMarkinnerspaces():
# issue #14118: markinnerspaces does not handle multiple quotations
def test_do_not_touch_normal_spaces(self):
test_list = ["a ", " a", "a b c", "'abcdefghij'"]
for i in test_list:
assert_equal(markinnerspaces(i), i)
def test_one_relevant_space(self):
assert_equal(markinnerspaces("a 'b c' \\\' \\\'"), "a 'b@_@c' \\' \\'")
assert_equal(markinnerspaces(r'a "b c" \" \"'), r'a "b@_@c" \" \"')
def test_ignore_inner_quotes(self):
assert_equal(markinnerspaces('a \'b c" " d\' e'),
"a 'b@_@c\"@_@\"@_@d' e")
assert_equal(markinnerspaces('a "b c\' \' d" e'),
"a \"b@_@c'@_@'@_@d\" e")
def test_multiple_relevant_spaces(self):
assert_equal(markinnerspaces("a 'b c' 'd e'"), "a 'b@_@c' 'd@_@e'")
assert_equal(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.
There exists two usage cases where analyzing dimensions
specifications are important.
In the first case, the size of output arrays must be defined based
on the inputs to a Fortran function. Because Fortran supports
arbitrary bases for indexing, for instance, `arr(lower:upper)`,
f2py has to evaluate an expression `upper - lower + 1` where
`lower` and `upper` are arbitrary expressions of input parameters.
The evaluation is performed in C, so f2py has to translate Fortran
expressions to valid C expressions (an alternative approach is
that a developer specifies the corresponding C expressions in a
.pyf file).
In the second case, when user provides an input array with a given
size but some hidden parameters used in dimensions specifications
need to be determined based on the input array size. This is a
harder problem because f2py has to solve the inverse problem: find
a parameter `p` such that `upper(p) - lower(p) + 1` equals to the
size of input array. In the case when this equation cannot be
solved (e.g. because the input array size is wrong), raise an
error before calling the Fortran function (that otherwise would
likely crash Python process when the size of input arrays is
wrong). f2py currently supports this case only when the equation
is linear with respect to unknown parameter.
"""
suffix = '.f90'
code_template = textwrap.dedent("""
function get_arr_size_{count}(a, n) result (length)
integer, intent(in) :: n
integer, dimension({dimspec}), intent(out) :: a
integer length
length = size(a)
end function
subroutine get_inv_arr_size_{count}(a, n)
integer :: n
! the value of n is computed in f2py wrapper
!f2py intent(out) n
integer, dimension({dimspec}), intent(in) :: a
if (a({first}).gt.0) then
print*, "a=", a
endif
end subroutine
""")
linear_dimspecs = [
"n", "2*n", "2:n", "n/2", "5 - n/2", "3*n:20", "n*(n+1):n*(n+5)",
"2*n, n"
]
nonlinear_dimspecs = ["2*n:3*n*n+2*n"]
all_dimspecs = linear_dimspecs + nonlinear_dimspecs
code = ''
for count, dimspec in enumerate(all_dimspecs):
lst = [(d.split(":")[0] if ":" in d else "1") for d in dimspec.split(',')]
code += code_template.format(
count=count,
dimspec=dimspec,
first=", ".join(lst),
)
@pytest.mark.parametrize('dimspec', all_dimspecs)
def test_array_size(self, dimspec):
count = self.all_dimspecs.index(dimspec)
get_arr_size = getattr(self.module, f'get_arr_size_{count}')
for n in [1, 2, 3, 4, 5]:
sz, a = get_arr_size(n)
assert a.size == sz
@pytest.mark.parametrize('dimspec', all_dimspecs)
def test_inv_array_size(self, dimspec):
count = self.all_dimspecs.index(dimspec)
get_arr_size = getattr(self.module, f'get_arr_size_{count}')
get_inv_arr_size = getattr(self.module, f'get_inv_arr_size_{count}')
for n in [1, 2, 3, 4, 5]:
sz, a = get_arr_size(n)
if dimspec in self.nonlinear_dimspecs:
# one must specify n as input, the call we'll ensure
# that a and n are compatible:
n1 = get_inv_arr_size(a, n)
else:
# in case of linear dependence, n can be determined
# from the shape of a:
n1 = get_inv_arr_size(a)
# n1 may be different from n (for instance, when `a` size
# is a function of some `n` fraction) but it must produce
# the same sized array
sz1, _ = get_arr_size(n1)
assert sz == sz1, (n, n1, sz, sz1)
class TestModuleDeclaration():
def test_dependencies(self, tmp_path):
f_path = tmp_path / "mod.f90"
with f_path.open('w') as ff:
ff.write(textwrap.dedent("""\
module foo
type bar
character(len = 4) :: text
end type bar
type(bar), parameter :: abar = bar('abar')
end module foo
"""))
mod = crackfortran.crackfortran([str(f_path)])
assert len(mod) == 1
assert mod[0]['vars']['abar']['='] == "bar('abar')"

View File

@ -0,0 +1,32 @@
import os
import pytest
from numpy.testing import assert_
from numpy.f2py.crackfortran import (
_selected_int_kind_func as selected_int_kind,
_selected_real_kind_func as selected_real_kind
)
from . import util
def _path(*a):
return os.path.join(*((os.path.dirname(__file__),) + a))
class TestKind(util.F2PyTest):
sources = [_path('src', 'kind', 'foo.f90')]
@pytest.mark.slow
def test_all(self):
selectedrealkind = self.module.selectedrealkind
selectedintkind = self.module.selectedintkind
for i in range(40):
assert_(selectedintkind(i) in [selected_int_kind(i), -1],
'selectedintkind(%s): expected %r but got %r' %
(i, selected_int_kind(i), selectedintkind(i)))
for i in range(20):
assert_(selectedrealkind(i) in [selected_real_kind(i), -1],
'selectedrealkind(%s): expected %r but got %r' %
(i, selected_real_kind(i), selectedrealkind(i)))

View File

@ -0,0 +1,35 @@
import os
import textwrap
import pytest
from numpy.testing import assert_, assert_equal, IS_PYPY
from . import util
def _path(*a):
return os.path.join(*((os.path.dirname(__file__),) + a))
class TestMixed(util.F2PyTest):
sources = [_path('src', 'mixed', 'foo.f'),
_path('src', 'mixed', 'foo_fixed.f90'),
_path('src', 'mixed', 'foo_free.f90')]
def test_all(self):
assert_(self.module.bar11() == 11)
assert_(self.module.foo_fixed.bar12() == 12)
assert_(self.module.foo_free.bar13() == 13)
@pytest.mark.xfail(IS_PYPY,
reason="PyPy cannot modify tp_doc after PyType_Ready")
def test_docstring(self):
expected = textwrap.dedent("""\
a = bar11()
Wrapper for ``bar11``.
Returns
-------
a : int
""")
assert_equal(self.module.bar11.__doc__, expected)

View File

@ -0,0 +1,30 @@
import os
import sys
import pytest
import textwrap
from . import util
from numpy.testing import assert_equal, IS_PYPY
def _path(*a):
return os.path.join(*((os.path.dirname(__file__),) + a))
class TestModuleDocString(util.F2PyTest):
sources = [_path('src', 'module_data', 'module_data_docstring.f90')]
@pytest.mark.skipif(sys.platform=='win32',
reason='Fails with MinGW64 Gfortran (Issue #9673)')
@pytest.mark.xfail(IS_PYPY,
reason="PyPy cannot modify tp_doc after PyType_Ready")
def test_module_docstring(self):
assert_equal(self.module.mod.__doc__,
textwrap.dedent('''\
i : 'i'-scalar
x : 'i'-array(4)
a : 'f'-array(2,3)
b : 'f'-array(-1,-1), not allocated\x00
foo()\n
Wrapper for ``foo``.\n\n''')
)

View File

@ -0,0 +1,116 @@
import os
import pytest
import numpy as np
from numpy.testing import assert_raises, assert_equal
from . import util
def _path(*a):
return os.path.join(*((os.path.dirname(__file__),) + a))
class TestParameters(util.F2PyTest):
# Check that intent(in out) translates as intent(inout)
sources = [_path('src', 'parameter', 'constant_real.f90'),
_path('src', 'parameter', 'constant_integer.f90'),
_path('src', 'parameter', 'constant_both.f90'),
_path('src', 'parameter', 'constant_compound.f90'),
_path('src', 'parameter', 'constant_non_compound.f90'),
]
@pytest.mark.slow
def test_constant_real_single(self):
# non-contiguous should raise error
x = np.arange(6, dtype=np.float32)[::2]
assert_raises(ValueError, self.module.foo_single, x)
# check values with contiguous array
x = np.arange(3, dtype=np.float32)
self.module.foo_single(x)
assert_equal(x, [0 + 1 + 2*3, 1, 2])
@pytest.mark.slow
def test_constant_real_double(self):
# non-contiguous should raise error
x = np.arange(6, dtype=np.float64)[::2]
assert_raises(ValueError, self.module.foo_double, x)
# check values with contiguous array
x = np.arange(3, dtype=np.float64)
self.module.foo_double(x)
assert_equal(x, [0 + 1 + 2*3, 1, 2])
@pytest.mark.slow
def test_constant_compound_int(self):
# non-contiguous should raise error
x = np.arange(6, dtype=np.int32)[::2]
assert_raises(ValueError, self.module.foo_compound_int, x)
# check values with contiguous array
x = np.arange(3, dtype=np.int32)
self.module.foo_compound_int(x)
assert_equal(x, [0 + 1 + 2*6, 1, 2])
@pytest.mark.slow
def test_constant_non_compound_int(self):
# check values
x = np.arange(4, dtype=np.int32)
self.module.foo_non_compound_int(x)
assert_equal(x, [0 + 1 + 2 + 3*4, 1, 2, 3])
@pytest.mark.slow
def test_constant_integer_int(self):
# non-contiguous should raise error
x = np.arange(6, dtype=np.int32)[::2]
assert_raises(ValueError, self.module.foo_int, x)
# check values with contiguous array
x = np.arange(3, dtype=np.int32)
self.module.foo_int(x)
assert_equal(x, [0 + 1 + 2*3, 1, 2])
@pytest.mark.slow
def test_constant_integer_long(self):
# non-contiguous should raise error
x = np.arange(6, dtype=np.int64)[::2]
assert_raises(ValueError, self.module.foo_long, x)
# check values with contiguous array
x = np.arange(3, dtype=np.int64)
self.module.foo_long(x)
assert_equal(x, [0 + 1 + 2*3, 1, 2])
@pytest.mark.slow
def test_constant_both(self):
# non-contiguous should raise error
x = np.arange(6, dtype=np.float64)[::2]
assert_raises(ValueError, self.module.foo, x)
# check values with contiguous array
x = np.arange(3, dtype=np.float64)
self.module.foo(x)
assert_equal(x, [0 + 1*3*3 + 2*3*3, 1*3, 2*3])
@pytest.mark.slow
def test_constant_no(self):
# non-contiguous should raise error
x = np.arange(6, dtype=np.float64)[::2]
assert_raises(ValueError, self.module.foo_no, x)
# check values with contiguous array
x = np.arange(3, dtype=np.float64)
self.module.foo_no(x)
assert_equal(x, [0 + 1*3*3 + 2*3*3, 1*3, 2*3])
@pytest.mark.slow
def test_constant_sum(self):
# non-contiguous should raise error
x = np.arange(6, dtype=np.float64)[::2]
assert_raises(ValueError, self.module.foo_sum, x)
# check values with contiguous array
x = np.arange(3, dtype=np.float64)
self.module.foo_sum(x)
assert_equal(x, [0 + 1*3*3 + 2*3*3, 1*3, 2*3])

View File

@ -0,0 +1,32 @@
"""See https://github.com/numpy/numpy/pull/10676.
"""
import sys
import pytest
from numpy.testing import assert_equal
from . import util
class TestQuotedCharacter(util.F2PyTest):
code = """
SUBROUTINE FOO(OUT1, OUT2, OUT3, OUT4, OUT5, OUT6)
CHARACTER SINGLE, DOUBLE, SEMICOL, EXCLA, OPENPAR, CLOSEPAR
PARAMETER (SINGLE="'", DOUBLE='"', SEMICOL=';', EXCLA="!",
1 OPENPAR="(", CLOSEPAR=")")
CHARACTER OUT1, OUT2, OUT3, OUT4, OUT5, OUT6
Cf2py intent(out) OUT1, OUT2, OUT3, OUT4, OUT5, OUT6
OUT1 = SINGLE
OUT2 = DOUBLE
OUT3 = SEMICOL
OUT4 = EXCLA
OUT5 = OPENPAR
OUT6 = CLOSEPAR
RETURN
END
"""
@pytest.mark.skipif(sys.platform=='win32',
reason='Fails with MinGW64 Gfortran (Issue #9673)')
def test_quoted_character(self):
assert_equal(self.module.foo(), (b"'", b'"', b';', b'!', b'(', b')'))

View File

@ -0,0 +1,55 @@
import os
import pytest
import numpy as np
from numpy.testing import assert_, assert_raises, assert_equal, assert_string_equal
from . import util
def _path(*a):
return os.path.join(*((os.path.dirname(__file__),) + a))
class TestIntentInOut(util.F2PyTest):
# Check that intent(in out) translates as intent(inout)
sources = [_path('src', 'regression', 'inout.f90')]
@pytest.mark.slow
def test_inout(self):
# non-contiguous should raise error
x = np.arange(6, dtype=np.float32)[::2]
assert_raises(ValueError, self.module.foo, x)
# check values with contiguous array
x = np.arange(3, dtype=np.float32)
self.module.foo(x)
assert_equal(x, [3, 1, 2])
class TestNumpyVersionAttribute(util.F2PyTest):
# Check that th attribute __f2py_numpy_version__ is present
# in the compiled module and that has the value np.__version__.
sources = [_path('src', 'regression', 'inout.f90')]
@pytest.mark.slow
def test_numpy_version_attribute(self):
# Check that self.module has an attribute named "__f2py_numpy_version__"
assert_(hasattr(self.module, "__f2py_numpy_version__"),
msg="Fortran module does not have __f2py_numpy_version__")
# Check that the attribute __f2py_numpy_version__ is a string
assert_(isinstance(self.module.__f2py_numpy_version__, str),
msg="__f2py_numpy_version__ is not a string")
# Check that __f2py_numpy_version__ has the value numpy.__version__
assert_string_equal(np.__version__, self.module.__f2py_numpy_version__)
def test_include_path():
incdir = np.f2py.get_include()
fnames_in_dir = os.listdir(incdir)
for fname in ('fortranobject.c', 'fortranobject.h'):
assert fname in fnames_in_dir

View File

@ -0,0 +1,145 @@
import pytest
from numpy import array
from numpy.testing import assert_
from . import util
import platform
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')
r = t('ab')
assert_(r == b'a', repr(r))
r = t(array('ab'))
assert_(r == b'a', repr(r))
r = t(array(77, 'u1'))
assert_(r == b'M', repr(r))
#assert_(_raises(ValueError, t, array([77,87])))
#assert_(_raises(ValueError, t, array(77)))
elif tname in ['ts', 'ss']:
assert_(t(23) == b'23', repr(t(23)))
assert_(t('123456789abcdef') == b'123456789a')
elif tname in ['t5', 's5']:
assert_(t(23) == b'23', repr(t(23)))
assert_(t('ab') == b'ab', repr(t('ab')))
assert_(t('123456789abcdef') == b'12345')
else:
raise NotImplementedError
class TestF77ReturnCharacter(TestReturnCharacter):
code = """
function t0(value)
character value
character t0
t0 = value
end
function t1(value)
character*1 value
character*1 t1
t1 = value
end
function t5(value)
character*5 value
character*5 t5
t5 = value
end
function ts(value)
character*(*) value
character*(*) ts
ts = value
end
subroutine s0(t0,value)
character value
character t0
cf2py intent(out) t0
t0 = value
end
subroutine s1(t1,value)
character*1 value
character*1 t1
cf2py intent(out) t1
t1 = value
end
subroutine s5(t5,value)
character*5 value
character*5 t5
cf2py intent(out) t5
t5 = value
end
subroutine ss(ts,value)
character*(*) value
character*10 ts
cf2py intent(out) ts
ts = value
end
"""
@pytest.mark.xfail(IS_S390X, reason="callback returns ' '")
@pytest.mark.parametrize('name', 't0,t1,t5,s0,s1,s5,ss'.split(','))
def test_all(self, name):
self.check_function(getattr(self.module, name), name)
class TestF90ReturnCharacter(TestReturnCharacter):
suffix = ".f90"
code = """
module f90_return_char
contains
function t0(value)
character :: value
character :: t0
t0 = value
end function t0
function t1(value)
character(len=1) :: value
character(len=1) :: t1
t1 = value
end function t1
function t5(value)
character(len=5) :: value
character(len=5) :: t5
t5 = value
end function t5
function ts(value)
character(len=*) :: value
character(len=10) :: ts
ts = value
end function ts
subroutine s0(t0,value)
character :: value
character :: t0
!f2py intent(out) t0
t0 = value
end subroutine s0
subroutine s1(t1,value)
character(len=1) :: value
character(len=1) :: t1
!f2py intent(out) t1
t1 = value
end subroutine s1
subroutine s5(t5,value)
character(len=5) :: value
character(len=5) :: t5
!f2py intent(out) t5
t5 = value
end subroutine s5
subroutine ss(ts,value)
character(len=*) :: value
character(len=10) :: ts
!f2py intent(out) ts
ts = value
end subroutine ss
end module f90_return_char
"""
@pytest.mark.xfail(IS_S390X, reason="callback returns ' '")
@pytest.mark.parametrize('name', 't0,t1,t5,ts,s0,s1,s5,ss'.split(','))
def test_all(self, name):
self.check_function(getattr(self.module.f90_return_char, name), name)

View File

@ -0,0 +1,163 @@
import pytest
from numpy import array
from numpy.testing import assert_, assert_raises
from . import util
class TestReturnComplex(util.F2PyTest):
def check_function(self, t, tname):
if tname in ['t0', 't8', 's0', 's8']:
err = 1e-5
else:
err = 0.0
assert_(abs(t(234j) - 234.0j) <= err)
assert_(abs(t(234.6) - 234.6) <= err)
assert_(abs(t(234) - 234.0) <= err)
assert_(abs(t(234.6 + 3j) - (234.6 + 3j)) <= err)
#assert_( abs(t('234')-234.)<=err)
#assert_( abs(t('234.6')-234.6)<=err)
assert_(abs(t(-234) + 234.) <= err)
assert_(abs(t([234]) - 234.) <= err)
assert_(abs(t((234,)) - 234.) <= err)
assert_(abs(t(array(234)) - 234.) <= err)
assert_(abs(t(array(23 + 4j, 'F')) - (23 + 4j)) <= err)
assert_(abs(t(array([234])) - 234.) <= err)
assert_(abs(t(array([[234]])) - 234.) <= err)
assert_(abs(t(array([234], 'b')) + 22.) <= err)
assert_(abs(t(array([234], 'h')) - 234.) <= err)
assert_(abs(t(array([234], 'i')) - 234.) <= err)
assert_(abs(t(array([234], 'l')) - 234.) <= err)
assert_(abs(t(array([234], 'q')) - 234.) <= err)
assert_(abs(t(array([234], 'f')) - 234.) <= err)
assert_(abs(t(array([234], 'd')) - 234.) <= err)
assert_(abs(t(array([234 + 3j], 'F')) - (234 + 3j)) <= err)
assert_(abs(t(array([234], 'D')) - 234.) <= err)
#assert_raises(TypeError, t, array([234], 'a1'))
assert_raises(TypeError, t, 'abc')
assert_raises(IndexError, t, [])
assert_raises(IndexError, t, ())
assert_raises(TypeError, t, t)
assert_raises(TypeError, t, {})
try:
r = t(10 ** 400)
assert_(repr(r) in ['(inf+0j)', '(Infinity+0j)'], repr(r))
except OverflowError:
pass
class TestF77ReturnComplex(TestReturnComplex):
code = """
function t0(value)
complex value
complex t0
t0 = value
end
function t8(value)
complex*8 value
complex*8 t8
t8 = value
end
function t16(value)
complex*16 value
complex*16 t16
t16 = value
end
function td(value)
double complex value
double complex td
td = value
end
subroutine s0(t0,value)
complex value
complex t0
cf2py intent(out) t0
t0 = value
end
subroutine s8(t8,value)
complex*8 value
complex*8 t8
cf2py intent(out) t8
t8 = value
end
subroutine s16(t16,value)
complex*16 value
complex*16 t16
cf2py intent(out) t16
t16 = value
end
subroutine sd(td,value)
double complex value
double complex td
cf2py intent(out) td
td = value
end
"""
@pytest.mark.parametrize('name', 't0,t8,t16,td,s0,s8,s16,sd'.split(','))
def test_all(self, name):
self.check_function(getattr(self.module, name), name)
class TestF90ReturnComplex(TestReturnComplex):
suffix = ".f90"
code = """
module f90_return_complex
contains
function t0(value)
complex :: value
complex :: t0
t0 = value
end function t0
function t8(value)
complex(kind=4) :: value
complex(kind=4) :: t8
t8 = value
end function t8
function t16(value)
complex(kind=8) :: value
complex(kind=8) :: t16
t16 = value
end function t16
function td(value)
double complex :: value
double complex :: td
td = value
end function td
subroutine s0(t0,value)
complex :: value
complex :: t0
!f2py intent(out) t0
t0 = value
end subroutine s0
subroutine s8(t8,value)
complex(kind=4) :: value
complex(kind=4) :: t8
!f2py intent(out) t8
t8 = value
end subroutine s8
subroutine s16(t16,value)
complex(kind=8) :: value
complex(kind=8) :: t16
!f2py intent(out) t16
t16 = value
end subroutine s16
subroutine sd(td,value)
double complex :: value
double complex :: td
!f2py intent(out) td
td = value
end subroutine sd
end module f90_return_complex
"""
@pytest.mark.parametrize('name', 't0,t8,t16,td,s0,s8,s16,sd'.split(','))
def test_all(self, name):
self.check_function(getattr(self.module.f90_return_complex, name), name)

View File

@ -0,0 +1,175 @@
import pytest
from numpy import array
from numpy.testing import assert_, assert_raises
from . import util
class TestReturnInteger(util.F2PyTest):
def check_function(self, t, tname):
assert_(t(123) == 123, repr(t(123)))
assert_(t(123.6) == 123)
assert_(t('123') == 123)
assert_(t(-123) == -123)
assert_(t([123]) == 123)
assert_(t((123,)) == 123)
assert_(t(array(123)) == 123)
assert_(t(array([123])) == 123)
assert_(t(array([[123]])) == 123)
assert_(t(array([123], 'b')) == 123)
assert_(t(array([123], 'h')) == 123)
assert_(t(array([123], 'i')) == 123)
assert_(t(array([123], 'l')) == 123)
assert_(t(array([123], 'B')) == 123)
assert_(t(array([123], 'f')) == 123)
assert_(t(array([123], 'd')) == 123)
#assert_raises(ValueError, t, array([123],'S3'))
assert_raises(ValueError, t, 'abc')
assert_raises(IndexError, t, [])
assert_raises(IndexError, t, ())
assert_raises(Exception, t, t)
assert_raises(Exception, t, {})
if tname in ['t8', 's8']:
assert_raises(OverflowError, t, 100000000000000000000000)
assert_raises(OverflowError, t, 10000000011111111111111.23)
class TestF77ReturnInteger(TestReturnInteger):
code = """
function t0(value)
integer value
integer t0
t0 = value
end
function t1(value)
integer*1 value
integer*1 t1
t1 = value
end
function t2(value)
integer*2 value
integer*2 t2
t2 = value
end
function t4(value)
integer*4 value
integer*4 t4
t4 = value
end
function t8(value)
integer*8 value
integer*8 t8
t8 = value
end
subroutine s0(t0,value)
integer value
integer t0
cf2py intent(out) t0
t0 = value
end
subroutine s1(t1,value)
integer*1 value
integer*1 t1
cf2py intent(out) t1
t1 = value
end
subroutine s2(t2,value)
integer*2 value
integer*2 t2
cf2py intent(out) t2
t2 = value
end
subroutine s4(t4,value)
integer*4 value
integer*4 t4
cf2py intent(out) t4
t4 = value
end
subroutine s8(t8,value)
integer*8 value
integer*8 t8
cf2py intent(out) t8
t8 = value
end
"""
@pytest.mark.parametrize('name',
't0,t1,t2,t4,t8,s0,s1,s2,s4,s8'.split(','))
def test_all(self, name):
self.check_function(getattr(self.module, name), name)
class TestF90ReturnInteger(TestReturnInteger):
suffix = ".f90"
code = """
module f90_return_integer
contains
function t0(value)
integer :: value
integer :: t0
t0 = value
end function t0
function t1(value)
integer(kind=1) :: value
integer(kind=1) :: t1
t1 = value
end function t1
function t2(value)
integer(kind=2) :: value
integer(kind=2) :: t2
t2 = value
end function t2
function t4(value)
integer(kind=4) :: value
integer(kind=4) :: t4
t4 = value
end function t4
function t8(value)
integer(kind=8) :: value
integer(kind=8) :: t8
t8 = value
end function t8
subroutine s0(t0,value)
integer :: value
integer :: t0
!f2py intent(out) t0
t0 = value
end subroutine s0
subroutine s1(t1,value)
integer(kind=1) :: value
integer(kind=1) :: t1
!f2py intent(out) t1
t1 = value
end subroutine s1
subroutine s2(t2,value)
integer(kind=2) :: value
integer(kind=2) :: t2
!f2py intent(out) t2
t2 = value
end subroutine s2
subroutine s4(t4,value)
integer(kind=4) :: value
integer(kind=4) :: t4
!f2py intent(out) t4
t4 = value
end subroutine s4
subroutine s8(t8,value)
integer(kind=8) :: value
integer(kind=8) :: t8
!f2py intent(out) t8
t8 = value
end subroutine s8
end module f90_return_integer
"""
@pytest.mark.parametrize('name',
't0,t1,t2,t4,t8,s0,s1,s2,s4,s8'.split(','))
def test_all(self, name):
self.check_function(getattr(self.module.f90_return_integer, name), name)

View File

@ -0,0 +1,185 @@
import pytest
from numpy import array
from numpy.testing import assert_, assert_raises
from . import util
class TestReturnLogical(util.F2PyTest):
def check_function(self, t):
assert_(t(True) == 1, repr(t(True)))
assert_(t(False) == 0, repr(t(False)))
assert_(t(0) == 0)
assert_(t(None) == 0)
assert_(t(0.0) == 0)
assert_(t(0j) == 0)
assert_(t(1j) == 1)
assert_(t(234) == 1)
assert_(t(234.6) == 1)
assert_(t(234.6 + 3j) == 1)
assert_(t('234') == 1)
assert_(t('aaa') == 1)
assert_(t('') == 0)
assert_(t([]) == 0)
assert_(t(()) == 0)
assert_(t({}) == 0)
assert_(t(t) == 1)
assert_(t(-234) == 1)
assert_(t(10 ** 100) == 1)
assert_(t([234]) == 1)
assert_(t((234,)) == 1)
assert_(t(array(234)) == 1)
assert_(t(array([234])) == 1)
assert_(t(array([[234]])) == 1)
assert_(t(array([234], 'b')) == 1)
assert_(t(array([234], 'h')) == 1)
assert_(t(array([234], 'i')) == 1)
assert_(t(array([234], 'l')) == 1)
assert_(t(array([234], 'f')) == 1)
assert_(t(array([234], 'd')) == 1)
assert_(t(array([234 + 3j], 'F')) == 1)
assert_(t(array([234], 'D')) == 1)
assert_(t(array(0)) == 0)
assert_(t(array([0])) == 0)
assert_(t(array([[0]])) == 0)
assert_(t(array([0j])) == 0)
assert_(t(array([1])) == 1)
assert_raises(ValueError, t, array([0, 0]))
class TestF77ReturnLogical(TestReturnLogical):
code = """
function t0(value)
logical value
logical t0
t0 = value
end
function t1(value)
logical*1 value
logical*1 t1
t1 = value
end
function t2(value)
logical*2 value
logical*2 t2
t2 = value
end
function t4(value)
logical*4 value
logical*4 t4
t4 = value
end
c function t8(value)
c logical*8 value
c logical*8 t8
c t8 = value
c end
subroutine s0(t0,value)
logical value
logical t0
cf2py intent(out) t0
t0 = value
end
subroutine s1(t1,value)
logical*1 value
logical*1 t1
cf2py intent(out) t1
t1 = value
end
subroutine s2(t2,value)
logical*2 value
logical*2 t2
cf2py intent(out) t2
t2 = value
end
subroutine s4(t4,value)
logical*4 value
logical*4 t4
cf2py intent(out) t4
t4 = value
end
c subroutine s8(t8,value)
c logical*8 value
c logical*8 t8
cf2py intent(out) t8
c t8 = value
c end
"""
@pytest.mark.slow
@pytest.mark.parametrize('name', 't0,t1,t2,t4,s0,s1,s2,s4'.split(','))
def test_all(self, name):
self.check_function(getattr(self.module, name))
class TestF90ReturnLogical(TestReturnLogical):
suffix = ".f90"
code = """
module f90_return_logical
contains
function t0(value)
logical :: value
logical :: t0
t0 = value
end function t0
function t1(value)
logical(kind=1) :: value
logical(kind=1) :: t1
t1 = value
end function t1
function t2(value)
logical(kind=2) :: value
logical(kind=2) :: t2
t2 = value
end function t2
function t4(value)
logical(kind=4) :: value
logical(kind=4) :: t4
t4 = value
end function t4
function t8(value)
logical(kind=8) :: value
logical(kind=8) :: t8
t8 = value
end function t8
subroutine s0(t0,value)
logical :: value
logical :: t0
!f2py intent(out) t0
t0 = value
end subroutine s0
subroutine s1(t1,value)
logical(kind=1) :: value
logical(kind=1) :: t1
!f2py intent(out) t1
t1 = value
end subroutine s1
subroutine s2(t2,value)
logical(kind=2) :: value
logical(kind=2) :: t2
!f2py intent(out) t2
t2 = value
end subroutine s2
subroutine s4(t4,value)
logical(kind=4) :: value
logical(kind=4) :: t4
!f2py intent(out) t4
t4 = value
end subroutine s4
subroutine s8(t8,value)
logical(kind=8) :: value
logical(kind=8) :: t8
!f2py intent(out) t8
t8 = value
end subroutine s8
end module f90_return_logical
"""
@pytest.mark.slow
@pytest.mark.parametrize('name',
't0,t1,t2,t4,t8,s0,s1,s2,s4,s8'.split(','))
def test_all(self, name):
self.check_function(getattr(self.module.f90_return_logical, name))

View File

@ -0,0 +1,203 @@
import platform
import pytest
from numpy import array
from numpy.testing import assert_, assert_raises
from . import util
class TestReturnReal(util.F2PyTest):
def check_function(self, t, tname):
if tname in ['t0', 't4', 's0', 's4']:
err = 1e-5
else:
err = 0.0
assert_(abs(t(234) - 234.0) <= err)
assert_(abs(t(234.6) - 234.6) <= err)
assert_(abs(t('234') - 234) <= err)
assert_(abs(t('234.6') - 234.6) <= err)
assert_(abs(t(-234) + 234) <= err)
assert_(abs(t([234]) - 234) <= err)
assert_(abs(t((234,)) - 234.) <= err)
assert_(abs(t(array(234)) - 234.) <= err)
assert_(abs(t(array([234])) - 234.) <= err)
assert_(abs(t(array([[234]])) - 234.) <= err)
assert_(abs(t(array([234], 'b')) + 22) <= err)
assert_(abs(t(array([234], 'h')) - 234.) <= err)
assert_(abs(t(array([234], 'i')) - 234.) <= err)
assert_(abs(t(array([234], 'l')) - 234.) <= err)
assert_(abs(t(array([234], 'B')) - 234.) <= err)
assert_(abs(t(array([234], 'f')) - 234.) <= err)
assert_(abs(t(array([234], 'd')) - 234.) <= err)
if tname in ['t0', 't4', 's0', 's4']:
assert_(t(1e200) == t(1e300)) # inf
#assert_raises(ValueError, t, array([234], 'S1'))
assert_raises(ValueError, t, 'abc')
assert_raises(IndexError, t, [])
assert_raises(IndexError, t, ())
assert_raises(Exception, t, t)
assert_raises(Exception, t, {})
try:
r = t(10 ** 400)
assert_(repr(r) in ['inf', 'Infinity'], repr(r))
except OverflowError:
pass
@pytest.mark.skipif(
platform.system() == 'Darwin',
reason="Prone to error when run with numpy/f2py/tests on mac os, "
"but not when run in isolation")
class TestCReturnReal(TestReturnReal):
suffix = ".pyf"
module_name = "c_ext_return_real"
code = """
python module c_ext_return_real
usercode \'\'\'
float t4(float value) { return value; }
void s4(float *t4, float value) { *t4 = value; }
double t8(double value) { return value; }
void s8(double *t8, double value) { *t8 = value; }
\'\'\'
interface
function t4(value)
real*4 intent(c) :: t4,value
end
function t8(value)
real*8 intent(c) :: t8,value
end
subroutine s4(t4,value)
intent(c) s4
real*4 intent(out) :: t4
real*4 intent(c) :: value
end
subroutine s8(t8,value)
intent(c) s8
real*8 intent(out) :: t8
real*8 intent(c) :: value
end
end interface
end python module c_ext_return_real
"""
@pytest.mark.parametrize('name', 't4,t8,s4,s8'.split(','))
def test_all(self, name):
self.check_function(getattr(self.module, name), name)
class TestF77ReturnReal(TestReturnReal):
code = """
function t0(value)
real value
real t0
t0 = value
end
function t4(value)
real*4 value
real*4 t4
t4 = value
end
function t8(value)
real*8 value
real*8 t8
t8 = value
end
function td(value)
double precision value
double precision td
td = value
end
subroutine s0(t0,value)
real value
real t0
cf2py intent(out) t0
t0 = value
end
subroutine s4(t4,value)
real*4 value
real*4 t4
cf2py intent(out) t4
t4 = value
end
subroutine s8(t8,value)
real*8 value
real*8 t8
cf2py intent(out) t8
t8 = value
end
subroutine sd(td,value)
double precision value
double precision td
cf2py intent(out) td
td = value
end
"""
@pytest.mark.parametrize('name', 't0,t4,t8,td,s0,s4,s8,sd'.split(','))
def test_all(self, name):
self.check_function(getattr(self.module, name), name)
class TestF90ReturnReal(TestReturnReal):
suffix = ".f90"
code = """
module f90_return_real
contains
function t0(value)
real :: value
real :: t0
t0 = value
end function t0
function t4(value)
real(kind=4) :: value
real(kind=4) :: t4
t4 = value
end function t4
function t8(value)
real(kind=8) :: value
real(kind=8) :: t8
t8 = value
end function t8
function td(value)
double precision :: value
double precision :: td
td = value
end function td
subroutine s0(t0,value)
real :: value
real :: t0
!f2py intent(out) t0
t0 = value
end subroutine s0
subroutine s4(t4,value)
real(kind=4) :: value
real(kind=4) :: t4
!f2py intent(out) t4
t4 = value
end subroutine s4
subroutine s8(t8,value)
real(kind=8) :: value
real(kind=8) :: t8
!f2py intent(out) t8
t8 = value
end subroutine s8
subroutine sd(td,value)
double precision :: value
double precision :: td
!f2py intent(out) td
td = value
end subroutine sd
end module f90_return_real
"""
@pytest.mark.parametrize('name', 't0,t4,t8,td,s0,s4,s8,sd'.split(','))
def test_all(self, name):
self.check_function(getattr(self.module.f90_return_real, name), name)

View File

@ -0,0 +1,63 @@
import platform
import pytest
from . import util
from numpy.testing import assert_equal
@pytest.mark.skipif(
platform.system() == 'Darwin',
reason="Prone to error when run with numpy/f2py/tests on mac os, "
"but not when run in isolation")
class TestMultiline(util.F2PyTest):
suffix = ".pyf"
module_name = "multiline"
code = """
python module {module}
usercode '''
void foo(int* x) {{
char dummy = ';';
*x = 42;
}}
'''
interface
subroutine foo(x)
intent(c) foo
integer intent(out) :: x
end subroutine foo
end interface
end python module {module}
""".format(module=module_name)
def test_multiline(self):
assert_equal(self.module.foo(), 42)
@pytest.mark.skipif(
platform.system() == 'Darwin',
reason="Prone to error when run with numpy/f2py/tests on mac os, "
"but not when run in isolation")
class TestCallstatement(util.F2PyTest):
suffix = ".pyf"
module_name = "callstatement"
code = """
python module {module}
usercode '''
void foo(int* x) {{
}}
'''
interface
subroutine foo(x)
intent(c) foo
integer intent(out) :: x
callprotoargument int*
callstatement {{ &
; &
x = 42; &
}}
end subroutine foo
end interface
end python module {module}
""".format(module=module_name)
def test_callstatement(self):
assert_equal(self.module.foo(), 42)

View File

@ -0,0 +1,49 @@
import os
import pytest
from numpy.testing import assert_equal
from . import util
def _path(*a):
return os.path.join(*((os.path.dirname(__file__),) + a))
class TestSizeSumExample(util.F2PyTest):
sources = [_path('src', 'size', 'foo.f90')]
@pytest.mark.slow
def test_all(self):
r = self.module.foo([[]])
assert_equal(r, [0], repr(r))
r = self.module.foo([[1, 2]])
assert_equal(r, [3], repr(r))
r = self.module.foo([[1, 2], [3, 4]])
assert_equal(r, [3, 7], repr(r))
r = self.module.foo([[1, 2], [3, 4], [5, 6]])
assert_equal(r, [3, 7, 11], repr(r))
@pytest.mark.slow
def test_transpose(self):
r = self.module.trans([[]])
assert_equal(r.T, [[]], repr(r))
r = self.module.trans([[1, 2]])
assert_equal(r, [[1], [2]], repr(r))
r = self.module.trans([[1, 2, 3], [4, 5, 6]])
assert_equal(r, [[1, 4], [2, 5], [3, 6]], repr(r))
@pytest.mark.slow
def test_flatten(self):
r = self.module.flatten([[]])
assert_equal(r, [], repr(r))
r = self.module.flatten([[1, 2]])
assert_equal(r, [1, 2], repr(r))
r = self.module.flatten([[1, 2, 3], [4, 5, 6]])
assert_equal(r, [1, 2, 3, 4, 5, 6], repr(r))

View File

@ -0,0 +1,166 @@
import os
import pytest
import textwrap
from numpy.testing import assert_array_equal
import numpy as np
from . import util
def _path(*a):
return os.path.join(*((os.path.dirname(__file__),) + a))
class TestString(util.F2PyTest):
sources = [_path('src', 'string', 'char.f90')]
@pytest.mark.slow
def test_char(self):
strings = np.array(['ab', 'cd', 'ef'], dtype='c').T
inp, out = self.module.char_test.change_strings(strings,
strings.shape[1])
assert_array_equal(inp, strings)
expected = strings.copy()
expected[1, :] = 'AAA'
assert_array_equal(out, expected)
class TestDocStringArguments(util.F2PyTest):
suffix = '.f'
code = """
C FILE: STRING.F
SUBROUTINE FOO(A,B,C,D)
CHARACTER*5 A, B
CHARACTER*(*) C,D
Cf2py intent(in) a,c
Cf2py intent(inout) b,d
PRINT*, "A=",A
PRINT*, "B=",B
PRINT*, "C=",C
PRINT*, "D=",D
PRINT*, "CHANGE A,B,C,D"
A(1:1) = 'A'
B(1:1) = 'B'
C(1:1) = 'C'
D(1:1) = 'D'
PRINT*, "A=",A
PRINT*, "B=",B
PRINT*, "C=",C
PRINT*, "D=",D
END
C END OF FILE STRING.F
"""
def test_example(self):
a = np.array(b'123\0\0')
b = np.array(b'123\0\0')
c = np.array(b'123')
d = np.array(b'123')
self.module.foo(a, b, c, d)
assert a.tobytes() == b'123\0\0'
assert b.tobytes() == b'B23\0\0', (b.tobytes(),)
assert c.tobytes() == b'123'
assert d.tobytes() == b'D23'
class TestFixedString(util.F2PyTest):
suffix = '.f90'
code = textwrap.dedent("""
function sint(s) result(i)
implicit none
character(len=*) :: s
integer :: j, i
i = 0
do j=len(s), 1, -1
if (.not.((i.eq.0).and.(s(j:j).eq.' '))) then
i = i + ichar(s(j:j)) * 10 ** (j - 1)
endif
end do
return
end function sint
function test_in_bytes4(a) result (i)
implicit none
integer :: sint
character(len=4) :: a
integer :: i
i = sint(a)
a(1:1) = 'A'
return
end function test_in_bytes4
function test_inout_bytes4(a) result (i)
implicit none
integer :: sint
character(len=4), intent(inout) :: a
integer :: i
if (a(1:1).ne.' ') then
a(1:1) = 'E'
endif
i = sint(a)
return
end function test_inout_bytes4
""")
@staticmethod
def _sint(s, start=0, end=None):
"""Return the content of a string buffer as integer value.
For example:
_sint('1234') -> 4321
_sint('123A') -> 17321
"""
if isinstance(s, np.ndarray):
s = s.tobytes()
elif isinstance(s, str):
s = s.encode()
assert isinstance(s, bytes)
if end is None:
end = len(s)
i = 0
for j in range(start, min(end, len(s))):
i += s[j] * 10 ** j
return i
def _get_input(self, intent='in'):
if intent in ['in']:
yield ''
yield '1'
yield '1234'
yield '12345'
yield b''
yield b'\0'
yield b'1'
yield b'\01'
yield b'1\0'
yield b'1234'
yield b'12345'
yield np.ndarray((), np.bytes_, buffer=b'') # array(b'', dtype='|S0')
yield np.array(b'') # array(b'', dtype='|S1')
yield np.array(b'\0')
yield np.array(b'1')
yield np.array(b'1\0')
yield np.array(b'\01')
yield np.array(b'1234')
yield np.array(b'123\0')
yield np.array(b'12345')
def test_intent_in(self):
for s in self._get_input():
r = self.module.test_in_bytes4(s)
# also checks that s is not changed inplace
expected = self._sint(s, end=4)
assert r == expected, (s)
def test_intent_inout(self):
for s in self._get_input(intent='inout'):
rest = self._sint(s, start=4)
r = self.module.test_inout_bytes4(s)
expected = self._sint(s, end=4)
assert r == expected
# check that the rest of input string is preserved
assert rest == self._sint(s, start=4)

View File

@ -0,0 +1,462 @@
from numpy.testing import assert_raises
from numpy.f2py.symbolic import (
Expr, Op, ArithOp, Language,
as_symbol, as_number, as_string, as_array, as_complex,
as_terms, as_factors, eliminate_quotes, insert_quotes,
fromstring, as_expr, as_apply,
as_numer_denom, as_ternary, as_ref, as_deref,
normalize, as_eq, as_ne, as_lt, as_gt, as_le, as_ge
)
from . import util
class TestSymbolic(util.F2PyTest):
def test_eliminate_quotes(self):
def worker(s):
r, d = eliminate_quotes(s)
s1 = insert_quotes(r, d)
assert s1 == s
for kind in ['', 'mykind_']:
worker(kind + '"1234" // "ABCD"')
worker(kind + '"1234" // ' + kind + '"ABCD"')
worker(kind + '"1234" // \'ABCD\'')
worker(kind + '"1234" // ' + kind + '\'ABCD\'')
worker(kind + '"1\\"2\'AB\'34"')
worker('a = ' + kind + "'1\\'2\"AB\"34'")
def test_sanity(self):
x = as_symbol('x')
y = as_symbol('y')
z = as_symbol('z')
assert x.op == Op.SYMBOL
assert repr(x) == "Expr(Op.SYMBOL, 'x')"
assert x == x
assert x != y
assert hash(x) is not None
n = as_number(123)
m = as_number(456)
assert n.op == Op.INTEGER
assert repr(n) == "Expr(Op.INTEGER, (123, 4))"
assert n == n
assert n != m
assert hash(n) is not None
fn = as_number(12.3)
fm = as_number(45.6)
assert fn.op == Op.REAL
assert repr(fn) == "Expr(Op.REAL, (12.3, 4))"
assert fn == fn
assert fn != fm
assert hash(fn) is not None
c = as_complex(1, 2)
c2 = as_complex(3, 4)
assert c.op == Op.COMPLEX
assert repr(c) == ("Expr(Op.COMPLEX, (Expr(Op.INTEGER, (1, 4)),"
" Expr(Op.INTEGER, (2, 4))))")
assert c == c
assert c != c2
assert hash(c) is not None
s = as_string("'123'")
s2 = as_string('"ABC"')
assert s.op == Op.STRING
assert repr(s) == "Expr(Op.STRING, (\"'123'\", 1))", repr(s)
assert s == s
assert s != s2
a = as_array((n, m))
b = as_array((n,))
assert a.op == Op.ARRAY
assert repr(a) == ("Expr(Op.ARRAY, (Expr(Op.INTEGER, (123, 4)),"
" Expr(Op.INTEGER, (456, 4))))")
assert a == a
assert a != b
t = as_terms(x)
u = as_terms(y)
assert t.op == Op.TERMS
assert repr(t) == "Expr(Op.TERMS, {Expr(Op.SYMBOL, 'x'): 1})"
assert t == t
assert t != u
assert hash(t) is not None
v = as_factors(x)
w = as_factors(y)
assert v.op == Op.FACTORS
assert repr(v) == "Expr(Op.FACTORS, {Expr(Op.SYMBOL, 'x'): 1})"
assert v == v
assert w != v
assert hash(v) is not None
t = as_ternary(x, y, z)
u = as_ternary(x, z, y)
assert t.op == Op.TERNARY
assert t == t
assert t != u
assert hash(t) is not None
e = as_eq(x, y)
f = as_lt(x, y)
assert e.op == Op.RELATIONAL
assert e == e
assert e != f
assert hash(e) is not None
def test_tostring_fortran(self):
x = as_symbol('x')
y = as_symbol('y')
z = as_symbol('z')
n = as_number(123)
m = as_number(456)
a = as_array((n, m))
c = as_complex(n, m)
assert str(x) == 'x'
assert str(n) == '123'
assert str(a) == '[123, 456]'
assert str(c) == '(123, 456)'
assert str(Expr(Op.TERMS, {x: 1})) == 'x'
assert str(Expr(Op.TERMS, {x: 2})) == '2 * x'
assert str(Expr(Op.TERMS, {x: -1})) == '-x'
assert str(Expr(Op.TERMS, {x: -2})) == '-2 * x'
assert str(Expr(Op.TERMS, {x: 1, y: 1})) == 'x + y'
assert str(Expr(Op.TERMS, {x: -1, y: -1})) == '-x - y'
assert str(Expr(Op.TERMS, {x: 2, y: 3})) == '2 * x + 3 * y'
assert str(Expr(Op.TERMS, {x: -2, y: 3})) == '-2 * x + 3 * y'
assert str(Expr(Op.TERMS, {x: 2, y: -3})) == '2 * x - 3 * y'
assert str(Expr(Op.FACTORS, {x: 1})) == 'x'
assert str(Expr(Op.FACTORS, {x: 2})) == 'x ** 2'
assert str(Expr(Op.FACTORS, {x: -1})) == 'x ** -1'
assert str(Expr(Op.FACTORS, {x: -2})) == 'x ** -2'
assert str(Expr(Op.FACTORS, {x: 1, y: 1})) == 'x * y'
assert str(Expr(Op.FACTORS, {x: 2, y: 3})) == 'x ** 2 * y ** 3'
v = Expr(Op.FACTORS, {x: 2, Expr(Op.TERMS, {x: 1, y: 1}): 3})
assert str(v) == 'x ** 2 * (x + y) ** 3', str(v)
v = Expr(Op.FACTORS, {x: 2, Expr(Op.FACTORS, {x: 1, y: 1}): 3})
assert str(v) == 'x ** 2 * (x * y) ** 3', str(v)
assert str(Expr(Op.APPLY, ('f', (), {}))) == 'f()'
assert str(Expr(Op.APPLY, ('f', (x,), {}))) == 'f(x)'
assert str(Expr(Op.APPLY, ('f', (x, y), {}))) == 'f(x, y)'
assert str(Expr(Op.INDEXING, ('f', x))) == 'f[x]'
assert str(as_ternary(x, y, z)) == 'merge(y, z, x)'
assert str(as_eq(x, y)) == 'x .eq. y'
assert str(as_ne(x, y)) == 'x .ne. y'
assert str(as_lt(x, y)) == 'x .lt. y'
assert str(as_le(x, y)) == 'x .le. y'
assert str(as_gt(x, y)) == 'x .gt. y'
assert str(as_ge(x, y)) == 'x .ge. y'
def test_tostring_c(self):
language = Language.C
x = as_symbol('x')
y = as_symbol('y')
z = as_symbol('z')
n = as_number(123)
assert Expr(Op.FACTORS, {x: 2}).tostring(language=language) == 'x * x'
assert Expr(Op.FACTORS, {x + y: 2}).tostring(
language=language) == '(x + y) * (x + y)'
assert Expr(Op.FACTORS, {x: 12}).tostring(
language=language) == 'pow(x, 12)'
assert as_apply(ArithOp.DIV, x, y).tostring(
language=language) == 'x / y'
assert as_apply(ArithOp.DIV, x, x + y).tostring(
language=language) == 'x / (x + y)'
assert as_apply(ArithOp.DIV, x - y, x + y).tostring(
language=language) == '(x - y) / (x + y)'
assert (x + (x - y) / (x + y) + n).tostring(
language=language) == '123 + x + (x - y) / (x + y)'
assert as_ternary(x, y, z).tostring(language=language) == "(x?y:z)"
assert as_eq(x, y).tostring(language=language) == "x == y"
assert as_ne(x, y).tostring(language=language) == "x != y"
assert as_lt(x, y).tostring(language=language) == "x < y"
assert as_le(x, y).tostring(language=language) == "x <= y"
assert as_gt(x, y).tostring(language=language) == "x > y"
assert as_ge(x, y).tostring(language=language) == "x >= y"
def test_operations(self):
x = as_symbol('x')
y = as_symbol('y')
z = as_symbol('z')
assert x + x == Expr(Op.TERMS, {x: 2})
assert x - x == Expr(Op.INTEGER, (0, 4))
assert x + y == Expr(Op.TERMS, {x: 1, y: 1})
assert x - y == Expr(Op.TERMS, {x: 1, y: -1})
assert x * x == Expr(Op.FACTORS, {x: 2})
assert x * y == Expr(Op.FACTORS, {x: 1, y: 1})
assert +x == x
assert -x == Expr(Op.TERMS, {x: -1}), repr(-x)
assert 2 * x == Expr(Op.TERMS, {x: 2})
assert 2 + x == Expr(Op.TERMS, {x: 1, as_number(1): 2})
assert 2 * x + 3 * y == Expr(Op.TERMS, {x: 2, y: 3})
assert (x + y) * 2 == Expr(Op.TERMS, {x: 2, y: 2})
assert x ** 2 == Expr(Op.FACTORS, {x: 2})
assert (x + y) ** 2 == Expr(Op.TERMS,
{Expr(Op.FACTORS, {x: 2}): 1,
Expr(Op.FACTORS, {y: 2}): 1,
Expr(Op.FACTORS, {x: 1, y: 1}): 2})
assert (x + y) * x == x ** 2 + x * y
assert (x + y) ** 2 == x ** 2 + 2 * x * y + y ** 2
assert (x + y) ** 2 + (x - y) ** 2 == 2 * x ** 2 + 2 * y ** 2
assert (x + y) * z == x * z + y * z
assert z * (x + y) == x * z + y * z
assert (x / 2) == as_apply(ArithOp.DIV, x, as_number(2))
assert (2 * x / 2) == x
assert (3 * x / 2) == as_apply(ArithOp.DIV, 3*x, as_number(2))
assert (4 * x / 2) == 2 * x
assert (5 * x / 2) == as_apply(ArithOp.DIV, 5*x, as_number(2))
assert (6 * x / 2) == 3 * x
assert ((3*5) * x / 6) == as_apply(ArithOp.DIV, 5*x, as_number(2))
assert (30*x**2*y**4 / (24*x**3*y**3)) == as_apply(ArithOp.DIV,
5*y, 4*x)
assert ((15 * x / 6) / 5) == as_apply(
ArithOp.DIV, x, as_number(2)), ((15 * x / 6) / 5)
assert (x / (5 / x)) == as_apply(ArithOp.DIV, x**2, as_number(5))
assert (x / 2.0) == Expr(Op.TERMS, {x: 0.5})
s = as_string('"ABC"')
t = as_string('"123"')
assert s // t == Expr(Op.STRING, ('"ABC123"', 1))
assert s // x == Expr(Op.CONCAT, (s, x))
assert x // s == Expr(Op.CONCAT, (x, s))
c = as_complex(1., 2.)
assert -c == as_complex(-1., -2.)
assert c + c == as_expr((1+2j)*2)
assert c * c == as_expr((1+2j)**2)
def test_substitute(self):
x = as_symbol('x')
y = as_symbol('y')
z = as_symbol('z')
a = as_array((x, y))
assert x.substitute({x: y}) == y
assert (x + y).substitute({x: z}) == y + z
assert (x * y).substitute({x: z}) == y * z
assert (x ** 4).substitute({x: z}) == z ** 4
assert (x / y).substitute({x: z}) == z / y
assert x.substitute({x: y + z}) == y + z
assert a.substitute({x: y + z}) == as_array((y + z, y))
assert as_ternary(x, y, z).substitute(
{x: y + z}) == as_ternary(y + z, y, z)
assert as_eq(x, y).substitute(
{x: y + z}) == as_eq(y + z, y)
def test_fromstring(self):
x = as_symbol('x')
y = as_symbol('y')
z = as_symbol('z')
f = as_symbol('f')
s = as_string('"ABC"')
t = as_string('"123"')
a = as_array((x, y))
assert fromstring('x') == x
assert fromstring('+ x') == x
assert fromstring('- x') == -x
assert fromstring('x + y') == x + y
assert fromstring('x + 1') == x + 1
assert fromstring('x * y') == x * y
assert fromstring('x * 2') == x * 2
assert fromstring('x / y') == x / y
assert fromstring('x ** 2',
language=Language.Python) == x ** 2
assert fromstring('x ** 2 ** 3',
language=Language.Python) == x ** 2 ** 3
assert fromstring('(x + y) * z') == (x + y) * z
assert fromstring('f(x)') == f(x)
assert fromstring('f(x,y)') == f(x, y)
assert fromstring('f[x]') == f[x]
assert fromstring('f[x][y]') == f[x][y]
assert fromstring('"ABC"') == s
assert normalize(fromstring('"ABC" // "123" ',
language=Language.Fortran)) == s // t
assert fromstring('f("ABC")') == f(s)
assert fromstring('MYSTRKIND_"ABC"') == as_string('"ABC"', 'MYSTRKIND')
assert fromstring('(/x, y/)') == a, fromstring('(/x, y/)')
assert fromstring('f((/x, y/))') == f(a)
assert fromstring('(/(x+y)*z/)') == as_array(((x+y)*z,))
assert fromstring('123') == as_number(123)
assert fromstring('123_2') == as_number(123, 2)
assert fromstring('123_myintkind') == as_number(123, 'myintkind')
assert fromstring('123.0') == as_number(123.0, 4)
assert fromstring('123.0_4') == as_number(123.0, 4)
assert fromstring('123.0_8') == as_number(123.0, 8)
assert fromstring('123.0e0') == as_number(123.0, 4)
assert fromstring('123.0d0') == as_number(123.0, 8)
assert fromstring('123d0') == as_number(123.0, 8)
assert fromstring('123e-0') == as_number(123.0, 4)
assert fromstring('123d+0') == as_number(123.0, 8)
assert fromstring('123.0_myrealkind') == as_number(123.0, 'myrealkind')
assert fromstring('3E4') == as_number(30000.0, 4)
assert fromstring('(1, 2)') == as_complex(1, 2)
assert fromstring('(1e2, PI)') == as_complex(
as_number(100.0), as_symbol('PI'))
assert fromstring('[1, 2]') == as_array((as_number(1), as_number(2)))
assert fromstring('POINT(x, y=1)') == as_apply(
as_symbol('POINT'), x, y=as_number(1))
assert (fromstring('PERSON(name="John", age=50, shape=(/34, 23/))')
== as_apply(as_symbol('PERSON'),
name=as_string('"John"'),
age=as_number(50),
shape=as_array((as_number(34), as_number(23)))))
assert fromstring('x?y:z') == as_ternary(x, y, z)
assert fromstring('*x') == as_deref(x)
assert fromstring('**x') == as_deref(as_deref(x))
assert fromstring('&x') == as_ref(x)
assert fromstring('(*x) * (*y)') == as_deref(x) * as_deref(y)
assert fromstring('(*x) * *y') == as_deref(x) * as_deref(y)
assert fromstring('*x * *y') == as_deref(x) * as_deref(y)
assert fromstring('*x**y') == as_deref(x) * as_deref(y)
assert fromstring('x == y') == as_eq(x, y)
assert fromstring('x != y') == as_ne(x, y)
assert fromstring('x < y') == as_lt(x, y)
assert fromstring('x > y') == as_gt(x, y)
assert fromstring('x <= y') == as_le(x, y)
assert fromstring('x >= y') == as_ge(x, y)
assert fromstring('x .eq. y', language=Language.Fortran) == as_eq(x, y)
assert fromstring('x .ne. y', language=Language.Fortran) == as_ne(x, y)
assert fromstring('x .lt. y', language=Language.Fortran) == as_lt(x, y)
assert fromstring('x .gt. y', language=Language.Fortran) == as_gt(x, y)
assert fromstring('x .le. y', language=Language.Fortran) == as_le(x, y)
assert fromstring('x .ge. y', language=Language.Fortran) == as_ge(x, y)
def test_traverse(self):
x = as_symbol('x')
y = as_symbol('y')
z = as_symbol('z')
f = as_symbol('f')
# Use traverse to substitute a symbol
def replace_visit(s, r=z):
if s == x:
return r
assert x.traverse(replace_visit) == z
assert y.traverse(replace_visit) == y
assert z.traverse(replace_visit) == z
assert (f(y)).traverse(replace_visit) == f(y)
assert (f(x)).traverse(replace_visit) == f(z)
assert (f[y]).traverse(replace_visit) == f[y]
assert (f[z]).traverse(replace_visit) == f[z]
assert (x + y + z).traverse(replace_visit) == (2 * z + y)
assert (x + f(y, x - z)).traverse(
replace_visit) == (z + f(y, as_number(0)))
assert as_eq(x, y).traverse(replace_visit) == as_eq(z, y)
# Use traverse to collect symbols, method 1
function_symbols = set()
symbols = set()
def collect_symbols(s):
if s.op is Op.APPLY:
oper = s.data[0]
function_symbols.add(oper)
if oper in symbols:
symbols.remove(oper)
elif s.op is Op.SYMBOL and s not in function_symbols:
symbols.add(s)
(x + f(y, x - z)).traverse(collect_symbols)
assert function_symbols == {f}
assert symbols == {x, y, z}
# Use traverse to collect symbols, method 2
def collect_symbols2(expr, symbols):
if expr.op is Op.SYMBOL:
symbols.add(expr)
symbols = set()
(x + f(y, x - z)).traverse(collect_symbols2, symbols)
assert symbols == {x, y, z, f}
# Use traverse to partially collect symbols
def collect_symbols3(expr, symbols):
if expr.op is Op.APPLY:
# skip traversing function calls
return expr
if expr.op is Op.SYMBOL:
symbols.add(expr)
symbols = set()
(x + f(y, x - z)).traverse(collect_symbols3, symbols)
assert symbols == {x}
def test_linear_solve(self):
x = as_symbol('x')
y = as_symbol('y')
z = as_symbol('z')
assert x.linear_solve(x) == (as_number(1), as_number(0))
assert (x+1).linear_solve(x) == (as_number(1), as_number(1))
assert (2*x).linear_solve(x) == (as_number(2), as_number(0))
assert (2*x+3).linear_solve(x) == (as_number(2), as_number(3))
assert as_number(3).linear_solve(x) == (as_number(0), as_number(3))
assert y.linear_solve(x) == (as_number(0), y)
assert (y*z).linear_solve(x) == (as_number(0), y * z)
assert (x+y).linear_solve(x) == (as_number(1), y)
assert (z*x+y).linear_solve(x) == (z, y)
assert ((z+y)*x+y).linear_solve(x) == (z + y, y)
assert (z*y*x+y).linear_solve(x) == (z * y, y)
assert_raises(RuntimeError, lambda: (x*x).linear_solve(x))
def test_as_numer_denom(self):
x = as_symbol('x')
y = as_symbol('y')
n = as_number(123)
assert as_numer_denom(x) == (x, as_number(1))
assert as_numer_denom(x / n) == (x, n)
assert as_numer_denom(n / x) == (n, x)
assert as_numer_denom(x / y) == (x, y)
assert as_numer_denom(x * y) == (x * y, as_number(1))
assert as_numer_denom(n + x / y) == (x + n * y, y)
assert as_numer_denom(n + x / (y - x / n)) == (y * n ** 2, y * n - x)
def test_polynomial_atoms(self):
x = as_symbol('x')
y = as_symbol('y')
n = as_number(123)
assert x.polynomial_atoms() == {x}
assert n.polynomial_atoms() == set()
assert (y[x]).polynomial_atoms() == {y[x]}
assert (y(x)).polynomial_atoms() == {y(x)}
assert (y(x) + x).polynomial_atoms() == {y(x), x}
assert (y(x) * x[y]).polynomial_atoms() == {y(x), x[y]}
assert (y(x) ** x).polynomial_atoms() == {y(x)}

View File

@ -0,0 +1,356 @@
"""
Utility functions for
- building and importing modules on test time, using a temporary location
- detecting if compilers are present
"""
import os
import sys
import subprocess
import tempfile
import shutil
import atexit
import textwrap
import re
import pytest
from numpy.compat import asbytes, asstr
from numpy.testing import temppath
from importlib import import_module
#
# Maintaining a temporary module directory
#
_module_dir = None
_module_num = 5403
def _cleanup():
global _module_dir
if _module_dir is not None:
try:
sys.path.remove(_module_dir)
except ValueError:
pass
try:
shutil.rmtree(_module_dir)
except OSError:
pass
_module_dir = None
def get_module_dir():
global _module_dir
if _module_dir is None:
_module_dir = tempfile.mkdtemp()
atexit.register(_cleanup)
if _module_dir not in sys.path:
sys.path.insert(0, _module_dir)
return _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()
name = "_test_ext_module_%d" % _module_num
_module_num += 1
if name in sys.modules:
# this should not be possible, but check anyway
raise RuntimeError("Temporary module name already in use.")
return name
def _memoize(func):
memo = {}
def wrapper(*a, **kw):
key = repr((a, kw))
if key not in memo:
try:
memo[key] = func(*a, **kw)
except Exception as e:
memo[key] = e
raise
ret = memo[key]
if isinstance(ret, Exception):
raise ret
return ret
wrapper.__name__ = func.__name__
return wrapper
#
# Building modules
#
@_memoize
def build_module(source_files, options=[], skip=[], only=[], module_name=None):
"""
Compile and import a f2py module, built from the given files.
"""
code = ("import sys; sys.path = %s; import numpy.f2py as f2py2e; "
"f2py2e.main()" % repr(sys.path))
d = get_module_dir()
# Copy files
dst_sources = []
f2py_sources = []
for fn in source_files:
if not os.path.isfile(fn):
raise RuntimeError("%s is not a file" % fn)
dst = os.path.join(d, os.path.basename(fn))
shutil.copyfile(fn, dst)
dst_sources.append(dst)
base, ext = os.path.splitext(dst)
if ext in ('.f90', '.f', '.c', '.pyf'):
f2py_sources.append(dst)
# Prepare options
if module_name is None:
module_name = get_temp_module_name()
f2py_opts = ['-c', '-m', module_name] + options + f2py_sources
if skip:
f2py_opts += ['skip:'] + skip
if only:
f2py_opts += ['only:'] + only
# Build
cwd = os.getcwd()
try:
os.chdir(d)
cmd = [sys.executable, '-c', code] + f2py_opts
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
out, err = p.communicate()
if p.returncode != 0:
raise RuntimeError("Running f2py failed: %s\n%s"
% (cmd[4:], asstr(out)))
finally:
os.chdir(cwd)
# Partial cleanup
for fn in dst_sources:
os.unlink(fn)
# Import
return import_module(module_name)
@_memoize
def build_code(source_code, options=[], skip=[], only=[], suffix=None,
module_name=None):
"""
Compile and import Fortran code using f2py.
"""
if suffix is None:
suffix = '.f'
with temppath(suffix=suffix) as path:
with open(path, 'w') as f:
f.write(source_code)
return build_module([path], options=options, skip=skip, only=only,
module_name=module_name)
#
# Check if compilers are available at all...
#
_compiler_status = None
def _get_compiler_status():
global _compiler_status
if _compiler_status is not None:
return _compiler_status
_compiler_status = (False, False, False)
# XXX: this is really ugly. But I don't know how to invoke Distutils
# in a safer way...
code = textwrap.dedent("""\
import os
import sys
sys.path = %(syspath)s
def configuration(parent_name='',top_path=None):
global config
from numpy.distutils.misc_util import Configuration
config = Configuration('', parent_name, top_path)
return config
from numpy.distutils.core import setup
setup(configuration=configuration)
config_cmd = config.get_config_cmd()
have_c = config_cmd.try_compile('void foo() {}')
print('COMPILERS:%%d,%%d,%%d' %% (have_c,
config.have_f77c(),
config.have_f90c()))
sys.exit(99)
""")
code = code % dict(syspath=repr(sys.path))
tmpdir = tempfile.mkdtemp()
try:
script = os.path.join(tmpdir, 'setup.py')
with open(script, 'w') as f:
f.write(code)
cmd = [sys.executable, 'setup.py', 'config']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
cwd=tmpdir)
out, err = p.communicate()
finally:
shutil.rmtree(tmpdir)
m = re.search(br'COMPILERS:(\d+),(\d+),(\d+)', out)
if m:
_compiler_status = (bool(int(m.group(1))), bool(int(m.group(2))),
bool(int(m.group(3))))
# Finished
return _compiler_status
def has_c_compiler():
return _get_compiler_status()[0]
def has_f77_compiler():
return _get_compiler_status()[1]
def has_f90_compiler():
return _get_compiler_status()[2]
#
# Building with distutils
#
@_memoize
def build_module_distutils(source_files, config_code, module_name, **kw):
"""
Build a module via distutils and import it.
"""
d = get_module_dir()
# Copy files
dst_sources = []
for fn in source_files:
if not os.path.isfile(fn):
raise RuntimeError("%s is not a file" % fn)
dst = os.path.join(d, os.path.basename(fn))
shutil.copyfile(fn, dst)
dst_sources.append(dst)
# Build script
config_code = textwrap.dedent(config_code).replace("\n", "\n ")
code = textwrap.dedent("""\
import os
import sys
sys.path = %(syspath)s
def configuration(parent_name='',top_path=None):
from numpy.distutils.misc_util import Configuration
config = Configuration('', parent_name, top_path)
%(config_code)s
return config
if __name__ == "__main__":
from numpy.distutils.core import setup
setup(configuration=configuration)
""") % dict(config_code=config_code, syspath=repr(sys.path))
script = os.path.join(d, get_temp_module_name() + '.py')
dst_sources.append(script)
with open(script, 'wb') as f:
f.write(asbytes(code))
# Build
cwd = os.getcwd()
try:
os.chdir(d)
cmd = [sys.executable, script, 'build_ext', '-i']
p = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
out, err = p.communicate()
if p.returncode != 0:
raise RuntimeError("Running distutils build failed: %s\n%s"
% (cmd[4:], asstr(out)))
finally:
os.chdir(cwd)
# Partial cleanup
for fn in dst_sources:
os.unlink(fn)
# Import
__import__(module_name)
return sys.modules[module_name]
#
# Unittest convenience
#
class F2PyTest:
code = None
sources = None
options = []
skip = []
only = []
suffix = '.f'
module = None
module_name = None
def setup(self):
if sys.platform == 'win32':
pytest.skip('Fails with MinGW64 Gfortran (Issue #9673)')
if self.module is not None:
return
# Check compiler availability first
if not has_c_compiler():
pytest.skip("No C compiler available")
codes = []
if self.sources:
codes.extend(self.sources)
if self.code is not None:
codes.append(self.suffix)
needs_f77 = False
needs_f90 = False
for fn in codes:
if fn.endswith('.f'):
needs_f77 = True
elif fn.endswith('.f90'):
needs_f90 = True
if needs_f77 and not has_f77_compiler():
pytest.skip("No Fortran 77 compiler available")
if needs_f90 and not has_f90_compiler():
pytest.skip("No Fortran 90 compiler available")
# Build the module
if self.code is not None:
self.module = build_code(self.code, options=self.options,
skip=self.skip, only=self.only,
suffix=self.suffix,
module_name=self.module_name)
if self.sources is not None:
self.module = build_module(self.sources, options=self.options,
skip=self.skip, only=self.only,
module_name=self.module_name)