Merging PR_218 openai_rev package with new streamlit chat app
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
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
|
||||
@@ -0,0 +1,6 @@
|
||||
module test
|
||||
abstract interface
|
||||
subroutine foo()
|
||||
end subroutine
|
||||
end interface
|
||||
end module test
|
||||
@@ -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;
|
||||
int elsize = 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,"iiOiO|:wrap.call",\
|
||||
&type_num,&elsize,&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 = ndarray_from_pyobj(type_num,elsize,dims,rank,intent|F2PY_INTENT_OUT,arr_capi,"wrap.call failed");
|
||||
if (capi_arr_tmp == NULL) {
|
||||
free(dims);
|
||||
return NULL;
|
||||
}
|
||||
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("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
|
||||
@@ -0,0 +1 @@
|
||||
dict(real=dict(rk="double"))
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
module precision
|
||||
integer, parameter :: rk = selected_real_kind(8)
|
||||
integer, parameter :: ik = selected_real_kind(4)
|
||||
end module
|
||||
@@ -0,0 +1,6 @@
|
||||
SUBROUTINE FOO()
|
||||
INTEGER BAR(2, 3)
|
||||
|
||||
COMMON /BLOCK/ BAR
|
||||
RETURN
|
||||
END
|
||||
@@ -0,0 +1,62 @@
|
||||
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
|
||||
@@ -0,0 +1,7 @@
|
||||
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
|
||||
@@ -0,0 +1,17 @@
|
||||
! 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
|
||||
@@ -0,0 +1,3 @@
|
||||
SUBROUTINE HI
|
||||
PRINT*, "HELLO WORLD"
|
||||
END SUBROUTINE
|
||||
@@ -0,0 +1,3 @@
|
||||
function hi()
|
||||
print*, "Hello World"
|
||||
end function
|
||||
@@ -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
|
||||
@@ -0,0 +1,13 @@
|
||||
module foo
|
||||
public
|
||||
type, private, bind(c) :: a
|
||||
integer :: i
|
||||
end type a
|
||||
type, bind(c) :: b_
|
||||
integer :: j
|
||||
end type b_
|
||||
public :: b_
|
||||
type :: c
|
||||
integer :: k
|
||||
end type c
|
||||
end module foo
|
||||
@@ -0,0 +1,6 @@
|
||||
module foo
|
||||
type bar
|
||||
character(len = 4) :: text
|
||||
end type bar
|
||||
type(bar), parameter :: abar = bar('abar')
|
||||
end module foo
|
||||
@@ -0,0 +1,16 @@
|
||||
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
|
||||
@@ -0,0 +1,12 @@
|
||||
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
|
||||
@@ -0,0 +1,13 @@
|
||||
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
|
||||
@@ -0,0 +1,49 @@
|
||||
module foo
|
||||
type bar
|
||||
character(len = 32) :: item
|
||||
end type bar
|
||||
interface operator(.item.)
|
||||
module procedure item_int, item_real
|
||||
end interface operator(.item.)
|
||||
interface operator(==)
|
||||
module procedure items_are_equal
|
||||
end interface operator(==)
|
||||
interface assignment(=)
|
||||
module procedure get_int, get_real
|
||||
end interface assignment(=)
|
||||
contains
|
||||
function item_int(val) result(elem)
|
||||
integer, intent(in) :: val
|
||||
type(bar) :: elem
|
||||
|
||||
write(elem%item, "(I32)") val
|
||||
end function item_int
|
||||
|
||||
function item_real(val) result(elem)
|
||||
real, intent(in) :: val
|
||||
type(bar) :: elem
|
||||
|
||||
write(elem%item, "(1PE32.12)") val
|
||||
end function item_real
|
||||
|
||||
function items_are_equal(val1, val2) result(equal)
|
||||
type(bar), intent(in) :: val1, val2
|
||||
logical :: equal
|
||||
|
||||
equal = (val1%item == val2%item)
|
||||
end function items_are_equal
|
||||
|
||||
subroutine get_real(rval, item)
|
||||
real, intent(out) :: rval
|
||||
type(bar), intent(in) :: item
|
||||
|
||||
read(item%item, *) rval
|
||||
end subroutine get_real
|
||||
|
||||
subroutine get_int(rval, item)
|
||||
integer, intent(out) :: rval
|
||||
type(bar), intent(in) :: item
|
||||
|
||||
read(item%item, *) rval
|
||||
end subroutine get_int
|
||||
end module foo
|
||||
@@ -0,0 +1,11 @@
|
||||
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
|
||||
@@ -0,0 +1,10 @@
|
||||
module foo
|
||||
public
|
||||
integer, private :: a
|
||||
public :: setA
|
||||
contains
|
||||
subroutine setA(v)
|
||||
integer, intent(in) :: v
|
||||
a = v
|
||||
end subroutine setA
|
||||
end module foo
|
||||
@@ -0,0 +1,10 @@
|
||||
module foo
|
||||
public
|
||||
integer, private :: a
|
||||
integer :: b
|
||||
contains
|
||||
subroutine setA(v)
|
||||
integer, intent(in) :: v
|
||||
a = v
|
||||
end subroutine setA
|
||||
end module foo
|
||||
@@ -0,0 +1,4 @@
|
||||
subroutine foo(x)
|
||||
real(8), intent(in) :: x
|
||||
! Écrit à l'écran la valeur de x
|
||||
end subroutine
|
||||
@@ -0,0 +1 @@
|
||||
dict(real=dict(real32='float', real64='double'), integer=dict(int64='long_long'))
|
||||
@@ -0,0 +1,9 @@
|
||||
subroutine func1(n, x, res)
|
||||
use, intrinsic :: iso_fortran_env, only: int64, real64
|
||||
implicit none
|
||||
integer(int64), intent(in) :: n
|
||||
real(real64), intent(in) :: x(n)
|
||||
real(real64), intent(out) :: res
|
||||
Cf2py intent(hide) :: n
|
||||
res = sum(x)
|
||||
end
|
||||
@@ -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
|
||||
@@ -0,0 +1,5 @@
|
||||
subroutine bar11(a)
|
||||
cf2py intent(out) a
|
||||
integer a
|
||||
a = 11
|
||||
end
|
||||
@@ -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
|
||||
@@ -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
|
||||
Binary file not shown.
@@ -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
|
||||
@@ -0,0 +1,7 @@
|
||||
subroutine foo(is_, ie_, arr, tout)
|
||||
implicit none
|
||||
integer :: is_,ie_
|
||||
real, intent(in) :: arr(is_:ie_)
|
||||
real, intent(out) :: tout(is_:ie_)
|
||||
tout = arr
|
||||
end
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
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
|
||||
@@ -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
|
||||
@@ -0,0 +1,45 @@
|
||||
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
|
||||
@@ -0,0 +1,48 @@
|
||||
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
|
||||
@@ -0,0 +1,45 @@
|
||||
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
|
||||
@@ -0,0 +1,48 @@
|
||||
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
|
||||
@@ -0,0 +1,56 @@
|
||||
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
|
||||
@@ -0,0 +1,59 @@
|
||||
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
|
||||
@@ -0,0 +1,56 @@
|
||||
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
|
||||
@@ -0,0 +1,59 @@
|
||||
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
|
||||
@@ -0,0 +1,45 @@
|
||||
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
|
||||
@@ -0,0 +1,48 @@
|
||||
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
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
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
|
||||
@@ -0,0 +1,9 @@
|
||||
MODULE string_test
|
||||
|
||||
character(len=8) :: string
|
||||
character string77 * 8
|
||||
|
||||
character(len=12), dimension(5,7) :: strarr
|
||||
character strarr77(5,7) * 12
|
||||
|
||||
END MODULE string_test
|
||||
@@ -0,0 +1,12 @@
|
||||
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
|
||||
A(1:1) = 'A'
|
||||
B(1:1) = 'B'
|
||||
C(1:1) = 'C'
|
||||
D(1:1) = 'D'
|
||||
END
|
||||
C END OF FILE STRING.F
|
||||
@@ -0,0 +1,9 @@
|
||||
module fortfuncs
|
||||
implicit none
|
||||
contains
|
||||
subroutine square(x,y)
|
||||
integer, intent(in), value :: x
|
||||
integer, intent(out) :: y
|
||||
y = x*x
|
||||
end subroutine square
|
||||
end module fortfuncs
|
||||
@@ -0,0 +1,25 @@
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
import textwrap
|
||||
from . import util
|
||||
from numpy.f2py import crackfortran
|
||||
from numpy.testing import IS_WASM
|
||||
|
||||
|
||||
@pytest.mark.skipif(IS_WASM, reason="Cannot start subprocess")
|
||||
class TestAbstractInterface(util.F2PyTest):
|
||||
sources = [util.getpath("tests", "src", "abstract_interface", "foo.f90")]
|
||||
|
||||
skip = ["add1", "add2"]
|
||||
|
||||
def test_abstract_interface(self):
|
||||
assert self.module.ops_module.foo(3, 5) == (8, 13)
|
||||
|
||||
def test_parse_abstract_interface(self):
|
||||
# Test gh18403
|
||||
fpath = util.getpath("tests", "src", "abstract_interface",
|
||||
"gh18403_mod.f90")
|
||||
mod = crackfortran.crackfortran([str(fpath)])
|
||||
assert len(mod) == 1
|
||||
assert len(mod[0]["body"]) == 1
|
||||
assert mod[0]["body"][0]["block"] == "abstract interface"
|
||||
@@ -0,0 +1,686 @@
|
||||
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 as _typeinfo
|
||||
from . import util
|
||||
|
||||
wrap = None
|
||||
|
||||
# Extend core typeinfo with CHARACTER to test dtype('c')
|
||||
_ti = _typeinfo['STRING']
|
||||
typeinfo = dict(
|
||||
CHARACTER=type(_ti)(('c', _ti.num, 8, _ti.alignment, _ti.type)),
|
||||
**_typeinfo)
|
||||
|
||||
|
||||
def setup_module():
|
||||
"""
|
||||
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 = [
|
||||
util.getpath("tests", "src", "array_from_pyobj", "wrapmodule.c"),
|
||||
util.getpath("src", "fortranobject.c"),
|
||||
util.getpath("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",
|
||||
"STRING1",
|
||||
"STRING5",
|
||||
"CHARACTER",
|
||||
]
|
||||
|
||||
_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"]
|
||||
|
||||
_cast_dict['STRING1'] = ['STRING1']
|
||||
_cast_dict['STRING5'] = ['STRING5']
|
||||
_cast_dict['CHARACTER'] = ['CHARACTER']
|
||||
|
||||
# 32 bit system malloc typically does not provide the alignment required by
|
||||
# 16 byte long double types this means the inout intent cannot be satisfied
|
||||
# and several tests fail as the alignment flag can be randomly true or fals
|
||||
# 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()
|
||||
|
||||
if self.NAME == 'CHARACTER':
|
||||
info = typeinfo[self.NAME]
|
||||
self.type_num = getattr(wrap, 'NPY_STRING')
|
||||
self.elsize = 1
|
||||
self.dtype = np.dtype('c')
|
||||
elif self.NAME.startswith('STRING'):
|
||||
info = typeinfo[self.NAME[:6]]
|
||||
self.type_num = getattr(wrap, 'NPY_STRING')
|
||||
self.elsize = int(self.NAME[6:] or 0)
|
||||
self.dtype = np.dtype(f'S{self.elsize}')
|
||||
else:
|
||||
info = typeinfo[self.NAME]
|
||||
self.type_num = getattr(wrap, 'NPY_' + self.NAME)
|
||||
self.elsize = info.bits // 8
|
||||
self.dtype = np.dtype(info.type)
|
||||
|
||||
assert self.type_num == info.num
|
||||
self.type = info.type
|
||||
self.dtypechar = info.char
|
||||
|
||||
def __repr__(self):
|
||||
return (f"Type({self.NAME})|type_num={self.type_num},"
|
||||
f" dtype={self.dtype},"
|
||||
f" type={self.type}, elsize={self.elsize},"
|
||||
f" dtypechar={self.dtypechar}")
|
||||
|
||||
def cast_types(self):
|
||||
return [self.__class__(_m) for _m in _cast_dict[self.NAME]]
|
||||
|
||||
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 __repr__(self):
|
||||
return (f'Array({self.type}, {self.dims}, {self.intent},'
|
||||
f' {self.obj})|arr={self.arr}')
|
||||
|
||||
def __init__(self, typ, dims, intent, obj):
|
||||
self.type = typ
|
||||
self.dims = dims
|
||||
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,
|
||||
typ.elsize,
|
||||
dims, intent.flags, obj)
|
||||
|
||||
assert isinstance(self.arr, np.ndarray)
|
||||
|
||||
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"]
|
||||
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
|
||||
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
|
||||
else:
|
||||
assert 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 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:
|
||||
|
||||
@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)
|
||||
|
||||
@property
|
||||
def num2seq(self):
|
||||
if self.type.NAME.startswith('STRING'):
|
||||
elsize = self.type.elsize
|
||||
return ['1' * elsize, '2' * elsize]
|
||||
return [1, 2]
|
||||
|
||||
@property
|
||||
def num23seq(self):
|
||||
if self.type.NAME.startswith('STRING'):
|
||||
elsize = self.type.elsize
|
||||
return [['1' * elsize, '2' * elsize, '3' * elsize],
|
||||
['4' * elsize, '5' * elsize, '6' * elsize]]
|
||||
return [[1, 2, 3], [4, 5, 6]]
|
||||
|
||||
def test_in_from_2seq(self):
|
||||
a = self.array([2], intent.in_, self.num2seq)
|
||||
assert not a.has_shared_memory()
|
||||
|
||||
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()
|
||||
|
||||
@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()
|
||||
|
||||
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()
|
||||
|
||||
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()
|
||||
else:
|
||||
assert not a.has_shared_memory()
|
||||
|
||||
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()
|
||||
else:
|
||||
assert not a.has_shared_memory()
|
||||
|
||||
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()
|
||||
|
||||
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()
|
||||
|
||||
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()
|
||||
|
||||
a = self.array(shape, intent.in_.cache, obj)
|
||||
assert a.has_shared_memory()
|
||||
|
||||
obj = np.array(self.num2seq, dtype=t.dtype, order="F")
|
||||
a = self.array(shape, intent.in_.c.cache, obj)
|
||||
assert a.has_shared_memory()
|
||||
|
||||
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.NAME == 'STRING':
|
||||
# string elsize is 0, so skipping the test
|
||||
continue
|
||||
if t.elsize >= self.type.elsize:
|
||||
continue
|
||||
obj = np.array(self.num2seq, dtype=t.dtype)
|
||||
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)
|
||||
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)
|
||||
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!
|
||||
@@ -0,0 +1,49 @@
|
||||
import os
|
||||
import pytest
|
||||
import tempfile
|
||||
|
||||
from . import util
|
||||
|
||||
|
||||
class TestAssumedShapeSumExample(util.F2PyTest):
|
||||
sources = [
|
||||
util.getpath("tests", "src", "assumed_shape", "foo_free.f90"),
|
||||
util.getpath("tests", "src", "assumed_shape", "foo_use.f90"),
|
||||
util.getpath("tests", "src", "assumed_shape", "precision.f90"),
|
||||
util.getpath("tests", "src", "assumed_shape", "foo_mod.f90"),
|
||||
util.getpath("tests", "src", "assumed_shape", ".f2py_f2cmap"),
|
||||
]
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_all(self):
|
||||
r = self.module.fsum([1, 2])
|
||||
assert r == 3
|
||||
r = self.module.sum([1, 2])
|
||||
assert r == 3
|
||||
r = self.module.sum_with_use([1, 2])
|
||||
assert r == 3
|
||||
|
||||
r = self.module.mod.sum([1, 2])
|
||||
assert r == 3
|
||||
r = self.module.mod.fsum([1, 2])
|
||||
assert r == 3
|
||||
|
||||
|
||||
class TestF2cmapOption(TestAssumedShapeSumExample):
|
||||
def setup_method(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_method()
|
||||
|
||||
def teardown_method(self):
|
||||
os.unlink(self.f2cmap_file.name)
|
||||
@@ -0,0 +1,17 @@
|
||||
import sys
|
||||
import pytest
|
||||
from . import util
|
||||
|
||||
from numpy.testing import IS_PYPY
|
||||
|
||||
|
||||
class TestBlockDocString(util.F2PyTest):
|
||||
sources = [util.getpath("tests", "src", "block_docstring", "foo.f")]
|
||||
|
||||
@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 self.module.block.__doc__ == expected
|
||||
@@ -0,0 +1,230 @@
|
||||
import math
|
||||
import textwrap
|
||||
import sys
|
||||
import pytest
|
||||
import threading
|
||||
import traceback
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
from numpy.testing import IS_PYPY
|
||||
from . import util
|
||||
|
||||
|
||||
class TestF77Callback(util.F2PyTest):
|
||||
sources = [util.getpath("tests", "src", "callback", "foo.f")]
|
||||
|
||||
@pytest.mark.parametrize("name", "t,t2".split(","))
|
||||
def test_all(self, name):
|
||||
self.check_function(name)
|
||||
|
||||
@pytest.mark.xfail(IS_PYPY,
|
||||
reason="PyPy cannot modify tp_doc after PyType_Ready")
|
||||
def test_docstring(self):
|
||||
expected = textwrap.dedent("""\
|
||||
a = t(fun,[fun_extra_args])
|
||||
|
||||
Wrapper for ``t``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fun : call-back function
|
||||
|
||||
Other Parameters
|
||||
----------------
|
||||
fun_extra_args : input tuple, optional
|
||||
Default: ()
|
||||
|
||||
Returns
|
||||
-------
|
||||
a : int
|
||||
|
||||
Notes
|
||||
-----
|
||||
Call-back functions::
|
||||
|
||||
def fun(): return a
|
||||
Return objects:
|
||||
a : int
|
||||
""")
|
||||
assert self.module.t.__doc__ == expected
|
||||
|
||||
def check_function(self, name):
|
||||
t = getattr(self.module, name)
|
||||
r = t(lambda: 4)
|
||||
assert r == 4
|
||||
r = t(lambda a: 5, fun_extra_args=(6, ))
|
||||
assert r == 5
|
||||
r = t(lambda a: a, fun_extra_args=(6, ))
|
||||
assert r == 6
|
||||
r = t(lambda a: 5 + a, fun_extra_args=(7, ))
|
||||
assert r == 12
|
||||
r = t(lambda a: math.degrees(a), fun_extra_args=(math.pi, ))
|
||||
assert r == 180
|
||||
r = t(math.degrees, fun_extra_args=(math.pi, ))
|
||||
assert r == 180
|
||||
|
||||
r = t(self.module.func, fun_extra_args=(6, ))
|
||||
assert r == 17
|
||||
r = t(self.module.func0)
|
||||
assert r == 11
|
||||
r = t(self.module.func0._cpointer)
|
||||
assert r == 11
|
||||
|
||||
class A:
|
||||
def __call__(self):
|
||||
return 7
|
||||
|
||||
def mth(self):
|
||||
return 9
|
||||
|
||||
a = A()
|
||||
r = t(a)
|
||||
assert r == 7
|
||||
r = t(a.mth)
|
||||
assert r == 9
|
||||
|
||||
@pytest.mark.skipif(sys.platform == 'win32',
|
||||
reason='Fails with MinGW64 Gfortran (Issue #9673)')
|
||||
def test_string_callback(self):
|
||||
def callback(code):
|
||||
if code == "r":
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
f = getattr(self.module, "string_callback")
|
||||
r = f(callback)
|
||||
assert r == 0
|
||||
|
||||
@pytest.mark.skipif(sys.platform == 'win32',
|
||||
reason='Fails with MinGW64 Gfortran (Issue #9673)')
|
||||
def test_string_callback_array(self):
|
||||
# See gh-10027
|
||||
cu1 = np.zeros((1, ), "S8")
|
||||
cu2 = np.zeros((1, 8), "c")
|
||||
cu3 = np.array([""], "S8")
|
||||
|
||||
def callback(cu, lencu):
|
||||
if cu.shape != (lencu,):
|
||||
return 1
|
||||
if cu.dtype != "S8":
|
||||
return 2
|
||||
if not np.all(cu == b""):
|
||||
return 3
|
||||
return 0
|
||||
|
||||
f = getattr(self.module, "string_callback_array")
|
||||
for cu in [cu1, cu2, cu3]:
|
||||
res = f(callback, cu, cu.size)
|
||||
assert res == 0
|
||||
|
||||
def test_threadsafety(self):
|
||||
# Segfaults if the callback handling is not threadsafe
|
||||
|
||||
errors = []
|
||||
|
||||
def cb():
|
||||
# Sleep here to make it more likely for another thread
|
||||
# to call their callback at the same time.
|
||||
time.sleep(1e-3)
|
||||
|
||||
# Check reentrancy
|
||||
r = self.module.t(lambda: 123)
|
||||
assert r == 123
|
||||
|
||||
return 42
|
||||
|
||||
def runner(name):
|
||||
try:
|
||||
for j in range(50):
|
||||
r = self.module.t(cb)
|
||||
assert r == 42
|
||||
self.check_function(name)
|
||||
except Exception:
|
||||
errors.append(traceback.format_exc())
|
||||
|
||||
threads = [
|
||||
threading.Thread(target=runner, args=(arg, ))
|
||||
for arg in ("t", "t2") for n in range(20)
|
||||
]
|
||||
|
||||
for t in threads:
|
||||
t.start()
|
||||
|
||||
for t in threads:
|
||||
t.join()
|
||||
|
||||
errors = "\n\n".join(errors)
|
||||
if errors:
|
||||
raise AssertionError(errors)
|
||||
|
||||
def test_hidden_callback(self):
|
||||
try:
|
||||
self.module.hidden_callback(2)
|
||||
except Exception as msg:
|
||||
assert str(msg).startswith("Callback global_f not defined")
|
||||
|
||||
try:
|
||||
self.module.hidden_callback2(2)
|
||||
except Exception as msg:
|
||||
assert str(msg).startswith("cb: Callback global_f not defined")
|
||||
|
||||
self.module.global_f = lambda x: x + 1
|
||||
r = self.module.hidden_callback(2)
|
||||
assert r == 3
|
||||
|
||||
self.module.global_f = lambda x: x + 2
|
||||
r = self.module.hidden_callback(2)
|
||||
assert r == 4
|
||||
|
||||
del self.module.global_f
|
||||
try:
|
||||
self.module.hidden_callback(2)
|
||||
except Exception as msg:
|
||||
assert str(msg).startswith("Callback global_f not defined")
|
||||
|
||||
self.module.global_f = lambda x=0: x + 3
|
||||
r = self.module.hidden_callback(2)
|
||||
assert r == 5
|
||||
|
||||
# reproducer of gh18341
|
||||
r = self.module.hidden_callback2(2)
|
||||
assert r == 3
|
||||
|
||||
|
||||
class TestF77CallbackPythonTLS(TestF77Callback):
|
||||
"""
|
||||
Callback tests using Python thread-local storage instead of
|
||||
compiler-provided
|
||||
"""
|
||||
|
||||
options = ["-DF2PY_USE_PYTHON_TLS"]
|
||||
|
||||
|
||||
class TestF90Callback(util.F2PyTest):
|
||||
sources = [util.getpath("tests", "src", "callback", "gh17797.f90")]
|
||||
|
||||
def test_gh17797(self):
|
||||
def incr(x):
|
||||
return x + 123
|
||||
|
||||
y = np.array([1, 2, 3], dtype=np.int64)
|
||||
r = self.module.gh17797(incr, y)
|
||||
assert r == 123 + 1 + 2 + 3
|
||||
|
||||
|
||||
class TestGH18335(util.F2PyTest):
|
||||
"""The reproduction of the reported issue requires specific input that
|
||||
extensions may break the issue conditions, so the reproducer is
|
||||
implemented as a separate test class. Do not extend this test with
|
||||
other tests!
|
||||
"""
|
||||
sources = [util.getpath("tests", "src", "callback", "gh18335.f90")]
|
||||
|
||||
def test_gh18335(self):
|
||||
def foo(x):
|
||||
x[0] += 1
|
||||
|
||||
r = self.module.gh18335(foo)
|
||||
assert r == 123 + 1
|
||||
@@ -0,0 +1,592 @@
|
||||
import pytest
|
||||
import textwrap
|
||||
from numpy.testing import assert_array_equal, assert_equal, assert_raises
|
||||
import numpy as np
|
||||
from numpy.f2py.tests import util
|
||||
|
||||
|
||||
class TestCharacterString(util.F2PyTest):
|
||||
# options = ['--debug-capi', '--build-dir', '/tmp/test-build-f2py']
|
||||
suffix = '.f90'
|
||||
fprefix = 'test_character_string'
|
||||
length_list = ['1', '3', 'star']
|
||||
|
||||
code = ''
|
||||
for length in length_list:
|
||||
fsuffix = length
|
||||
clength = dict(star='(*)').get(length, length)
|
||||
|
||||
code += textwrap.dedent(f"""
|
||||
|
||||
subroutine {fprefix}_input_{fsuffix}(c, o, n)
|
||||
character*{clength}, intent(in) :: c
|
||||
integer n
|
||||
!f2py integer, depend(c), intent(hide) :: n = slen(c)
|
||||
integer*1, dimension(n) :: o
|
||||
!f2py intent(out) o
|
||||
o = transfer(c, o)
|
||||
end subroutine {fprefix}_input_{fsuffix}
|
||||
|
||||
subroutine {fprefix}_output_{fsuffix}(c, o, n)
|
||||
character*{clength}, intent(out) :: c
|
||||
integer n
|
||||
integer*1, dimension(n), intent(in) :: o
|
||||
!f2py integer, depend(o), intent(hide) :: n = len(o)
|
||||
c = transfer(o, c)
|
||||
end subroutine {fprefix}_output_{fsuffix}
|
||||
|
||||
subroutine {fprefix}_array_input_{fsuffix}(c, o, m, n)
|
||||
integer m, i, n
|
||||
character*{clength}, intent(in), dimension(m) :: c
|
||||
!f2py integer, depend(c), intent(hide) :: m = len(c)
|
||||
!f2py integer, depend(c), intent(hide) :: n = f2py_itemsize(c)
|
||||
integer*1, dimension(m, n), intent(out) :: o
|
||||
do i=1,m
|
||||
o(i, :) = transfer(c(i), o(i, :))
|
||||
end do
|
||||
end subroutine {fprefix}_array_input_{fsuffix}
|
||||
|
||||
subroutine {fprefix}_array_output_{fsuffix}(c, o, m, n)
|
||||
character*{clength}, intent(out), dimension(m) :: c
|
||||
integer n
|
||||
integer*1, dimension(m, n), intent(in) :: o
|
||||
!f2py character(f2py_len=n) :: c
|
||||
!f2py integer, depend(o), intent(hide) :: m = len(o)
|
||||
!f2py integer, depend(o), intent(hide) :: n = shape(o, 1)
|
||||
do i=1,m
|
||||
c(i) = transfer(o(i, :), c(i))
|
||||
end do
|
||||
end subroutine {fprefix}_array_output_{fsuffix}
|
||||
|
||||
subroutine {fprefix}_2d_array_input_{fsuffix}(c, o, m1, m2, n)
|
||||
integer m1, m2, i, j, n
|
||||
character*{clength}, intent(in), dimension(m1, m2) :: c
|
||||
!f2py integer, depend(c), intent(hide) :: m1 = len(c)
|
||||
!f2py integer, depend(c), intent(hide) :: m2 = shape(c, 1)
|
||||
!f2py integer, depend(c), intent(hide) :: n = f2py_itemsize(c)
|
||||
integer*1, dimension(m1, m2, n), intent(out) :: o
|
||||
do i=1,m1
|
||||
do j=1,m2
|
||||
o(i, j, :) = transfer(c(i, j), o(i, j, :))
|
||||
end do
|
||||
end do
|
||||
end subroutine {fprefix}_2d_array_input_{fsuffix}
|
||||
""")
|
||||
|
||||
@pytest.mark.parametrize("length", length_list)
|
||||
def test_input(self, length):
|
||||
fsuffix = {'(*)': 'star'}.get(length, length)
|
||||
f = getattr(self.module, self.fprefix + '_input_' + fsuffix)
|
||||
|
||||
a = {'1': 'a', '3': 'abc', 'star': 'abcde' * 3}[length]
|
||||
|
||||
assert_array_equal(f(a), np.array(list(map(ord, a)), dtype='u1'))
|
||||
|
||||
@pytest.mark.parametrize("length", length_list[:-1])
|
||||
def test_output(self, length):
|
||||
fsuffix = length
|
||||
f = getattr(self.module, self.fprefix + '_output_' + fsuffix)
|
||||
|
||||
a = {'1': 'a', '3': 'abc'}[length]
|
||||
|
||||
assert_array_equal(f(np.array(list(map(ord, a)), dtype='u1')),
|
||||
a.encode())
|
||||
|
||||
@pytest.mark.parametrize("length", length_list)
|
||||
def test_array_input(self, length):
|
||||
fsuffix = length
|
||||
f = getattr(self.module, self.fprefix + '_array_input_' + fsuffix)
|
||||
|
||||
a = np.array([{'1': 'a', '3': 'abc', 'star': 'abcde' * 3}[length],
|
||||
{'1': 'A', '3': 'ABC', 'star': 'ABCDE' * 3}[length],
|
||||
], dtype='S')
|
||||
|
||||
expected = np.array([[c for c in s] for s in a], dtype='u1')
|
||||
assert_array_equal(f(a), expected)
|
||||
|
||||
@pytest.mark.parametrize("length", length_list)
|
||||
def test_array_output(self, length):
|
||||
fsuffix = length
|
||||
f = getattr(self.module, self.fprefix + '_array_output_' + fsuffix)
|
||||
|
||||
expected = np.array(
|
||||
[{'1': 'a', '3': 'abc', 'star': 'abcde' * 3}[length],
|
||||
{'1': 'A', '3': 'ABC', 'star': 'ABCDE' * 3}[length]], dtype='S')
|
||||
|
||||
a = np.array([[c for c in s] for s in expected], dtype='u1')
|
||||
assert_array_equal(f(a), expected)
|
||||
|
||||
@pytest.mark.parametrize("length", length_list)
|
||||
def test_2d_array_input(self, length):
|
||||
fsuffix = length
|
||||
f = getattr(self.module, self.fprefix + '_2d_array_input_' + fsuffix)
|
||||
|
||||
a = np.array([[{'1': 'a', '3': 'abc', 'star': 'abcde' * 3}[length],
|
||||
{'1': 'A', '3': 'ABC', 'star': 'ABCDE' * 3}[length]],
|
||||
[{'1': 'f', '3': 'fgh', 'star': 'fghij' * 3}[length],
|
||||
{'1': 'F', '3': 'FGH', 'star': 'FGHIJ' * 3}[length]]],
|
||||
dtype='S')
|
||||
expected = np.array([[[c for c in item] for item in row] for row in a],
|
||||
dtype='u1', order='F')
|
||||
assert_array_equal(f(a), expected)
|
||||
|
||||
|
||||
class TestCharacter(util.F2PyTest):
|
||||
# options = ['--debug-capi', '--build-dir', '/tmp/test-build-f2py']
|
||||
suffix = '.f90'
|
||||
fprefix = 'test_character'
|
||||
|
||||
code = textwrap.dedent(f"""
|
||||
subroutine {fprefix}_input(c, o)
|
||||
character, intent(in) :: c
|
||||
integer*1 o
|
||||
!f2py intent(out) o
|
||||
o = transfer(c, o)
|
||||
end subroutine {fprefix}_input
|
||||
|
||||
subroutine {fprefix}_output(c, o)
|
||||
character :: c
|
||||
integer*1, intent(in) :: o
|
||||
!f2py intent(out) c
|
||||
c = transfer(o, c)
|
||||
end subroutine {fprefix}_output
|
||||
|
||||
subroutine {fprefix}_input_output(c, o)
|
||||
character, intent(in) :: c
|
||||
character o
|
||||
!f2py intent(out) o
|
||||
o = c
|
||||
end subroutine {fprefix}_input_output
|
||||
|
||||
subroutine {fprefix}_inout(c, n)
|
||||
character :: c, n
|
||||
!f2py intent(in) n
|
||||
!f2py intent(inout) c
|
||||
c = n
|
||||
end subroutine {fprefix}_inout
|
||||
|
||||
function {fprefix}_return(o) result (c)
|
||||
character :: c
|
||||
character, intent(in) :: o
|
||||
c = transfer(o, c)
|
||||
end function {fprefix}_return
|
||||
|
||||
subroutine {fprefix}_array_input(c, o)
|
||||
character, intent(in) :: c(3)
|
||||
integer*1 o(3)
|
||||
!f2py intent(out) o
|
||||
integer i
|
||||
do i=1,3
|
||||
o(i) = transfer(c(i), o(i))
|
||||
end do
|
||||
end subroutine {fprefix}_array_input
|
||||
|
||||
subroutine {fprefix}_2d_array_input(c, o)
|
||||
character, intent(in) :: c(2, 3)
|
||||
integer*1 o(2, 3)
|
||||
!f2py intent(out) o
|
||||
integer i, j
|
||||
do i=1,2
|
||||
do j=1,3
|
||||
o(i, j) = transfer(c(i, j), o(i, j))
|
||||
end do
|
||||
end do
|
||||
end subroutine {fprefix}_2d_array_input
|
||||
|
||||
subroutine {fprefix}_array_output(c, o)
|
||||
character :: c(3)
|
||||
integer*1, intent(in) :: o(3)
|
||||
!f2py intent(out) c
|
||||
do i=1,3
|
||||
c(i) = transfer(o(i), c(i))
|
||||
end do
|
||||
end subroutine {fprefix}_array_output
|
||||
|
||||
subroutine {fprefix}_array_inout(c, n)
|
||||
character :: c(3), n(3)
|
||||
!f2py intent(in) n(3)
|
||||
!f2py intent(inout) c(3)
|
||||
do i=1,3
|
||||
c(i) = n(i)
|
||||
end do
|
||||
end subroutine {fprefix}_array_inout
|
||||
|
||||
subroutine {fprefix}_2d_array_inout(c, n)
|
||||
character :: c(2, 3), n(2, 3)
|
||||
!f2py intent(in) n(2, 3)
|
||||
!f2py intent(inout) c(2. 3)
|
||||
integer i, j
|
||||
do i=1,2
|
||||
do j=1,3
|
||||
c(i, j) = n(i, j)
|
||||
end do
|
||||
end do
|
||||
end subroutine {fprefix}_2d_array_inout
|
||||
|
||||
function {fprefix}_array_return(o) result (c)
|
||||
character, dimension(3) :: c
|
||||
character, intent(in) :: o(3)
|
||||
do i=1,3
|
||||
c(i) = o(i)
|
||||
end do
|
||||
end function {fprefix}_array_return
|
||||
|
||||
function {fprefix}_optional(o) result (c)
|
||||
character, intent(in) :: o
|
||||
!f2py character o = "a"
|
||||
character :: c
|
||||
c = o
|
||||
end function {fprefix}_optional
|
||||
""")
|
||||
|
||||
@pytest.mark.parametrize("dtype", ['c', 'S1'])
|
||||
def test_input(self, dtype):
|
||||
f = getattr(self.module, self.fprefix + '_input')
|
||||
|
||||
assert_equal(f(np.array('a', dtype=dtype)), ord('a'))
|
||||
assert_equal(f(np.array(b'a', dtype=dtype)), ord('a'))
|
||||
assert_equal(f(np.array(['a'], dtype=dtype)), ord('a'))
|
||||
assert_equal(f(np.array('abc', dtype=dtype)), ord('a'))
|
||||
assert_equal(f(np.array([['a']], dtype=dtype)), ord('a'))
|
||||
|
||||
def test_input_varia(self):
|
||||
f = getattr(self.module, self.fprefix + '_input')
|
||||
|
||||
assert_equal(f('a'), ord('a'))
|
||||
assert_equal(f(b'a'), ord(b'a'))
|
||||
assert_equal(f(''), 0)
|
||||
assert_equal(f(b''), 0)
|
||||
assert_equal(f(b'\0'), 0)
|
||||
assert_equal(f('ab'), ord('a'))
|
||||
assert_equal(f(b'ab'), ord('a'))
|
||||
assert_equal(f(['a']), ord('a'))
|
||||
|
||||
assert_equal(f(np.array(b'a')), ord('a'))
|
||||
assert_equal(f(np.array([b'a'])), ord('a'))
|
||||
a = np.array('a')
|
||||
assert_equal(f(a), ord('a'))
|
||||
a = np.array(['a'])
|
||||
assert_equal(f(a), ord('a'))
|
||||
|
||||
try:
|
||||
f([])
|
||||
except IndexError as msg:
|
||||
if not str(msg).endswith(' got 0-list'):
|
||||
raise
|
||||
else:
|
||||
raise SystemError(f'{f.__name__} should have failed on empty list')
|
||||
|
||||
try:
|
||||
f(97)
|
||||
except TypeError as msg:
|
||||
if not str(msg).endswith(' got int instance'):
|
||||
raise
|
||||
else:
|
||||
raise SystemError(f'{f.__name__} should have failed on int value')
|
||||
|
||||
@pytest.mark.parametrize("dtype", ['c', 'S1', 'U1'])
|
||||
def test_array_input(self, dtype):
|
||||
f = getattr(self.module, self.fprefix + '_array_input')
|
||||
|
||||
assert_array_equal(f(np.array(['a', 'b', 'c'], dtype=dtype)),
|
||||
np.array(list(map(ord, 'abc')), dtype='i1'))
|
||||
assert_array_equal(f(np.array([b'a', b'b', b'c'], dtype=dtype)),
|
||||
np.array(list(map(ord, 'abc')), dtype='i1'))
|
||||
|
||||
def test_array_input_varia(self):
|
||||
f = getattr(self.module, self.fprefix + '_array_input')
|
||||
assert_array_equal(f(['a', 'b', 'c']),
|
||||
np.array(list(map(ord, 'abc')), dtype='i1'))
|
||||
assert_array_equal(f([b'a', b'b', b'c']),
|
||||
np.array(list(map(ord, 'abc')), dtype='i1'))
|
||||
|
||||
try:
|
||||
f(['a', 'b', 'c', 'd'])
|
||||
except ValueError as msg:
|
||||
if not str(msg).endswith(
|
||||
'th dimension must be fixed to 3 but got 4'):
|
||||
raise
|
||||
else:
|
||||
raise SystemError(
|
||||
f'{f.__name__} should have failed on wrong input')
|
||||
|
||||
@pytest.mark.parametrize("dtype", ['c', 'S1', 'U1'])
|
||||
def test_2d_array_input(self, dtype):
|
||||
f = getattr(self.module, self.fprefix + '_2d_array_input')
|
||||
|
||||
a = np.array([['a', 'b', 'c'],
|
||||
['d', 'e', 'f']], dtype=dtype, order='F')
|
||||
expected = a.view(np.uint32 if dtype == 'U1' else np.uint8)
|
||||
assert_array_equal(f(a), expected)
|
||||
|
||||
def test_output(self):
|
||||
f = getattr(self.module, self.fprefix + '_output')
|
||||
|
||||
assert_equal(f(ord(b'a')), b'a')
|
||||
assert_equal(f(0), b'\0')
|
||||
|
||||
def test_array_output(self):
|
||||
f = getattr(self.module, self.fprefix + '_array_output')
|
||||
|
||||
assert_array_equal(f(list(map(ord, 'abc'))),
|
||||
np.array(list('abc'), dtype='S1'))
|
||||
|
||||
def test_input_output(self):
|
||||
f = getattr(self.module, self.fprefix + '_input_output')
|
||||
|
||||
assert_equal(f(b'a'), b'a')
|
||||
assert_equal(f('a'), b'a')
|
||||
assert_equal(f(''), b'\0')
|
||||
|
||||
@pytest.mark.parametrize("dtype", ['c', 'S1'])
|
||||
def test_inout(self, dtype):
|
||||
f = getattr(self.module, self.fprefix + '_inout')
|
||||
|
||||
a = np.array(list('abc'), dtype=dtype)
|
||||
f(a, 'A')
|
||||
assert_array_equal(a, np.array(list('Abc'), dtype=a.dtype))
|
||||
f(a[1:], 'B')
|
||||
assert_array_equal(a, np.array(list('ABc'), dtype=a.dtype))
|
||||
|
||||
a = np.array(['abc'], dtype=dtype)
|
||||
f(a, 'A')
|
||||
assert_array_equal(a, np.array(['Abc'], dtype=a.dtype))
|
||||
|
||||
def test_inout_varia(self):
|
||||
f = getattr(self.module, self.fprefix + '_inout')
|
||||
a = np.array('abc', dtype='S3')
|
||||
f(a, 'A')
|
||||
assert_array_equal(a, np.array('Abc', dtype=a.dtype))
|
||||
|
||||
a = np.array(['abc'], dtype='S3')
|
||||
f(a, 'A')
|
||||
assert_array_equal(a, np.array(['Abc'], dtype=a.dtype))
|
||||
|
||||
try:
|
||||
f('abc', 'A')
|
||||
except ValueError as msg:
|
||||
if not str(msg).endswith(' got 3-str'):
|
||||
raise
|
||||
else:
|
||||
raise SystemError(f'{f.__name__} should have failed on str value')
|
||||
|
||||
@pytest.mark.parametrize("dtype", ['c', 'S1'])
|
||||
def test_array_inout(self, dtype):
|
||||
f = getattr(self.module, self.fprefix + '_array_inout')
|
||||
n = np.array(['A', 'B', 'C'], dtype=dtype, order='F')
|
||||
|
||||
a = np.array(['a', 'b', 'c'], dtype=dtype, order='F')
|
||||
f(a, n)
|
||||
assert_array_equal(a, n)
|
||||
|
||||
a = np.array(['a', 'b', 'c', 'd'], dtype=dtype)
|
||||
f(a[1:], n)
|
||||
assert_array_equal(a, np.array(['a', 'A', 'B', 'C'], dtype=dtype))
|
||||
|
||||
a = np.array([['a', 'b', 'c']], dtype=dtype, order='F')
|
||||
f(a, n)
|
||||
assert_array_equal(a, np.array([['A', 'B', 'C']], dtype=dtype))
|
||||
|
||||
a = np.array(['a', 'b', 'c', 'd'], dtype=dtype, order='F')
|
||||
try:
|
||||
f(a, n)
|
||||
except ValueError as msg:
|
||||
if not str(msg).endswith(
|
||||
'th dimension must be fixed to 3 but got 4'):
|
||||
raise
|
||||
else:
|
||||
raise SystemError(
|
||||
f'{f.__name__} should have failed on wrong input')
|
||||
|
||||
@pytest.mark.parametrize("dtype", ['c', 'S1'])
|
||||
def test_2d_array_inout(self, dtype):
|
||||
f = getattr(self.module, self.fprefix + '_2d_array_inout')
|
||||
n = np.array([['A', 'B', 'C'],
|
||||
['D', 'E', 'F']],
|
||||
dtype=dtype, order='F')
|
||||
a = np.array([['a', 'b', 'c'],
|
||||
['d', 'e', 'f']],
|
||||
dtype=dtype, order='F')
|
||||
f(a, n)
|
||||
assert_array_equal(a, n)
|
||||
|
||||
def test_return(self):
|
||||
f = getattr(self.module, self.fprefix + '_return')
|
||||
|
||||
assert_equal(f('a'), b'a')
|
||||
|
||||
@pytest.mark.skip('fortran function returning array segfaults')
|
||||
def test_array_return(self):
|
||||
f = getattr(self.module, self.fprefix + '_array_return')
|
||||
|
||||
a = np.array(list('abc'), dtype='S1')
|
||||
assert_array_equal(f(a), a)
|
||||
|
||||
def test_optional(self):
|
||||
f = getattr(self.module, self.fprefix + '_optional')
|
||||
|
||||
assert_equal(f(), b"a")
|
||||
assert_equal(f(b'B'), b"B")
|
||||
|
||||
|
||||
class TestMiscCharacter(util.F2PyTest):
|
||||
# options = ['--debug-capi', '--build-dir', '/tmp/test-build-f2py']
|
||||
suffix = '.f90'
|
||||
fprefix = 'test_misc_character'
|
||||
|
||||
code = textwrap.dedent(f"""
|
||||
subroutine {fprefix}_gh18684(x, y, m)
|
||||
character(len=5), dimension(m), intent(in) :: x
|
||||
character*5, dimension(m), intent(out) :: y
|
||||
integer i, m
|
||||
!f2py integer, intent(hide), depend(x) :: m = f2py_len(x)
|
||||
do i=1,m
|
||||
y(i) = x(i)
|
||||
end do
|
||||
end subroutine {fprefix}_gh18684
|
||||
|
||||
subroutine {fprefix}_gh6308(x, i)
|
||||
integer i
|
||||
!f2py check(i>=0 && i<12) i
|
||||
character*5 name, x
|
||||
common name(12)
|
||||
name(i + 1) = x
|
||||
end subroutine {fprefix}_gh6308
|
||||
|
||||
subroutine {fprefix}_gh4519(x)
|
||||
character(len=*), intent(in) :: x(:)
|
||||
!f2py intent(out) x
|
||||
integer :: i
|
||||
do i=1, size(x)
|
||||
print*, "x(",i,")=", x(i)
|
||||
end do
|
||||
end subroutine {fprefix}_gh4519
|
||||
|
||||
pure function {fprefix}_gh3425(x) result (y)
|
||||
character(len=*), intent(in) :: x
|
||||
character(len=len(x)) :: y
|
||||
integer :: i
|
||||
do i = 1, len(x)
|
||||
j = iachar(x(i:i))
|
||||
if (j>=iachar("a") .and. j<=iachar("z") ) then
|
||||
y(i:i) = achar(j-32)
|
||||
else
|
||||
y(i:i) = x(i:i)
|
||||
endif
|
||||
end do
|
||||
end function {fprefix}_gh3425
|
||||
|
||||
subroutine {fprefix}_character_bc_new(x, y, z)
|
||||
character, intent(in) :: x
|
||||
character, intent(out) :: y
|
||||
!f2py character, depend(x) :: y = x
|
||||
!f2py character, dimension((x=='a'?1:2)), depend(x), intent(out) :: z
|
||||
character, dimension(*) :: z
|
||||
!f2py character, optional, check(x == 'a' || x == 'b') :: x = 'a'
|
||||
!f2py callstatement (*f2py_func)(&x, &y, z)
|
||||
!f2py callprotoargument character*, character*, character*
|
||||
if (y.eq.x) then
|
||||
y = x
|
||||
else
|
||||
y = 'e'
|
||||
endif
|
||||
z(1) = 'c'
|
||||
end subroutine {fprefix}_character_bc_new
|
||||
|
||||
subroutine {fprefix}_character_bc_old(x, y, z)
|
||||
character, intent(in) :: x
|
||||
character, intent(out) :: y
|
||||
!f2py character, depend(x) :: y = x[0]
|
||||
!f2py character, dimension((*x=='a'?1:2)), depend(x), intent(out) :: z
|
||||
character, dimension(*) :: z
|
||||
!f2py character, optional, check(*x == 'a' || x[0] == 'b') :: x = 'a'
|
||||
!f2py callstatement (*f2py_func)(x, y, z)
|
||||
!f2py callprotoargument char*, char*, char*
|
||||
if (y.eq.x) then
|
||||
y = x
|
||||
else
|
||||
y = 'e'
|
||||
endif
|
||||
z(1) = 'c'
|
||||
end subroutine {fprefix}_character_bc_old
|
||||
""")
|
||||
|
||||
def test_gh18684(self):
|
||||
# Test character(len=5) and character*5 usages
|
||||
f = getattr(self.module, self.fprefix + '_gh18684')
|
||||
x = np.array(["abcde", "fghij"], dtype='S5')
|
||||
y = f(x)
|
||||
|
||||
assert_array_equal(x, y)
|
||||
|
||||
def test_gh6308(self):
|
||||
# Test character string array in a common block
|
||||
f = getattr(self.module, self.fprefix + '_gh6308')
|
||||
|
||||
assert_equal(self.module._BLNK_.name.dtype, np.dtype('S5'))
|
||||
assert_equal(len(self.module._BLNK_.name), 12)
|
||||
f("abcde", 0)
|
||||
assert_equal(self.module._BLNK_.name[0], b"abcde")
|
||||
f("12345", 5)
|
||||
assert_equal(self.module._BLNK_.name[5], b"12345")
|
||||
|
||||
def test_gh4519(self):
|
||||
# Test array of assumed length strings
|
||||
f = getattr(self.module, self.fprefix + '_gh4519')
|
||||
|
||||
for x, expected in [
|
||||
('a', dict(shape=(), dtype=np.dtype('S1'))),
|
||||
('text', dict(shape=(), dtype=np.dtype('S4'))),
|
||||
(np.array(['1', '2', '3'], dtype='S1'),
|
||||
dict(shape=(3,), dtype=np.dtype('S1'))),
|
||||
(['1', '2', '34'],
|
||||
dict(shape=(3,), dtype=np.dtype('S2'))),
|
||||
(['', ''], dict(shape=(2,), dtype=np.dtype('S1')))]:
|
||||
r = f(x)
|
||||
for k, v in expected.items():
|
||||
assert_equal(getattr(r, k), v)
|
||||
|
||||
def test_gh3425(self):
|
||||
# Test returning a copy of assumed length string
|
||||
f = getattr(self.module, self.fprefix + '_gh3425')
|
||||
# f is equivalent to bytes.upper
|
||||
|
||||
assert_equal(f('abC'), b'ABC')
|
||||
assert_equal(f(''), b'')
|
||||
assert_equal(f('abC12d'), b'ABC12D')
|
||||
|
||||
@pytest.mark.parametrize("state", ['new', 'old'])
|
||||
def test_character_bc(self, state):
|
||||
f = getattr(self.module, self.fprefix + '_character_bc_' + state)
|
||||
|
||||
c, a = f()
|
||||
assert_equal(c, b'a')
|
||||
assert_equal(len(a), 1)
|
||||
|
||||
c, a = f(b'b')
|
||||
assert_equal(c, b'b')
|
||||
assert_equal(len(a), 2)
|
||||
|
||||
assert_raises(Exception, lambda: f(b'c'))
|
||||
|
||||
|
||||
class TestStringScalarArr(util.F2PyTest):
|
||||
sources = [util.getpath("tests", "src", "string", "scalar_string.f90")]
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_char(self):
|
||||
for out in (self.module.string_test.string,
|
||||
self.module.string_test.string77):
|
||||
expected = ()
|
||||
assert out.shape == expected
|
||||
expected = '|S8'
|
||||
assert out.dtype == expected
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_char_arr(self):
|
||||
for out in (self.module.string_test.strarr,
|
||||
self.module.string_test.strarr77):
|
||||
expected = (5,7)
|
||||
assert out.shape == expected
|
||||
expected = '|S12'
|
||||
assert out.dtype == expected
|
||||
@@ -0,0 +1,18 @@
|
||||
import os
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
import numpy as np
|
||||
from . import util
|
||||
|
||||
|
||||
class TestCommonBlock(util.F2PyTest):
|
||||
sources = [util.getpath("tests", "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 self.module.block.long_bn == np.array(1.0, dtype=np.float64)
|
||||
assert self.module.block.string_bn == np.array("2", dtype="|S1")
|
||||
assert self.module.block.ok == np.array(3, dtype=np.int32)
|
||||
@@ -0,0 +1,117 @@
|
||||
"""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 . 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
|
||||
with util.switchdir(moddir):
|
||||
ret_val = numpy.f2py.compile(fsource,
|
||||
modulename=modname,
|
||||
extra_args=extra_args,
|
||||
source_fn=source_fn)
|
||||
|
||||
# check for compile success return value
|
||||
assert 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 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 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 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
|
||||
with util.switchdir(tmpdir):
|
||||
ret_val = numpy.f2py.compile(fsource,
|
||||
modulename="test_compile_from_strings",
|
||||
extension=".f90")
|
||||
assert ret_val == 0
|
||||
@@ -0,0 +1,278 @@
|
||||
import importlib
|
||||
import codecs
|
||||
import unicodedata
|
||||
import pytest
|
||||
import numpy as np
|
||||
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
|
||||
sources = [util.getpath("tests", "src", "crackfortran", "gh15035.f")]
|
||||
|
||||
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 np.allclose(k, w + 1)
|
||||
self.module.subc([w, k])
|
||||
assert np.allclose(k, w + 1)
|
||||
assert self.module.t0("23") == b"2"
|
||||
|
||||
|
||||
class TestPublicPrivate:
|
||||
def test_defaultPrivate(self):
|
||||
fpath = util.getpath("tests", "src", "crackfortran", "privatemod.f90")
|
||||
mod = crackfortran.crackfortran([str(fpath)])
|
||||
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):
|
||||
fpath = util.getpath("tests", "src", "crackfortran", "publicmod.f90")
|
||||
mod = crackfortran.crackfortran([str(fpath)])
|
||||
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"]
|
||||
|
||||
def test_access_type(self, tmp_path):
|
||||
fpath = util.getpath("tests", "src", "crackfortran", "accesstype.f90")
|
||||
mod = crackfortran.crackfortran([str(fpath)])
|
||||
assert len(mod) == 1
|
||||
tt = mod[0]['vars']
|
||||
assert set(tt['a']['attrspec']) == {'private', 'bind(c)'}
|
||||
assert set(tt['b_']['attrspec']) == {'public', 'bind(c)'}
|
||||
assert set(tt['c']['attrspec']) == {'public'}
|
||||
|
||||
|
||||
class TestModuleProcedure():
|
||||
def test_moduleOperators(self, tmp_path):
|
||||
fpath = util.getpath("tests", "src", "crackfortran", "operators.f90")
|
||||
mod = crackfortran.crackfortran([str(fpath)])
|
||||
assert len(mod) == 1
|
||||
mod = mod[0]
|
||||
assert "body" in mod and len(mod["body"]) == 9
|
||||
assert mod["body"][1]["name"] == "operator(.item.)"
|
||||
assert "implementedby" in mod["body"][1]
|
||||
assert mod["body"][1]["implementedby"] == \
|
||||
["item_int", "item_real"]
|
||||
assert mod["body"][2]["name"] == "operator(==)"
|
||||
assert "implementedby" in mod["body"][2]
|
||||
assert mod["body"][2]["implementedby"] == ["items_are_equal"]
|
||||
assert mod["body"][3]["name"] == "assignment(=)"
|
||||
assert "implementedby" in mod["body"][3]
|
||||
assert mod["body"][3]["implementedby"] == \
|
||||
["get_int", "get_real"]
|
||||
|
||||
def test_notPublicPrivate(self, tmp_path):
|
||||
fpath = util.getpath("tests", "src", "crackfortran", "pubprivmod.f90")
|
||||
mod = crackfortran.crackfortran([str(fpath)])
|
||||
assert len(mod) == 1
|
||||
mod = mod[0]
|
||||
assert mod['vars']['a']['attrspec'] == ['private', ]
|
||||
assert mod['vars']['b']['attrspec'] == ['public', ]
|
||||
assert mod['vars']['seta']['attrspec'] == ['public', ]
|
||||
|
||||
|
||||
class TestExternal(util.F2PyTest):
|
||||
# issue gh-17859: add external attribute support
|
||||
sources = [util.getpath("tests", "src", "crackfortran", "gh17859.f")]
|
||||
|
||||
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):
|
||||
# gh-2848: commented lines between parameters in subroutine parameter lists
|
||||
sources = [util.getpath("tests", "src", "crackfortran", "gh2848.f90")]
|
||||
|
||||
def test_gh2848(self):
|
||||
r = self.module.gh2848(1, 2)
|
||||
assert r == (1, 2)
|
||||
|
||||
|
||||
class TestMarkinnerspaces:
|
||||
# gh-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 markinnerspaces(i) == i
|
||||
|
||||
def test_one_relevant_space(self):
|
||||
assert markinnerspaces("a 'b c' \\' \\'") == "a 'b@_@c' \\' \\'"
|
||||
assert markinnerspaces(r'a "b c" \" \"') == r'a "b@_@c" \" \"'
|
||||
|
||||
def test_ignore_inner_quotes(self):
|
||||
assert markinnerspaces("a 'b c\" \" d' e") == "a 'b@_@c\"@_@\"@_@d' e"
|
||||
assert markinnerspaces("a \"b c' ' d\" e") == "a \"b@_@c'@_@'@_@d\" e"
|
||||
|
||||
def test_multiple_relevant_spaces(self):
|
||||
assert markinnerspaces("a 'b c' 'd e'") == "a 'b@_@c' 'd@_@e'"
|
||||
assert markinnerspaces(r'a "b c" "d e"') == r'a "b@_@c" "d@_@e"'
|
||||
|
||||
class TestDimSpec(util.F2PyTest):
|
||||
"""This test suite tests various expressions that are used as dimension
|
||||
specifications.
|
||||
|
||||
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):
|
||||
fpath = util.getpath("tests", "src", "crackfortran", "foo_deps.f90")
|
||||
mod = crackfortran.crackfortran([str(fpath)])
|
||||
assert len(mod) == 1
|
||||
assert mod[0]["vars"]["abar"]["="] == "bar('abar')"
|
||||
|
||||
class TestEval(util.F2PyTest):
|
||||
def test_eval_scalar(self):
|
||||
eval_scalar = crackfortran._eval_scalar
|
||||
|
||||
assert eval_scalar('123', {}) == '123'
|
||||
assert eval_scalar('12 + 3', {}) == '15'
|
||||
assert eval_scalar('a + b', dict(a=1, b=2)) == '3'
|
||||
assert eval_scalar('"123"', {}) == "'123'"
|
||||
|
||||
|
||||
class TestFortranReader(util.F2PyTest):
|
||||
@pytest.mark.parametrize("encoding",
|
||||
['ascii', 'utf-8', 'utf-16', 'utf-32'])
|
||||
def test_input_encoding(self, tmp_path, encoding):
|
||||
# gh-635
|
||||
f_path = tmp_path / f"input_with_{encoding}_encoding.f90"
|
||||
with f_path.open('w', encoding=encoding) as ff:
|
||||
ff.write("""
|
||||
subroutine foo()
|
||||
end subroutine foo
|
||||
""")
|
||||
mod = crackfortran.crackfortran([str(f_path)])
|
||||
assert mod[0]['name'] == 'foo'
|
||||
|
||||
class TestUnicodeComment(util.F2PyTest):
|
||||
sources = [util.getpath("tests", "src", "crackfortran", "unicode_comment.f90")]
|
||||
|
||||
@pytest.mark.skipif(
|
||||
(importlib.util.find_spec("charset_normalizer") is None),
|
||||
reason="test requires charset_normalizer which is not installed",
|
||||
)
|
||||
def test_encoding_comment(self):
|
||||
self.module.foo(3)
|
||||
@@ -0,0 +1,55 @@
|
||||
import os
|
||||
import pytest
|
||||
import numpy as np
|
||||
from numpy.testing import assert_array_equal, assert_equal
|
||||
from . import util
|
||||
|
||||
|
||||
def get_docdir():
|
||||
# assuming that documentation tests are run from a source
|
||||
# directory
|
||||
return os.path.abspath(os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
'..', '..', '..',
|
||||
'doc', 'source', 'f2py', 'code'))
|
||||
|
||||
|
||||
pytestmark = pytest.mark.skipif(
|
||||
not os.path.isdir(get_docdir()),
|
||||
reason=('Could not find f2py documentation sources'
|
||||
f' ({get_docdir()} does not exists)'))
|
||||
|
||||
|
||||
def _path(*a):
|
||||
return os.path.join(*((get_docdir(),) + a))
|
||||
|
||||
|
||||
class TestDocAdvanced(util.F2PyTest):
|
||||
# options = ['--debug-capi', '--build-dir', '/tmp/build-f2py']
|
||||
sources = [_path('asterisk1.f90'), _path('asterisk2.f90'),
|
||||
_path('ftype.f')]
|
||||
|
||||
def test_asterisk1(self):
|
||||
foo = getattr(self.module, 'foo1')
|
||||
assert_equal(foo(), b'123456789A12')
|
||||
|
||||
def test_asterisk2(self):
|
||||
foo = getattr(self.module, 'foo2')
|
||||
assert_equal(foo(2), b'12')
|
||||
assert_equal(foo(12), b'123456789A12')
|
||||
assert_equal(foo(24), b'123456789A123456789B')
|
||||
|
||||
def test_ftype(self):
|
||||
ftype = self.module
|
||||
ftype.foo()
|
||||
assert_equal(ftype.data.a, 0)
|
||||
ftype.data.a = 3
|
||||
ftype.data.x = [1, 2, 3]
|
||||
assert_equal(ftype.data.a, 3)
|
||||
assert_array_equal(ftype.data.x,
|
||||
np.array([1, 2, 3], dtype=np.float32))
|
||||
ftype.data.x[1] = 45
|
||||
assert_array_equal(ftype.data.x,
|
||||
np.array([1, 45, 3], dtype=np.float32))
|
||||
|
||||
# TODO: implement test methods for other example Fortran codes
|
||||
@@ -0,0 +1,15 @@
|
||||
from . import util
|
||||
import numpy as np
|
||||
|
||||
class TestF2Cmap(util.F2PyTest):
|
||||
sources = [
|
||||
util.getpath("tests", "src", "f2cmap", "isoFortranEnvMap.f90"),
|
||||
util.getpath("tests", "src", "f2cmap", ".f2py_f2cmap")
|
||||
]
|
||||
|
||||
# gh-15095
|
||||
def test_long_long_map(self):
|
||||
inp = np.ones(3)
|
||||
out = self.module.func1(inp)
|
||||
exp_out = 3
|
||||
assert out == exp_out
|
||||
769
venv/lib/python3.9/site-packages/numpy/f2py/tests/test_f2py2e.py
Normal file
769
venv/lib/python3.9/site-packages/numpy/f2py/tests/test_f2py2e.py
Normal file
@@ -0,0 +1,769 @@
|
||||
import textwrap, re, sys, subprocess, shlex
|
||||
from pathlib import Path
|
||||
from collections import namedtuple
|
||||
|
||||
import pytest
|
||||
|
||||
from . import util
|
||||
from numpy.f2py.f2py2e import main as f2pycli
|
||||
|
||||
#########################
|
||||
# CLI utils and classes #
|
||||
#########################
|
||||
|
||||
PPaths = namedtuple("PPaths", "finp, f90inp, pyf, wrap77, wrap90, cmodf")
|
||||
|
||||
|
||||
def get_io_paths(fname_inp, mname="untitled"):
|
||||
"""Takes in a temporary file for testing and returns the expected output and input paths
|
||||
|
||||
Here expected output is essentially one of any of the possible generated
|
||||
files.
|
||||
|
||||
..note::
|
||||
|
||||
Since this does not actually run f2py, none of these are guaranteed to
|
||||
exist, and module names are typically incorrect
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname_inp : str
|
||||
The input filename
|
||||
mname : str, optional
|
||||
The name of the module, untitled by default
|
||||
|
||||
Returns
|
||||
-------
|
||||
genp : NamedTuple PPaths
|
||||
The possible paths which are generated, not all of which exist
|
||||
"""
|
||||
bpath = Path(fname_inp)
|
||||
return PPaths(
|
||||
finp=bpath.with_suffix(".f"),
|
||||
f90inp=bpath.with_suffix(".f90"),
|
||||
pyf=bpath.with_suffix(".pyf"),
|
||||
wrap77=bpath.with_name(f"{mname}-f2pywrappers.f"),
|
||||
wrap90=bpath.with_name(f"{mname}-f2pywrappers2.f90"),
|
||||
cmodf=bpath.with_name(f"{mname}module.c"),
|
||||
)
|
||||
|
||||
|
||||
##############
|
||||
# CLI Fixtures and Tests #
|
||||
#############
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def hello_world_f90(tmpdir_factory):
|
||||
"""Generates a single f90 file for testing"""
|
||||
fdat = util.getpath("tests", "src", "cli", "hiworld.f90").read_text()
|
||||
fn = tmpdir_factory.getbasetemp() / "hello.f90"
|
||||
fn.write_text(fdat, encoding="ascii")
|
||||
return fn
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def hello_world_f77(tmpdir_factory):
|
||||
"""Generates a single f77 file for testing"""
|
||||
fdat = util.getpath("tests", "src", "cli", "hi77.f").read_text()
|
||||
fn = tmpdir_factory.getbasetemp() / "hello.f"
|
||||
fn.write_text(fdat, encoding="ascii")
|
||||
return fn
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def retreal_f77(tmpdir_factory):
|
||||
"""Generates a single f77 file for testing"""
|
||||
fdat = util.getpath("tests", "src", "return_real", "foo77.f").read_text()
|
||||
fn = tmpdir_factory.getbasetemp() / "foo.f"
|
||||
fn.write_text(fdat, encoding="ascii")
|
||||
return fn
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def f2cmap_f90(tmpdir_factory):
|
||||
"""Generates a single f90 file for testing"""
|
||||
fdat = util.getpath("tests", "src", "f2cmap", "isoFortranEnvMap.f90").read_text()
|
||||
f2cmap = util.getpath("tests", "src", "f2cmap", ".f2py_f2cmap").read_text()
|
||||
fn = tmpdir_factory.getbasetemp() / "f2cmap.f90"
|
||||
fmap = tmpdir_factory.getbasetemp() / "mapfile"
|
||||
fn.write_text(fdat, encoding="ascii")
|
||||
fmap.write_text(f2cmap, encoding="ascii")
|
||||
return fn
|
||||
|
||||
|
||||
def test_gen_pyf(capfd, hello_world_f90, monkeypatch):
|
||||
"""Ensures that a signature file is generated via the CLI
|
||||
CLI :: -h
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
opath = Path(hello_world_f90).stem + ".pyf"
|
||||
monkeypatch.setattr(sys, "argv", f'f2py -h {opath} {ipath}'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli() # Generate wrappers
|
||||
out, _ = capfd.readouterr()
|
||||
assert "Saving signatures to file" in out
|
||||
assert Path(f'{opath}').exists()
|
||||
|
||||
|
||||
def test_gen_pyf_stdout(capfd, hello_world_f90, monkeypatch):
|
||||
"""Ensures that a signature file can be dumped to stdout
|
||||
CLI :: -h
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
monkeypatch.setattr(sys, "argv", f'f2py -h stdout {ipath}'.split())
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert "Saving signatures to file" in out
|
||||
assert "function hi() ! in " in out
|
||||
|
||||
|
||||
def test_gen_pyf_no_overwrite(capfd, hello_world_f90, monkeypatch):
|
||||
"""Ensures that the CLI refuses to overwrite signature files
|
||||
CLI :: -h without --overwrite-signature
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
monkeypatch.setattr(sys, "argv", f'f2py -h faker.pyf {ipath}'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
Path("faker.pyf").write_text("Fake news", encoding="ascii")
|
||||
with pytest.raises(SystemExit):
|
||||
f2pycli() # Refuse to overwrite
|
||||
_, err = capfd.readouterr()
|
||||
assert "Use --overwrite-signature to overwrite" in err
|
||||
|
||||
|
||||
@pytest.mark.xfail
|
||||
def test_f2py_skip(capfd, retreal_f77, monkeypatch):
|
||||
"""Tests that functions can be skipped
|
||||
CLI :: skip:
|
||||
"""
|
||||
foutl = get_io_paths(retreal_f77, mname="test")
|
||||
ipath = foutl.finp
|
||||
toskip = "t0 t4 t8 sd s8 s4"
|
||||
remaining = "td s0"
|
||||
monkeypatch.setattr(
|
||||
sys, "argv",
|
||||
f'f2py {ipath} -m test skip: {toskip}'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, err = capfd.readouterr()
|
||||
for skey in toskip.split():
|
||||
assert (
|
||||
f'buildmodule: Could not found the body of interfaced routine "{skey}". Skipping.'
|
||||
in err)
|
||||
for rkey in remaining.split():
|
||||
assert f'Constructing wrapper function "{rkey}"' in out
|
||||
|
||||
|
||||
def test_f2py_only(capfd, retreal_f77, monkeypatch):
|
||||
"""Test that functions can be kept by only:
|
||||
CLI :: only:
|
||||
"""
|
||||
foutl = get_io_paths(retreal_f77, mname="test")
|
||||
ipath = foutl.finp
|
||||
toskip = "t0 t4 t8 sd s8 s4"
|
||||
tokeep = "td s0"
|
||||
monkeypatch.setattr(
|
||||
sys, "argv",
|
||||
f'f2py {ipath} -m test only: {tokeep}'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, err = capfd.readouterr()
|
||||
for skey in toskip.split():
|
||||
assert (
|
||||
f'buildmodule: Could not find the body of interfaced routine "{skey}". Skipping.'
|
||||
in err)
|
||||
for rkey in tokeep.split():
|
||||
assert f'Constructing wrapper function "{rkey}"' in out
|
||||
|
||||
|
||||
def test_file_processing_switch(capfd, hello_world_f90, retreal_f77,
|
||||
monkeypatch):
|
||||
"""Tests that it is possible to return to file processing mode
|
||||
CLI :: :
|
||||
BUG: numpy-gh #20520
|
||||
"""
|
||||
foutl = get_io_paths(retreal_f77, mname="test")
|
||||
ipath = foutl.finp
|
||||
toskip = "t0 t4 t8 sd s8 s4"
|
||||
ipath2 = Path(hello_world_f90)
|
||||
tokeep = "td s0 hi" # hi is in ipath2
|
||||
mname = "blah"
|
||||
monkeypatch.setattr(
|
||||
sys,
|
||||
"argv",
|
||||
f'f2py {ipath} -m {mname} only: {tokeep} : {ipath2}'.split(
|
||||
),
|
||||
)
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, err = capfd.readouterr()
|
||||
for skey in toskip.split():
|
||||
assert (
|
||||
f'buildmodule: Could not find the body of interfaced routine "{skey}". Skipping.'
|
||||
in err)
|
||||
for rkey in tokeep.split():
|
||||
assert f'Constructing wrapper function "{rkey}"' in out
|
||||
|
||||
|
||||
def test_mod_gen_f77(capfd, hello_world_f90, monkeypatch):
|
||||
"""Checks the generation of files based on a module name
|
||||
CLI :: -m
|
||||
"""
|
||||
MNAME = "hi"
|
||||
foutl = get_io_paths(hello_world_f90, mname=MNAME)
|
||||
ipath = foutl.f90inp
|
||||
monkeypatch.setattr(sys, "argv", f'f2py {ipath} -m {MNAME}'.split())
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
|
||||
# Always generate C module
|
||||
assert Path.exists(foutl.cmodf)
|
||||
# File contains a function, check for F77 wrappers
|
||||
assert Path.exists(foutl.wrap77)
|
||||
|
||||
|
||||
def test_lower_cmod(capfd, hello_world_f77, monkeypatch):
|
||||
"""Lowers cases by flag or when -h is present
|
||||
|
||||
CLI :: --[no-]lower
|
||||
"""
|
||||
foutl = get_io_paths(hello_world_f77, mname="test")
|
||||
ipath = foutl.finp
|
||||
capshi = re.compile(r"HI\(\)")
|
||||
capslo = re.compile(r"hi\(\)")
|
||||
# Case I: --lower is passed
|
||||
monkeypatch.setattr(sys, "argv", f'f2py {ipath} -m test --lower'.split())
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert capslo.search(out) is not None
|
||||
assert capshi.search(out) is None
|
||||
# Case II: --no-lower is passed
|
||||
monkeypatch.setattr(sys, "argv",
|
||||
f'f2py {ipath} -m test --no-lower'.split())
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert capslo.search(out) is None
|
||||
assert capshi.search(out) is not None
|
||||
|
||||
|
||||
def test_lower_sig(capfd, hello_world_f77, monkeypatch):
|
||||
"""Lowers cases in signature files by flag or when -h is present
|
||||
|
||||
CLI :: --[no-]lower -h
|
||||
"""
|
||||
foutl = get_io_paths(hello_world_f77, mname="test")
|
||||
ipath = foutl.finp
|
||||
# Signature files
|
||||
capshi = re.compile(r"Block: HI")
|
||||
capslo = re.compile(r"Block: hi")
|
||||
# Case I: --lower is implied by -h
|
||||
# TODO: Clean up to prevent passing --overwrite-signature
|
||||
monkeypatch.setattr(
|
||||
sys,
|
||||
"argv",
|
||||
f'f2py {ipath} -h {foutl.pyf} -m test --overwrite-signature'.split(),
|
||||
)
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert capslo.search(out) is not None
|
||||
assert capshi.search(out) is None
|
||||
|
||||
# Case II: --no-lower overrides -h
|
||||
monkeypatch.setattr(
|
||||
sys,
|
||||
"argv",
|
||||
f'f2py {ipath} -h {foutl.pyf} -m test --overwrite-signature --no-lower'
|
||||
.split(),
|
||||
)
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert capslo.search(out) is None
|
||||
assert capshi.search(out) is not None
|
||||
|
||||
|
||||
def test_build_dir(capfd, hello_world_f90, monkeypatch):
|
||||
"""Ensures that the build directory can be specified
|
||||
|
||||
CLI :: --build-dir
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
mname = "blah"
|
||||
odir = "tttmp"
|
||||
monkeypatch.setattr(sys, "argv",
|
||||
f'f2py -m {mname} {ipath} --build-dir {odir}'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert f"Wrote C/API module \"{mname}\"" in out
|
||||
|
||||
|
||||
def test_overwrite(capfd, hello_world_f90, monkeypatch):
|
||||
"""Ensures that the build directory can be specified
|
||||
|
||||
CLI :: --overwrite-signature
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
monkeypatch.setattr(
|
||||
sys, "argv",
|
||||
f'f2py -h faker.pyf {ipath} --overwrite-signature'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
Path("faker.pyf").write_text("Fake news", encoding="ascii")
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert "Saving signatures to file" in out
|
||||
|
||||
|
||||
def test_latexdoc(capfd, hello_world_f90, monkeypatch):
|
||||
"""Ensures that TeX documentation is written out
|
||||
|
||||
CLI :: --latex-doc
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
mname = "blah"
|
||||
monkeypatch.setattr(sys, "argv",
|
||||
f'f2py -m {mname} {ipath} --latex-doc'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert "Documentation is saved to file" in out
|
||||
with Path(f"{mname}module.tex").open() as otex:
|
||||
assert "\\documentclass" in otex.read()
|
||||
|
||||
|
||||
def test_nolatexdoc(capfd, hello_world_f90, monkeypatch):
|
||||
"""Ensures that TeX documentation is written out
|
||||
|
||||
CLI :: --no-latex-doc
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
mname = "blah"
|
||||
monkeypatch.setattr(sys, "argv",
|
||||
f'f2py -m {mname} {ipath} --no-latex-doc'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert "Documentation is saved to file" not in out
|
||||
|
||||
|
||||
def test_shortlatex(capfd, hello_world_f90, monkeypatch):
|
||||
"""Ensures that truncated documentation is written out
|
||||
|
||||
TODO: Test to ensure this has no effect without --latex-doc
|
||||
CLI :: --latex-doc --short-latex
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
mname = "blah"
|
||||
monkeypatch.setattr(
|
||||
sys,
|
||||
"argv",
|
||||
f'f2py -m {mname} {ipath} --latex-doc --short-latex'.split(),
|
||||
)
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert "Documentation is saved to file" in out
|
||||
with Path(f"./{mname}module.tex").open() as otex:
|
||||
assert "\\documentclass" not in otex.read()
|
||||
|
||||
|
||||
def test_restdoc(capfd, hello_world_f90, monkeypatch):
|
||||
"""Ensures that RsT documentation is written out
|
||||
|
||||
CLI :: --rest-doc
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
mname = "blah"
|
||||
monkeypatch.setattr(sys, "argv",
|
||||
f'f2py -m {mname} {ipath} --rest-doc'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert "ReST Documentation is saved to file" in out
|
||||
with Path(f"./{mname}module.rest").open() as orst:
|
||||
assert r".. -*- rest -*-" in orst.read()
|
||||
|
||||
|
||||
def test_norestexdoc(capfd, hello_world_f90, monkeypatch):
|
||||
"""Ensures that TeX documentation is written out
|
||||
|
||||
CLI :: --no-rest-doc
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
mname = "blah"
|
||||
monkeypatch.setattr(sys, "argv",
|
||||
f'f2py -m {mname} {ipath} --no-rest-doc'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert "ReST Documentation is saved to file" not in out
|
||||
|
||||
|
||||
def test_debugcapi(capfd, hello_world_f90, monkeypatch):
|
||||
"""Ensures that debugging wrappers are written
|
||||
|
||||
CLI :: --debug-capi
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
mname = "blah"
|
||||
monkeypatch.setattr(sys, "argv",
|
||||
f'f2py -m {mname} {ipath} --debug-capi'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
with Path(f"./{mname}module.c").open() as ocmod:
|
||||
assert r"#define DEBUGCFUNCS" in ocmod.read()
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="Consistently fails on CI.")
|
||||
def test_debugcapi_bld(hello_world_f90, monkeypatch):
|
||||
"""Ensures that debugging wrappers work
|
||||
|
||||
CLI :: --debug-capi -c
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
mname = "blah"
|
||||
monkeypatch.setattr(sys, "argv",
|
||||
f'f2py -m {mname} {ipath} -c --debug-capi'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
cmd_run = shlex.split("python3 -c \"import blah; blah.hi()\"")
|
||||
rout = subprocess.run(cmd_run, capture_output=True, encoding='UTF-8')
|
||||
eout = ' Hello World\n'
|
||||
eerr = textwrap.dedent("""\
|
||||
debug-capi:Python C/API function blah.hi()
|
||||
debug-capi:float hi=:output,hidden,scalar
|
||||
debug-capi:hi=0
|
||||
debug-capi:Fortran subroutine `f2pywraphi(&hi)'
|
||||
debug-capi:hi=0
|
||||
debug-capi:Building return value.
|
||||
debug-capi:Python C/API function blah.hi: successful.
|
||||
debug-capi:Freeing memory.
|
||||
""")
|
||||
assert rout.stdout == eout
|
||||
assert rout.stderr == eerr
|
||||
|
||||
|
||||
def test_wrapfunc_def(capfd, hello_world_f90, monkeypatch):
|
||||
"""Ensures that fortran subroutine wrappers for F77 are included by default
|
||||
|
||||
CLI :: --[no]-wrap-functions
|
||||
"""
|
||||
# Implied
|
||||
ipath = Path(hello_world_f90)
|
||||
mname = "blah"
|
||||
monkeypatch.setattr(sys, "argv", f'f2py -m {mname} {ipath}'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert r"Fortran 77 wrappers are saved to" in out
|
||||
|
||||
# Explicit
|
||||
monkeypatch.setattr(sys, "argv",
|
||||
f'f2py -m {mname} {ipath} --wrap-functions'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert r"Fortran 77 wrappers are saved to" in out
|
||||
|
||||
|
||||
def test_nowrapfunc(capfd, hello_world_f90, monkeypatch):
|
||||
"""Ensures that fortran subroutine wrappers for F77 can be disabled
|
||||
|
||||
CLI :: --no-wrap-functions
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
mname = "blah"
|
||||
monkeypatch.setattr(sys, "argv",
|
||||
f'f2py -m {mname} {ipath} --no-wrap-functions'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert r"Fortran 77 wrappers are saved to" not in out
|
||||
|
||||
|
||||
def test_inclheader(capfd, hello_world_f90, monkeypatch):
|
||||
"""Add to the include directories
|
||||
|
||||
CLI :: -include
|
||||
TODO: Document this in the help string
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
mname = "blah"
|
||||
monkeypatch.setattr(
|
||||
sys,
|
||||
"argv",
|
||||
f'f2py -m {mname} {ipath} -include<stdbool.h> -include<stdio.h> '.
|
||||
split(),
|
||||
)
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
with Path(f"./{mname}module.c").open() as ocmod:
|
||||
ocmr = ocmod.read()
|
||||
assert "#include <stdbool.h>" in ocmr
|
||||
assert "#include <stdio.h>" in ocmr
|
||||
|
||||
|
||||
def test_inclpath():
|
||||
"""Add to the include directories
|
||||
|
||||
CLI :: --include-paths
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_hlink():
|
||||
"""Add to the include directories
|
||||
|
||||
CLI :: --help-link
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_f2cmap(capfd, f2cmap_f90, monkeypatch):
|
||||
"""Check that Fortran-to-Python KIND specs can be passed
|
||||
|
||||
CLI :: --f2cmap
|
||||
"""
|
||||
ipath = Path(f2cmap_f90)
|
||||
monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} --f2cmap mapfile'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert "Reading f2cmap from 'mapfile' ..." in out
|
||||
assert "Mapping \"real(kind=real32)\" to \"float\"" in out
|
||||
assert "Mapping \"real(kind=real64)\" to \"double\"" in out
|
||||
assert "Mapping \"integer(kind=int64)\" to \"long_long\"" in out
|
||||
assert "Successfully applied user defined f2cmap changes" in out
|
||||
|
||||
|
||||
def test_quiet(capfd, hello_world_f90, monkeypatch):
|
||||
"""Reduce verbosity
|
||||
|
||||
CLI :: --quiet
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} --quiet'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert len(out) == 0
|
||||
|
||||
|
||||
def test_verbose(capfd, hello_world_f90, monkeypatch):
|
||||
"""Increase verbosity
|
||||
|
||||
CLI :: --verbose
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} --verbose'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
assert "analyzeline" in out
|
||||
|
||||
|
||||
def test_version(capfd, monkeypatch):
|
||||
"""Ensure version
|
||||
|
||||
CLI :: -v
|
||||
"""
|
||||
monkeypatch.setattr(sys, "argv", 'f2py -v'.split())
|
||||
# TODO: f2py2e should not call sys.exit() after printing the version
|
||||
with pytest.raises(SystemExit):
|
||||
f2pycli()
|
||||
out, _ = capfd.readouterr()
|
||||
import numpy as np
|
||||
assert np.__version__ == out.strip()
|
||||
|
||||
|
||||
@pytest.mark.xfail(reason="Consistently fails on CI.")
|
||||
def test_npdistop(hello_world_f90, monkeypatch):
|
||||
"""
|
||||
CLI :: -c
|
||||
"""
|
||||
ipath = Path(hello_world_f90)
|
||||
monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} -c'.split())
|
||||
|
||||
with util.switchdir(ipath.parent):
|
||||
f2pycli()
|
||||
cmd_run = shlex.split("python -c \"import blah; blah.hi()\"")
|
||||
rout = subprocess.run(cmd_run, capture_output=True, encoding='UTF-8')
|
||||
eout = ' Hello World\n'
|
||||
assert rout.stdout == eout
|
||||
|
||||
|
||||
# Numpy distutils flags
|
||||
# TODO: These should be tested separately
|
||||
|
||||
|
||||
def test_npd_fcompiler():
|
||||
"""
|
||||
CLI :: -c --fcompiler
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_compiler():
|
||||
"""
|
||||
CLI :: -c --compiler
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_help_fcompiler():
|
||||
"""
|
||||
CLI :: -c --help-fcompiler
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_f77exec():
|
||||
"""
|
||||
CLI :: -c --f77exec
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_f90exec():
|
||||
"""
|
||||
CLI :: -c --f90exec
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_f77flags():
|
||||
"""
|
||||
CLI :: -c --f77flags
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_f90flags():
|
||||
"""
|
||||
CLI :: -c --f90flags
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_opt():
|
||||
"""
|
||||
CLI :: -c --opt
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_arch():
|
||||
"""
|
||||
CLI :: -c --arch
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_noopt():
|
||||
"""
|
||||
CLI :: -c --noopt
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_noarch():
|
||||
"""
|
||||
CLI :: -c --noarch
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_debug():
|
||||
"""
|
||||
CLI :: -c --debug
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_link_auto():
|
||||
"""
|
||||
CLI :: -c --link-<resource>
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_lib():
|
||||
"""
|
||||
CLI :: -c -L/path/to/lib/ -l<libname>
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_define():
|
||||
"""
|
||||
CLI :: -D<define>
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_undefine():
|
||||
"""
|
||||
CLI :: -U<name>
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_incl():
|
||||
"""
|
||||
CLI :: -I/path/to/include/
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
|
||||
|
||||
def test_npd_linker():
|
||||
"""
|
||||
CLI :: <filename>.o <filename>.so <filename>.a
|
||||
"""
|
||||
# TODO: populate
|
||||
pass
|
||||
@@ -0,0 +1,26 @@
|
||||
import os
|
||||
import pytest
|
||||
|
||||
from numpy.f2py.crackfortran import (
|
||||
_selected_int_kind_func as selected_int_kind,
|
||||
_selected_real_kind_func as selected_real_kind,
|
||||
)
|
||||
from . import util
|
||||
|
||||
|
||||
class TestKind(util.F2PyTest):
|
||||
sources = [util.getpath("tests", "src", "kind", "foo.f90")]
|
||||
|
||||
def test_all(self):
|
||||
selectedrealkind = self.module.selectedrealkind
|
||||
selectedintkind = self.module.selectedintkind
|
||||
|
||||
for i in range(40):
|
||||
assert selectedintkind(i) == selected_int_kind(
|
||||
i
|
||||
), f"selectedintkind({i}): expected {selected_int_kind(i)!r} but got {selectedintkind(i)!r}"
|
||||
|
||||
for i in range(20):
|
||||
assert selectedrealkind(i) == selected_real_kind(
|
||||
i
|
||||
), f"selectedrealkind({i}): expected {selected_real_kind(i)!r} but got {selectedrealkind(i)!r}"
|
||||
@@ -0,0 +1,33 @@
|
||||
import os
|
||||
import textwrap
|
||||
import pytest
|
||||
|
||||
from numpy.testing import IS_PYPY
|
||||
from . import util
|
||||
|
||||
|
||||
class TestMixed(util.F2PyTest):
|
||||
sources = [
|
||||
util.getpath("tests", "src", "mixed", "foo.f"),
|
||||
util.getpath("tests", "src", "mixed", "foo_fixed.f90"),
|
||||
util.getpath("tests", "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 self.module.bar11.__doc__ == expected
|
||||
@@ -0,0 +1,27 @@
|
||||
import os
|
||||
import sys
|
||||
import pytest
|
||||
import textwrap
|
||||
|
||||
from . import util
|
||||
from numpy.testing import IS_PYPY
|
||||
|
||||
|
||||
class TestModuleDocString(util.F2PyTest):
|
||||
sources = [
|
||||
util.getpath("tests", "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 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""")
|
||||
@@ -0,0 +1,112 @@
|
||||
import os
|
||||
import pytest
|
||||
|
||||
import numpy as np
|
||||
|
||||
from . import util
|
||||
|
||||
|
||||
class TestParameters(util.F2PyTest):
|
||||
# Check that intent(in out) translates as intent(inout)
|
||||
sources = [
|
||||
util.getpath("tests", "src", "parameter", "constant_real.f90"),
|
||||
util.getpath("tests", "src", "parameter", "constant_integer.f90"),
|
||||
util.getpath("tests", "src", "parameter", "constant_both.f90"),
|
||||
util.getpath("tests", "src", "parameter", "constant_compound.f90"),
|
||||
util.getpath("tests", "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]
|
||||
pytest.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 np.allclose(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]
|
||||
pytest.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 np.allclose(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]
|
||||
pytest.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 np.allclose(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 np.allclose(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]
|
||||
pytest.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 np.allclose(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]
|
||||
pytest.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 np.allclose(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]
|
||||
pytest.raises(ValueError, self.module.foo, x)
|
||||
|
||||
# check values with contiguous array
|
||||
x = np.arange(3, dtype=np.float64)
|
||||
self.module.foo(x)
|
||||
assert np.allclose(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]
|
||||
pytest.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 np.allclose(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]
|
||||
pytest.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 np.allclose(x, [0 + 1 * 3 * 3 + 2 * 3 * 3, 1 * 3, 2 * 3])
|
||||
@@ -0,0 +1,16 @@
|
||||
"""See https://github.com/numpy/numpy/pull/10676.
|
||||
|
||||
"""
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
from . import util
|
||||
|
||||
|
||||
class TestQuotedCharacter(util.F2PyTest):
|
||||
sources = [util.getpath("tests", "src", "quoted_character", "foo.f")]
|
||||
|
||||
@pytest.mark.skipif(sys.platform == "win32",
|
||||
reason="Fails with MinGW64 Gfortran (Issue #9673)")
|
||||
def test_quoted_character(self):
|
||||
assert self.module.foo() == (b"'", b'"', b";", b"!", b"(", b")")
|
||||
@@ -0,0 +1,66 @@
|
||||
import os
|
||||
import pytest
|
||||
|
||||
import numpy as np
|
||||
|
||||
from . import util
|
||||
|
||||
|
||||
class TestIntentInOut(util.F2PyTest):
|
||||
# Check that intent(in out) translates as intent(inout)
|
||||
sources = [util.getpath("tests", "src", "regression", "inout.f90")]
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_inout(self):
|
||||
# non-contiguous should raise error
|
||||
x = np.arange(6, dtype=np.float32)[::2]
|
||||
pytest.raises(ValueError, self.module.foo, x)
|
||||
|
||||
# check values with contiguous array
|
||||
x = np.arange(3, dtype=np.float32)
|
||||
self.module.foo(x)
|
||||
assert np.allclose(x, [3, 1, 2])
|
||||
|
||||
|
||||
class TestNegativeBounds(util.F2PyTest):
|
||||
# Check that negative bounds work correctly
|
||||
sources = [util.getpath("tests", "src", "negative_bounds", "issue_20853.f90")]
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_negbound(self):
|
||||
xvec = np.arange(12)
|
||||
xlow = -6
|
||||
xhigh = 4
|
||||
# Calculate the upper bound,
|
||||
# Keeping the 1 index in mind
|
||||
def ubound(xl, xh):
|
||||
return xh - xl + 1
|
||||
rval = self.module.foo(is_=xlow, ie_=xhigh,
|
||||
arr=xvec[:ubound(xlow, xhigh)])
|
||||
expval = np.arange(11, dtype = np.float32)
|
||||
assert np.allclose(rval, expval)
|
||||
|
||||
|
||||
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 = [util.getpath("tests", "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__")
|
||||
|
||||
# Check that the attribute __f2py_numpy_version__ is a string
|
||||
assert isinstance(self.module.__f2py_numpy_version__, str)
|
||||
|
||||
# Check that __f2py_numpy_version__ has the value numpy.__version__
|
||||
assert 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
|
||||
@@ -0,0 +1,45 @@
|
||||
import pytest
|
||||
|
||||
from numpy import array
|
||||
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"
|
||||
r = t(array("ab"))
|
||||
assert r == b"a"
|
||||
r = t(array(77, "u1"))
|
||||
assert r == b"M"
|
||||
elif tname in ["ts", "ss"]:
|
||||
assert t(23) == b"23"
|
||||
assert t("123456789abcdef") == b"123456789a"
|
||||
elif tname in ["t5", "s5"]:
|
||||
assert t(23) == b"23"
|
||||
assert t("ab") == b"ab"
|
||||
assert t("123456789abcdef") == b"12345"
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class TestFReturnCharacter(TestReturnCharacter):
|
||||
sources = [
|
||||
util.getpath("tests", "src", "return_character", "foo77.f"),
|
||||
util.getpath("tests", "src", "return_character", "foo90.f90"),
|
||||
]
|
||||
|
||||
@pytest.mark.xfail(IS_S390X, reason="callback returns ' '")
|
||||
@pytest.mark.parametrize("name", "t0,t1,t5,s0,s1,s5,ss".split(","))
|
||||
def test_all_f77(self, name):
|
||||
self.check_function(getattr(self.module, name), name)
|
||||
|
||||
@pytest.mark.xfail(IS_S390X, reason="callback returns ' '")
|
||||
@pytest.mark.parametrize("name", "t0,t1,t5,ts,s0,s1,s5,ss".split(","))
|
||||
def test_all_f90(self, name):
|
||||
self.check_function(getattr(self.module.f90_return_char, name), name)
|
||||
@@ -0,0 +1,65 @@
|
||||
import pytest
|
||||
|
||||
from numpy import array
|
||||
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.0) <= err
|
||||
assert abs(t([234]) - 234.0) <= err
|
||||
assert abs(t((234, )) - 234.0) <= err
|
||||
assert abs(t(array(234)) - 234.0) <= err
|
||||
assert abs(t(array(23 + 4j, "F")) - (23 + 4j)) <= err
|
||||
assert abs(t(array([234])) - 234.0) <= err
|
||||
assert abs(t(array([[234]])) - 234.0) <= err
|
||||
assert abs(t(array([234]).astype("b")) + 22.0) <= err
|
||||
assert abs(t(array([234], "h")) - 234.0) <= err
|
||||
assert abs(t(array([234], "i")) - 234.0) <= err
|
||||
assert abs(t(array([234], "l")) - 234.0) <= err
|
||||
assert abs(t(array([234], "q")) - 234.0) <= err
|
||||
assert abs(t(array([234], "f")) - 234.0) <= err
|
||||
assert abs(t(array([234], "d")) - 234.0) <= err
|
||||
assert abs(t(array([234 + 3j], "F")) - (234 + 3j)) <= err
|
||||
assert abs(t(array([234], "D")) - 234.0) <= err
|
||||
|
||||
# pytest.raises(TypeError, t, array([234], 'a1'))
|
||||
pytest.raises(TypeError, t, "abc")
|
||||
|
||||
pytest.raises(IndexError, t, [])
|
||||
pytest.raises(IndexError, t, ())
|
||||
|
||||
pytest.raises(TypeError, t, t)
|
||||
pytest.raises(TypeError, t, {})
|
||||
|
||||
try:
|
||||
r = t(10**400)
|
||||
assert repr(r) in ["(inf+0j)", "(Infinity+0j)"]
|
||||
except OverflowError:
|
||||
pass
|
||||
|
||||
|
||||
class TestFReturnComplex(TestReturnComplex):
|
||||
sources = [
|
||||
util.getpath("tests", "src", "return_complex", "foo77.f"),
|
||||
util.getpath("tests", "src", "return_complex", "foo90.f90"),
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize("name", "t0,t8,t16,td,s0,s8,s16,sd".split(","))
|
||||
def test_all_f77(self, name):
|
||||
self.check_function(getattr(self.module, name), name)
|
||||
|
||||
@pytest.mark.parametrize("name", "t0,t8,t16,td,s0,s8,s16,sd".split(","))
|
||||
def test_all_f90(self, name):
|
||||
self.check_function(getattr(self.module.f90_return_complex, name),
|
||||
name)
|
||||
@@ -0,0 +1,55 @@
|
||||
import pytest
|
||||
|
||||
from numpy import array
|
||||
from . import util
|
||||
|
||||
|
||||
class TestReturnInteger(util.F2PyTest):
|
||||
def check_function(self, t, tname):
|
||||
assert t(123) == 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
|
||||
|
||||
# pytest.raises(ValueError, t, array([123],'S3'))
|
||||
pytest.raises(ValueError, t, "abc")
|
||||
|
||||
pytest.raises(IndexError, t, [])
|
||||
pytest.raises(IndexError, t, ())
|
||||
|
||||
pytest.raises(Exception, t, t)
|
||||
pytest.raises(Exception, t, {})
|
||||
|
||||
if tname in ["t8", "s8"]:
|
||||
pytest.raises(OverflowError, t, 100000000000000000000000)
|
||||
pytest.raises(OverflowError, t, 10000000011111111111111.23)
|
||||
|
||||
|
||||
class TestFReturnInteger(TestReturnInteger):
|
||||
sources = [
|
||||
util.getpath("tests", "src", "return_integer", "foo77.f"),
|
||||
util.getpath("tests", "src", "return_integer", "foo90.f90"),
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize("name",
|
||||
"t0,t1,t2,t4,t8,s0,s1,s2,s4,s8".split(","))
|
||||
def test_all_f77(self, name):
|
||||
self.check_function(getattr(self.module, name), name)
|
||||
|
||||
@pytest.mark.parametrize("name",
|
||||
"t0,t1,t2,t4,t8,s0,s1,s2,s4,s8".split(","))
|
||||
def test_all_f90(self, name):
|
||||
self.check_function(getattr(self.module.f90_return_integer, name),
|
||||
name)
|
||||
@@ -0,0 +1,64 @@
|
||||
import pytest
|
||||
|
||||
from numpy import array
|
||||
from . import util
|
||||
|
||||
|
||||
class TestReturnLogical(util.F2PyTest):
|
||||
def check_function(self, t):
|
||||
assert t(True) == 1
|
||||
assert t(False) == 0
|
||||
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([127], "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
|
||||
pytest.raises(ValueError, t, array([0, 0]))
|
||||
|
||||
|
||||
class TestFReturnLogical(TestReturnLogical):
|
||||
sources = [
|
||||
util.getpath("tests", "src", "return_logical", "foo77.f"),
|
||||
util.getpath("tests", "src", "return_logical", "foo90.f90"),
|
||||
]
|
||||
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.parametrize("name", "t0,t1,t2,t4,s0,s1,s2,s4".split(","))
|
||||
def test_all_f77(self, name):
|
||||
self.check_function(getattr(self.module, name))
|
||||
|
||||
@pytest.mark.slow
|
||||
@pytest.mark.parametrize("name",
|
||||
"t0,t1,t2,t4,t8,s0,s1,s2,s4,s8".split(","))
|
||||
def test_all_f90(self, name):
|
||||
self.check_function(getattr(self.module.f90_return_logical, name))
|
||||
@@ -0,0 +1,109 @@
|
||||
import platform
|
||||
import pytest
|
||||
import numpy as np
|
||||
|
||||
from numpy import array
|
||||
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.0) <= err
|
||||
assert abs(t(array(234)) - 234.0) <= err
|
||||
assert abs(t(array([234])) - 234.0) <= err
|
||||
assert abs(t(array([[234]])) - 234.0) <= err
|
||||
assert abs(t(array([234]).astype("b")) + 22) <= err
|
||||
assert abs(t(array([234], "h")) - 234.0) <= err
|
||||
assert abs(t(array([234], "i")) - 234.0) <= err
|
||||
assert abs(t(array([234], "l")) - 234.0) <= err
|
||||
assert abs(t(array([234], "B")) - 234.0) <= err
|
||||
assert abs(t(array([234], "f")) - 234.0) <= err
|
||||
assert abs(t(array([234], "d")) - 234.0) <= err
|
||||
if tname in ["t0", "t4", "s0", "s4"]:
|
||||
assert t(1e200) == t(1e300) # inf
|
||||
|
||||
# pytest.raises(ValueError, t, array([234], 'S1'))
|
||||
pytest.raises(ValueError, t, "abc")
|
||||
|
||||
pytest.raises(IndexError, t, [])
|
||||
pytest.raises(IndexError, t, ())
|
||||
|
||||
pytest.raises(Exception, t, t)
|
||||
pytest.raises(Exception, t, {})
|
||||
|
||||
try:
|
||||
r = t(10**400)
|
||||
assert repr(r) in ["inf", "Infinity"]
|
||||
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",
|
||||
)
|
||||
@pytest.mark.skipif(
|
||||
np.dtype(np.intp).itemsize < 8,
|
||||
reason="32-bit builds are buggy"
|
||||
)
|
||||
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 TestFReturnReal(TestReturnReal):
|
||||
sources = [
|
||||
util.getpath("tests", "src", "return_real", "foo77.f"),
|
||||
util.getpath("tests", "src", "return_real", "foo90.f90"),
|
||||
]
|
||||
|
||||
@pytest.mark.parametrize("name", "t0,t4,t8,td,s0,s4,s8,sd".split(","))
|
||||
def test_all_f77(self, name):
|
||||
self.check_function(getattr(self.module, name), name)
|
||||
|
||||
@pytest.mark.parametrize("name", "t0,t4,t8,td,s0,s4,s8,sd".split(","))
|
||||
def test_all_f90(self, name):
|
||||
self.check_function(getattr(self.module.f90_return_real, name), name)
|
||||
@@ -0,0 +1,74 @@
|
||||
import platform
|
||||
import pytest
|
||||
import numpy as np
|
||||
|
||||
from . import util
|
||||
|
||||
|
||||
@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",
|
||||
)
|
||||
@pytest.mark.skipif(
|
||||
np.dtype(np.intp).itemsize < 8,
|
||||
reason="32-bit builds are buggy"
|
||||
)
|
||||
class TestMultiline(util.F2PyTest):
|
||||
suffix = ".pyf"
|
||||
module_name = "multiline"
|
||||
code = f"""
|
||||
python module {module_name}
|
||||
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_name}
|
||||
"""
|
||||
|
||||
def test_multiline(self):
|
||||
assert 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",
|
||||
)
|
||||
@pytest.mark.skipif(
|
||||
np.dtype(np.intp).itemsize < 8,
|
||||
reason="32-bit builds are buggy"
|
||||
)
|
||||
class TestCallstatement(util.F2PyTest):
|
||||
suffix = ".pyf"
|
||||
module_name = "callstatement"
|
||||
code = f"""
|
||||
python module {module_name}
|
||||
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_name}
|
||||
"""
|
||||
|
||||
def test_callstatement(self):
|
||||
assert self.module.foo() == 42
|
||||
@@ -0,0 +1,45 @@
|
||||
import os
|
||||
import pytest
|
||||
import numpy as np
|
||||
|
||||
from . import util
|
||||
|
||||
|
||||
class TestSizeSumExample(util.F2PyTest):
|
||||
sources = [util.getpath("tests", "src", "size", "foo.f90")]
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_all(self):
|
||||
r = self.module.foo([[]])
|
||||
assert r == [0]
|
||||
|
||||
r = self.module.foo([[1, 2]])
|
||||
assert r == [3]
|
||||
|
||||
r = self.module.foo([[1, 2], [3, 4]])
|
||||
assert np.allclose(r, [3, 7])
|
||||
|
||||
r = self.module.foo([[1, 2], [3, 4], [5, 6]])
|
||||
assert np.allclose(r, [3, 7, 11])
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_transpose(self):
|
||||
r = self.module.trans([[]])
|
||||
assert np.allclose(r.T, np.array([[]]))
|
||||
|
||||
r = self.module.trans([[1, 2]])
|
||||
assert np.allclose(r, [[1.], [2.]])
|
||||
|
||||
r = self.module.trans([[1, 2, 3], [4, 5, 6]])
|
||||
assert np.allclose(r, [[1, 4], [2, 5], [3, 6]])
|
||||
|
||||
@pytest.mark.slow
|
||||
def test_flatten(self):
|
||||
r = self.module.flatten([[]])
|
||||
assert np.allclose(r, [])
|
||||
|
||||
r = self.module.flatten([[1, 2]])
|
||||
assert np.allclose(r, [1, 2])
|
||||
|
||||
r = self.module.flatten([[1, 2, 3], [4, 5, 6]])
|
||||
assert np.allclose(r, [1, 2, 3, 4, 5, 6])
|
||||
100
venv/lib/python3.9/site-packages/numpy/f2py/tests/test_string.py
Normal file
100
venv/lib/python3.9/site-packages/numpy/f2py/tests/test_string.py
Normal file
@@ -0,0 +1,100 @@
|
||||
import os
|
||||
import pytest
|
||||
import textwrap
|
||||
import numpy as np
|
||||
from . import util
|
||||
|
||||
|
||||
class TestString(util.F2PyTest):
|
||||
sources = [util.getpath("tests", "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 inp == pytest.approx(strings)
|
||||
expected = strings.copy()
|
||||
expected[1, :] = "AAA"
|
||||
assert out == pytest.approx(expected)
|
||||
|
||||
|
||||
class TestDocStringArguments(util.F2PyTest):
|
||||
sources = [util.getpath("tests", "src", "string", "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"
|
||||
assert c.tobytes() == b"123"
|
||||
assert d.tobytes() == b"D23"
|
||||
|
||||
|
||||
class TestFixedString(util.F2PyTest):
|
||||
sources = [util.getpath("tests", "src", "string", "fixed_string.f90")]
|
||||
|
||||
@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)
|
||||
@@ -0,0 +1,494 @@
|
||||
import pytest
|
||||
|
||||
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.0, 2.0)
|
||||
assert -c == as_complex(-1.0, -2.0)
|
||||
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)
|
||||
|
||||
pytest.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)}
|
||||
@@ -0,0 +1,14 @@
|
||||
import os
|
||||
import pytest
|
||||
|
||||
from . import util
|
||||
|
||||
class TestValueAttr(util.F2PyTest):
|
||||
sources = [util.getpath("tests", "src", "value_attrspec", "gh21665.f90")]
|
||||
|
||||
# gh-21665
|
||||
def test_long_long_map(self):
|
||||
inp = 2
|
||||
out = self.module.fortfuncs.square(inp)
|
||||
exp_out = 4
|
||||
assert out == exp_out
|
||||
419
venv/lib/python3.9/site-packages/numpy/f2py/tests/util.py
Normal file
419
venv/lib/python3.9/site-packages/numpy/f2py/tests/util.py
Normal file
@@ -0,0 +1,419 @@
|
||||
"""
|
||||
Utility functions for
|
||||
|
||||
- building and importing modules on test time, using a temporary location
|
||||
- detecting if compilers are present
|
||||
- determining paths to tests
|
||||
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
import tempfile
|
||||
import shutil
|
||||
import atexit
|
||||
import textwrap
|
||||
import re
|
||||
import pytest
|
||||
import contextlib
|
||||
import numpy
|
||||
|
||||
from pathlib import Path
|
||||
from numpy.compat import asbytes, asstr
|
||||
from numpy.testing import temppath, IS_WASM
|
||||
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
|
||||
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 = f"import sys; sys.path = {sys.path!r}; import numpy.f2py; numpy.f2py.main()"
|
||||
|
||||
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)
|
||||
|
||||
assert f2py_sources
|
||||
|
||||
# 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)
|
||||
if IS_WASM:
|
||||
# Can't run compiler from inside WASM.
|
||||
return _compiler_status
|
||||
|
||||
# XXX: this is really ugly. But I don't know how to invoke Distutils
|
||||
# in a safer way...
|
||||
code = textwrap.dedent(f"""\
|
||||
import os
|
||||
import sys
|
||||
sys.path = {repr(sys.path)}
|
||||
|
||||
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 = fr"""
|
||||
import os
|
||||
import sys
|
||||
sys.path = {repr(sys.path)}
|
||||
|
||||
def configuration(parent_name='',top_path=None):
|
||||
from numpy.distutils.misc_util import Configuration
|
||||
config = Configuration('', parent_name, top_path)
|
||||
{config_code}
|
||||
return config
|
||||
|
||||
if __name__ == "__main__":
|
||||
from numpy.distutils.core import setup
|
||||
setup(configuration=configuration)
|
||||
"""
|
||||
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
|
||||
|
||||
@property
|
||||
def module_name(self):
|
||||
cls = type(self)
|
||||
return f'_{cls.__module__.rsplit(".",1)[-1]}_{cls.__name__}_ext_module'
|
||||
|
||||
def setup_method(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
|
||||
needs_pyf = False
|
||||
for fn in codes:
|
||||
if str(fn).endswith(".f"):
|
||||
needs_f77 = True
|
||||
elif str(fn).endswith(".f90"):
|
||||
needs_f90 = True
|
||||
elif str(fn).endswith(".pyf"):
|
||||
needs_pyf = 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")
|
||||
if needs_pyf and not (has_f90_compiler() or has_f77_compiler()):
|
||||
pytest.skip("No Fortran 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,
|
||||
)
|
||||
|
||||
|
||||
#
|
||||
# Helper functions
|
||||
#
|
||||
|
||||
|
||||
def getpath(*a):
|
||||
# Package root
|
||||
d = Path(numpy.f2py.__file__).parent.resolve()
|
||||
return d.joinpath(*a)
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def switchdir(path):
|
||||
curpath = Path.cwd()
|
||||
os.chdir(path)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
os.chdir(curpath)
|
||||
Reference in New Issue
Block a user