Updated script that can be controled by Nodejs web app

This commit is contained in:
mac OS
2024-11-25 12:24:18 +07:00
parent c440eda1f4
commit 8b0ab2bd3a
8662 changed files with 1803808 additions and 34 deletions

View File

@ -0,0 +1,15 @@
from numpy.testing import IS_WASM, IS_EDITABLE
import pytest
if IS_WASM:
pytest.skip(
"WASM/Pyodide does not use or support Fortran",
allow_module_level=True
)
if IS_EDITABLE:
pytest.skip(
"Editable install doesn't support tests with a compile step",
allow_module_level=True
)

View File

@ -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

View File

@ -0,0 +1,6 @@
module test
abstract interface
subroutine foo()
end subroutine
end interface
end module test

View File

@ -0,0 +1,235 @@
/*
* 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),
PyDataType_ALIGNMENT(PyArray_DESCR(arr)),
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_LEGACY", NPY_NTYPES_LEGACY);
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
#if Py_GIL_DISABLED
// signal whether this module supports running with the GIL disabled
PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED);
#endif
return m;
}
#ifdef __cplusplus
}
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,6 @@
SUBROUTINE FOO()
INTEGER BAR(2, 3)
COMMON /BLOCK/ BAR
RETURN
END

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,10 @@
SUBROUTINE FOO(FUN,R)
EXTERNAL FUN
INTEGER I
REAL*8 R, FUN
Cf2py intent(out) r
R = 0D0
DO I=-5,5
R = R + FUN(I)
ENDDO
END

View File

@ -0,0 +1,18 @@
python module __user__routines
interface
function fun(i) result (r)
integer :: i
real*8 :: r
end function fun
end interface
end python module __user__routines
python module callback2
interface
subroutine foo(f,r)
use __user__routines, f=>fun
external f
real*8 intent(out) :: r
end subroutine foo
end interface
end python module callback2

View File

@ -0,0 +1,6 @@
python module test_22819
interface
subroutine hello()
end subroutine hello
end interface
end python module test_22819

View File

@ -0,0 +1,3 @@
SUBROUTINE HI
PRINT*, "HELLO WORLD"
END SUBROUTINE

View File

@ -0,0 +1,3 @@
function hi()
print*, "Hello World"
end function

View File

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

View File

@ -0,0 +1,10 @@
module typedefmod
use iso_fortran_env, only: real32
end module typedefmod
module data
use typedefmod, only: real32
implicit none
real(kind=real32) :: x
common/test/x
end module data

View File

@ -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

View File

@ -0,0 +1,8 @@
BLOCK DATA PARAM_INI
COMMON /MYCOM/ MYDATA
DATA MYDATA /0/
END
SUBROUTINE SUB1
COMMON /MYCOM/ MYDATA
MYDATA = MYDATA + 1
END

View File

@ -0,0 +1,5 @@
BLOCK DATA MYBLK
IMPLICIT DOUBLE PRECISION (A-H,O-Z)
COMMON /MYCOM/ IVAR1, IVAR2, IVAR3, IVAR4, EVAR5
DATA IVAR1, IVAR2, IVAR3, IVAR4, EVAR5 /2*3,2*2,0.0D0/
END

View File

@ -0,0 +1,20 @@
! gh-23276
module cmplxdat
implicit none
integer :: i, j
real :: x, y
real, dimension(2) :: z
real(kind=8) :: pi
complex(kind=8), target :: medium_ref_index
complex(kind=8), target :: ref_index_one, ref_index_two
complex(kind=8), dimension(2) :: my_array
real(kind=8), dimension(3) :: my_real_array = (/1.0d0, 2.0d0, 3.0d0/)
data i, j / 2, 3 /
data x, y / 1.5, 2.0 /
data z / 3.5, 7.0 /
data medium_ref_index / (1.d0, 0.d0) /
data ref_index_one, ref_index_two / (13.0d0, 21.0d0), (-30.0d0, 43.0d0) /
data my_array / (1.0d0, 2.0d0), (-3.0d0, 4.0d0) /
data pi / 3.1415926535897932384626433832795028841971693993751058209749445923078164062d0 /
end module cmplxdat

View File

@ -0,0 +1,8 @@
BLOCK DATA PARAM_INI
COMMON /MYCOM/ MYTAB
INTEGER MYTAB(3)
DATA MYTAB/
* 0, ! 1 and more commenty stuff
* 4, ! 2
* 0 /
END

View File

@ -0,0 +1,6 @@
module foo
type bar
character(len = 4) :: text
end type bar
type(bar), parameter :: abar = bar('abar')
end module foo

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,7 @@
python module iri16py ! in
interface ! in :iri16py
block data ! in :iri16py:iridreg_modified.for
COMMON /fircom/ eden,tabhe,tabla,tabmo,tabza,tabfl
end block data
end interface
end python module iri16py

View File

@ -0,0 +1,5 @@
SUBROUTINE EXAMPLE( )
IF( .TRUE. ) THEN
CALL DO_SOMETHING()
END IF ! ** .TRUE. **
END

View File

@ -0,0 +1,4 @@
integer function intproduct(a, b) result(res)
integer, intent(in) :: a, b
res = a*b
end function

View File

@ -0,0 +1,11 @@
module test_bug
implicit none
private
public :: intproduct
contains
integer function intproduct(a, b) result(res)
integer, intent(in) :: a, b
res = a*b
end function
end module

View File

@ -0,0 +1,20 @@
module gh23879
implicit none
private
public :: foo
contains
subroutine foo(a, b)
integer, intent(in) :: a
integer, intent(out) :: b
b = a
call bar(b)
end subroutine
subroutine bar(x)
integer, intent(inout) :: x
x = 2*x
end subroutine
end module gh23879

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,4 @@
subroutine foo(x)
real(8), intent(in) :: x
! Écrit à l'écran la valeur de x
end subroutine

View File

@ -0,0 +1 @@
dict(real=dict(real32='float', real64='double'), integer=dict(int64='long_long'))

View File

@ -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
!f2py intent(hide) :: n
res = sum(x)
end

View File

@ -0,0 +1,34 @@
module coddity
use iso_c_binding, only: c_double, c_int, c_int64_t
implicit none
contains
subroutine c_add(a, b, c) bind(c, name="c_add")
real(c_double), intent(in) :: a, b
real(c_double), intent(out) :: c
c = a + b
end subroutine c_add
! gh-9693
function wat(x, y) result(z) bind(c)
integer(c_int), intent(in) :: x, y
integer(c_int) :: z
z = x + 7
end function wat
! gh-25207
subroutine c_add_int64(a, b, c) bind(c)
integer(c_int64_t), intent(in) :: a, b
integer(c_int64_t), intent(out) :: c
c = a + b
end subroutine c_add_int64
! gh-25207
subroutine add_arr(A, B, C)
integer(c_int64_t), intent(in) :: A(3)
integer(c_int64_t), intent(in) :: B(3)
integer(c_int64_t), intent(out) :: C(3)
integer :: j
do j = 1, 3
C(j) = A(j)+B(j)
end do
end subroutine
end module coddity

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,8 @@
module data
real(8) :: shift
contains
subroutine set_shift(in_shift)
real(8), intent(in) :: in_shift
shift = in_shift
end subroutine set_shift
end module data

View File

@ -0,0 +1,6 @@
subroutine shift_a(dim_a, a)
use data, only: shift
integer, intent(in) :: dim_a
real(8), intent(inout), dimension(dim_a) :: a
a = a + shift
end subroutine shift_a

View File

@ -0,0 +1,21 @@
module mod2
implicit none
private mod2_func1
contains
subroutine mod2_func1()
print*, "mod2_func1"
end subroutine mod2_func1
end module mod2
module mod1
implicit none
private :: mod1_func1
contains
subroutine mod1_func1()
print*, "mod1_func1"
end subroutine mod1_func1
end module mod1

View File

@ -0,0 +1,21 @@
module mod2
implicit none
PUBLIC :: mod2_func1
contains
subroutine mod2_func1()
print*, "mod2_func1"
end subroutine mod2_func1
end module mod2
module mod1
implicit none
PUBLIC :: mod1_func1
contains
subroutine mod1_func1()
print*, "mod1_func1"
end subroutine mod1_func1
end module mod1

View File

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

View File

@ -0,0 +1,20 @@
module mathops
implicit none
contains
function add(a, b) result(c)
integer, intent(in) :: a, b
integer :: c
c = a + b
end function add
end module mathops
module useops
use mathops, only: add
implicit none
contains
function sum_and_double(a, b) result(d)
integer, intent(in) :: a, b
integer :: d
d = 2 * add(a, b)
end function sum_and_double
end module useops

View File

@ -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

View File

@ -0,0 +1,45 @@
! Check that parameter arrays are correctly intercepted.
subroutine foo_array(x, y, z)
implicit none
integer, parameter :: dp = selected_real_kind(15)
integer, parameter :: pa = 2
integer, parameter :: intparamarray(2) = (/ 3, 5 /)
integer, dimension(pa), parameter :: pb = (/ 2, 10 /)
integer, parameter, dimension(intparamarray(1)) :: pc = (/ 2, 10, 20 /)
real(dp), parameter :: doubleparamarray(3) = (/ 3.14_dp, 4._dp, 6.44_dp /)
real(dp), intent(inout) :: x(intparamarray(1))
real(dp), intent(inout) :: y(intparamarray(2))
real(dp), intent(out) :: z
x = x/pb(2)
y = y*pc(2)
z = doubleparamarray(1)*doubleparamarray(2) + doubleparamarray(3)
return
end subroutine
subroutine foo_array_any_index(x, y)
implicit none
integer, parameter :: dp = selected_real_kind(15)
integer, parameter, dimension(-1:1) :: myparamarray = (/ 6, 3, 1 /)
integer, parameter, dimension(2) :: nested = (/ 2, 0 /)
integer, parameter :: dim = 2
real(dp), intent(in) :: x(myparamarray(-1))
real(dp), intent(out) :: y(nested(1), myparamarray(nested(dim)))
y = reshape(x, (/nested(1), myparamarray(nested(2))/))
return
end subroutine
subroutine foo_array_delims(x)
implicit none
integer, parameter :: dp = selected_real_kind(15)
integer, parameter, dimension(2) :: myparamarray = (/ (6), 1 /)
integer, parameter, dimension(3) :: test = (/2, 1, (3)/)
real(dp), intent(out) :: x
x = myparamarray(1)+test(3)
return
end subroutine

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,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

View File

@ -0,0 +1 @@
real(8) b, n, m

View File

@ -0,0 +1,26 @@
SUBROUTINE TESTSUB(
& INPUT1, INPUT2, !Input
& OUTPUT1, OUTPUT2) !Output
IMPLICIT NONE
INTEGER, INTENT(IN) :: INPUT1, INPUT2
INTEGER, INTENT(OUT) :: OUTPUT1, OUTPUT2
OUTPUT1 = INPUT1 + INPUT2
OUTPUT2 = INPUT1 * INPUT2
RETURN
END SUBROUTINE TESTSUB
SUBROUTINE TESTSUB2(OUTPUT)
IMPLICIT NONE
INTEGER, PARAMETER :: N = 10 ! Array dimension
REAL, INTENT(OUT) :: OUTPUT(N)
INTEGER :: I
DO I = 1, N
OUTPUT(I) = I * 2.0
END DO
RETURN
END

View File

@ -0,0 +1,5 @@
C This is an invalid file, but it does compile with -ffixed-form
subroutine mwe(
& x)
real x
end subroutine mwe

View File

@ -0,0 +1,9 @@
SUBROUTINE TESTSUB(INPUT1, & ! Hello
! commenty
INPUT2, OUTPUT1, OUTPUT2) ! more comments
INTEGER, INTENT(IN) :: INPUT1, INPUT2
INTEGER, INTENT(OUT) :: OUTPUT1, OUTPUT2
OUTPUT1 = INPUT1 + &
INPUT2
OUTPUT2 = INPUT1 * INPUT2
END SUBROUTINE TESTSUB

View File

@ -0,0 +1,5 @@
function add(n,m) result(b)
implicit none
include 'AB.inc'
b = n + m
end function add

View File

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

View File

@ -0,0 +1,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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

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

View File

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

View File

@ -0,0 +1,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

View File

@ -0,0 +1,8 @@
SUBROUTINE GREET(NAME, GREETING)
CHARACTER NAME*(*), GREETING*(*)
CHARACTER*(50) MESSAGE
MESSAGE = 'Hello, ' // NAME // ', ' // GREETING
c$$$ PRINT *, MESSAGE
END SUBROUTINE GREET

View File

@ -0,0 +1,7 @@
subroutine string_inout_optional(output)
implicit none
character*(32), optional, intent(inout) :: output
if (present(output)) then
output="output string"
endif
end subroutine

View File

@ -0,0 +1,14 @@
subroutine charint(trans, info)
character, intent(in) :: trans
integer, intent(out) :: info
if (trans == 'N') then
info = 1
else if (trans == 'T') then
info = 2
else if (trans == 'C') then
info = 3
else
info = -1
end if
end subroutine charint

View File

@ -0,0 +1,12 @@
python module _char_handling_test
interface
subroutine charint(trans, info)
callstatement (*f2py_func)(&trans, &info)
callprotoargument char*, int*
character, intent(in), check(trans=='N'||trans=='T'||trans=='C') :: trans = 'N'
integer intent(out) :: info
end subroutine charint
end interface
end python module _char_handling_test

View File

@ -0,0 +1,12 @@
python module _char_handling_test
interface
subroutine charint(trans, info)
callstatement (*f2py_func)(&trans, &info)
callprotoargument char*, int*
character, intent(in), check(*trans=='N'||*trans=='T'||*trans=='C') :: trans = 'N'
integer intent(out) :: info
end subroutine charint
end interface
end python module _char_handling_test

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,26 @@
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")
@pytest.mark.slow
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"

View File

@ -0,0 +1,679 @@
import os
import sys
import copy
import platform
import pytest
from pathlib import Path
import numpy as np
from numpy.testing import assert_, assert_equal
from numpy._core._type_aliases import c_names_dict as _c_names_dict
from . import util
wrap = None
# Extend core typeinfo with CHARACTER to test dtype('c')
c_names_dict = dict(
CHARACTER=np.dtype("c"),
**_c_names_dict
)
def get_testdir():
testroot = Path(__file__).resolve().parent / "src"
return testroot / "array_from_pyobj"
def setup_module():
"""
Build the required testing extension module
"""
global wrap
if wrap is None:
src = [
get_testdir() / "wrapmodule.c",
]
wrap = util.build_meson(src, module_name = "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):
return all(name in self.intent_list for name in names)
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 c_names_dict.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 = c_names_dict[self.NAME]
self.type_num = getattr(wrap, 'NPY_STRING')
self.elsize = 1
self.dtype = np.dtype('c')
elif self.NAME.startswith('STRING'):
info = c_names_dict[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 = c_names_dict[self.NAME]
self.type_num = getattr(wrap, 'NPY_' + self.NAME)
self.elsize = info.itemsize
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 = c_names_dict[self.NAME].alignment
types = []
for name in _type_names:
if c_names_dict[name].alignment < bits:
types.append(Type(name))
return types
def equal_types(self):
bits = c_names_dict[self.NAME].alignment
types = []
for name in _type_names:
if name == self.NAME:
continue
if c_names_dict[name].alignment == bits:
types.append(Type(name))
return types
def larger_types(self):
bits = c_names_dict[self.NAME].alignment
types = []
for name in _type_names:
if c_names_dict[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
is_int = np.issubdtype(t.dtype, np.integer)
if is_int and int(self.num2seq[0]) > np.iinfo(t.dtype).max:
# skip test if num2seq would trigger an overflow error
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!

View File

@ -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)

View File

@ -0,0 +1,18 @@
import sys
import pytest
from . import util
from numpy.testing import IS_PYPY
@pytest.mark.slow
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

View File

@ -0,0 +1,246 @@
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(","))
@pytest.mark.slow
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")]
@pytest.mark.slow
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")]
@pytest.mark.slow
def test_gh18335(self):
def foo(x):
x[0] += 1
r = self.module.gh18335(foo)
assert r == 123 + 1
class TestGH25211(util.F2PyTest):
sources = [util.getpath("tests", "src", "callback", "gh25211.f"),
util.getpath("tests", "src", "callback", "gh25211.pyf")]
module_name = "callback2"
def test_gh25211(self):
def bar(x):
return x*x
res = self.module.foo(bar)
assert res == 110

View File

@ -0,0 +1,639 @@
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
@pytest.mark.slow
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
! Uncomment for debug printing:
!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
""")
@pytest.mark.slow
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")]
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
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
class TestStringAssumedLength(util.F2PyTest):
sources = [util.getpath("tests", "src", "string", "gh24008.f")]
def test_gh24008(self):
self.module.greet("joe", "bob")
@pytest.mark.slow
class TestStringOptionalInOut(util.F2PyTest):
sources = [util.getpath("tests", "src", "string", "gh24662.f90")]
def test_gh24662(self):
self.module.string_inout_optional()
a = np.array('hi', dtype='S32')
self.module.string_inout_optional(a)
assert "output string" in a.tobytes().decode()
with pytest.raises(Exception):
aa = "Hi"
self.module.string_inout_optional(aa)
@pytest.mark.slow
class TestNewCharHandling(util.F2PyTest):
# from v1.24 onwards, gh-19388
sources = [
util.getpath("tests", "src", "string", "gh25286.pyf"),
util.getpath("tests", "src", "string", "gh25286.f90")
]
module_name = "_char_handling_test"
def test_gh25286(self):
info = self.module.charint('T')
assert info == 2
@pytest.mark.slow
class TestBCCharHandling(util.F2PyTest):
# SciPy style, "incorrect" bindings with a hook
sources = [
util.getpath("tests", "src", "string", "gh25286_bc.pyf"),
util.getpath("tests", "src", "string", "gh25286.f90")
]
module_name = "_char_handling_test"
def test_gh25286(self):
info = self.module.charint('T')
assert info == 2

View File

@ -0,0 +1,20 @@
import pytest
import numpy as np
from . import util
@pytest.mark.slow
class TestCommonBlock(util.F2PyTest):
sources = [util.getpath("tests", "src", "common", "block.f")]
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)
class TestCommonWithUse(util.F2PyTest):
sources = [util.getpath("tests", "src", "common", "gh19161.f90")]
def test_common_gh19161(self):
assert self.module.data.x == 0

View File

@ -0,0 +1,407 @@
import importlib
import codecs
import time
import unicodedata
import pytest
import numpy as np
from numpy.f2py.crackfortran import markinnerspaces, nameargspattern
from . import util
from numpy.f2py import crackfortran
import textwrap
import contextlib
import io
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'}
def test_nowrap_private_proceedures(self, tmp_path):
fpath = util.getpath("tests", "src", "crackfortran", "gh23879.f90")
mod = crackfortran.crackfortran([str(fpath)])
assert len(mod) == 1
pyf = crackfortran.crack2fortran(mod)
assert 'bar' not in pyf
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)
@pytest.mark.slow
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'
@pytest.mark.slow
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)
class TestNameArgsPatternBacktracking:
@pytest.mark.parametrize(
['adversary'],
[
('@)@bind@(@',),
('@)@bind @(@',),
('@)@bind foo bar baz@(@',)
]
)
def test_nameargspattern_backtracking(self, adversary):
'''address ReDOS vulnerability:
https://github.com/numpy/numpy/issues/23338'''
trials_per_batch = 12
batches_per_regex = 4
start_reps, end_reps = 15, 25
for ii in range(start_reps, end_reps):
repeated_adversary = adversary * ii
# test times in small batches.
# this gives us more chances to catch a bad regex
# while still catching it before too long if it is bad
for _ in range(batches_per_regex):
times = []
for _ in range(trials_per_batch):
t0 = time.perf_counter()
mtch = nameargspattern.search(repeated_adversary)
times.append(time.perf_counter() - t0)
# our pattern should be much faster than 0.2s per search
# it's unlikely that a bad regex will pass even on fast CPUs
assert np.median(times) < 0.2
assert not mtch
# if the adversary is capped with @)@, it becomes acceptable
# according to the old version of the regex.
# that should still be true.
good_version_of_adversary = repeated_adversary + '@)@'
assert nameargspattern.search(good_version_of_adversary)
class TestFunctionReturn(util.F2PyTest):
sources = [util.getpath("tests", "src", "crackfortran", "gh23598.f90")]
@pytest.mark.slow
def test_function_rettype(self):
# gh-23598
assert self.module.intproduct(3, 4) == 12
class TestFortranGroupCounters(util.F2PyTest):
def test_end_if_comment(self):
# gh-23533
fpath = util.getpath("tests", "src", "crackfortran", "gh23533.f")
try:
crackfortran.crackfortran([str(fpath)])
except Exception as exc:
assert False, f"'crackfortran.crackfortran' raised an exception {exc}"
class TestF77CommonBlockReader:
def test_gh22648(self, tmp_path):
fpath = util.getpath("tests", "src", "crackfortran", "gh22648.pyf")
with contextlib.redirect_stdout(io.StringIO()) as stdout_f2py:
mod = crackfortran.crackfortran([str(fpath)])
assert "Mismatch" not in stdout_f2py.getvalue()
class TestParamEval:
# issue gh-11612, array parameter parsing
def test_param_eval_nested(self):
v = '(/3.14, 4./)'
g_params = dict(kind=crackfortran._kind_func,
selected_int_kind=crackfortran._selected_int_kind_func,
selected_real_kind=crackfortran._selected_real_kind_func)
params = {'dp': 8, 'intparamarray': {1: 3, 2: 5},
'nested': {1: 1, 2: 2, 3: 3}}
dimspec = '(2)'
ret = crackfortran.param_eval(v, g_params, params, dimspec=dimspec)
assert ret == {1: 3.14, 2: 4.0}
def test_param_eval_nonstandard_range(self):
v = '(/ 6, 3, 1 /)'
g_params = dict(kind=crackfortran._kind_func,
selected_int_kind=crackfortran._selected_int_kind_func,
selected_real_kind=crackfortran._selected_real_kind_func)
params = {}
dimspec = '(-1:1)'
ret = crackfortran.param_eval(v, g_params, params, dimspec=dimspec)
assert ret == {-1: 6, 0: 3, 1: 1}
def test_param_eval_empty_range(self):
v = '6'
g_params = dict(kind=crackfortran._kind_func,
selected_int_kind=crackfortran._selected_int_kind_func,
selected_real_kind=crackfortran._selected_real_kind_func)
params = {}
dimspec = ''
pytest.raises(ValueError, crackfortran.param_eval, v, g_params, params,
dimspec=dimspec)
def test_param_eval_non_array_param(self):
v = '3.14_dp'
g_params = dict(kind=crackfortran._kind_func,
selected_int_kind=crackfortran._selected_int_kind_func,
selected_real_kind=crackfortran._selected_real_kind_func)
params = {}
ret = crackfortran.param_eval(v, g_params, params, dimspec=None)
assert ret == '3.14_dp'
def test_param_eval_too_many_dims(self):
v = 'reshape((/ (i, i=1, 250) /), (/5, 10, 5/))'
g_params = dict(kind=crackfortran._kind_func,
selected_int_kind=crackfortran._selected_int_kind_func,
selected_real_kind=crackfortran._selected_real_kind_func)
params = {}
dimspec = '(0:4, 3:12, 5)'
pytest.raises(ValueError, crackfortran.param_eval, v, g_params, params,
dimspec=dimspec)

View File

@ -0,0 +1,71 @@
import os
import pytest
import numpy as np
from . import util
from numpy.f2py.crackfortran import crackfortran
class TestData(util.F2PyTest):
sources = [util.getpath("tests", "src", "crackfortran", "data_stmts.f90")]
# For gh-23276
@pytest.mark.slow
def test_data_stmts(self):
assert self.module.cmplxdat.i == 2
assert self.module.cmplxdat.j == 3
assert self.module.cmplxdat.x == 1.5
assert self.module.cmplxdat.y == 2.0
assert self.module.cmplxdat.pi == 3.1415926535897932384626433832795028841971693993751058209749445923078164062
assert self.module.cmplxdat.medium_ref_index == np.array(1.+0.j)
assert np.all(self.module.cmplxdat.z == np.array([3.5, 7.0]))
assert np.all(self.module.cmplxdat.my_array == np.array([ 1.+2.j, -3.+4.j]))
assert np.all(self.module.cmplxdat.my_real_array == np.array([ 1., 2., 3.]))
assert np.all(self.module.cmplxdat.ref_index_one == np.array([13.0 + 21.0j]))
assert np.all(self.module.cmplxdat.ref_index_two == np.array([-30.0 + 43.0j]))
def test_crackedlines(self):
mod = crackfortran(self.sources)
assert mod[0]['vars']['x']['='] == '1.5'
assert mod[0]['vars']['y']['='] == '2.0'
assert mod[0]['vars']['pi']['='] == '3.1415926535897932384626433832795028841971693993751058209749445923078164062d0'
assert mod[0]['vars']['my_real_array']['='] == '(/1.0d0, 2.0d0, 3.0d0/)'
assert mod[0]['vars']['ref_index_one']['='] == '(13.0d0, 21.0d0)'
assert mod[0]['vars']['ref_index_two']['='] == '(-30.0d0, 43.0d0)'
assert mod[0]['vars']['my_array']['='] == '(/(1.0d0, 2.0d0), (-3.0d0, 4.0d0)/)'
assert mod[0]['vars']['z']['='] == '(/3.5, 7.0/)'
class TestDataF77(util.F2PyTest):
sources = [util.getpath("tests", "src", "crackfortran", "data_common.f")]
# For gh-23276
def test_data_stmts(self):
assert self.module.mycom.mydata == 0
def test_crackedlines(self):
mod = crackfortran(str(self.sources[0]))
print(mod[0]['vars'])
assert mod[0]['vars']['mydata']['='] == '0'
class TestDataMultiplierF77(util.F2PyTest):
sources = [util.getpath("tests", "src", "crackfortran", "data_multiplier.f")]
# For gh-23276
def test_data_stmts(self):
assert self.module.mycom.ivar1 == 3
assert self.module.mycom.ivar2 == 3
assert self.module.mycom.ivar3 == 2
assert self.module.mycom.ivar4 == 2
assert self.module.mycom.evar5 == 0
class TestDataWithCommentsF77(util.F2PyTest):
sources = [util.getpath("tests", "src", "crackfortran", "data_with_comments.f")]
# For gh-23276
def test_data_stmts(self):
assert len(self.module.mycom.mytab) == 3
assert self.module.mycom.mytab[0] == 0
assert self.module.mycom.mytab[1] == 4
assert self.module.mycom.mytab[2] == 0

View File

@ -0,0 +1,59 @@
import pytest
import numpy as np
from numpy.testing import assert_array_equal, assert_equal
from . import util
from pathlib import Path
def get_docdir():
parents = Path(__file__).resolve().parents
try:
# Assumes that spin is used to run tests
nproot = parents[8]
except IndexError:
docdir = None
else:
docdir = nproot / "doc" / "source" / "f2py" / "code"
if docdir and docdir.is_dir():
return docdir
# Assumes that an editable install is used to run tests
return parents[3] / "doc" / "source" / "f2py" / "code"
pytestmark = pytest.mark.skipif(
not get_docdir().is_dir(),
reason=f"Could not find f2py documentation sources"
f"({get_docdir()} does not exist)",
)
def _path(*args):
return get_docdir().joinpath(*args)
@pytest.mark.slow
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(20), 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

View File

@ -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_gh15095(self):
inp = np.ones(3)
out = self.module.func1(inp)
exp_out = 3
assert out == exp_out

View File

@ -0,0 +1,959 @@
import textwrap, re, sys, subprocess, shlex
from pathlib import Path
from collections import namedtuple
import platform
import pytest
from . import util
from numpy.f2py.f2py2e import main as f2pycli
from numpy.testing._private.utils import NOGIL_BUILD
#######################
# F2PY Test utilities #
######################
# Tests for CLI commands which call meson will fail if no compilers are present, these are to be skipped
def compiler_check_f2pycli():
if not util.has_fortran_compiler():
pytest.skip("CLI command needs a Fortran compiler")
else:
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 #
################
@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 gh23598_warn(tmpdir_factory):
"""F90 file for testing warnings in gh23598"""
fdat = util.getpath("tests", "src", "crackfortran", "gh23598Warn.f90").read_text()
fn = tmpdir_factory.getbasetemp() / "gh23598Warn.f90"
fn.write_text(fdat, encoding="ascii")
return fn
@pytest.fixture(scope="session")
def gh22819_cli(tmpdir_factory):
"""F90 file for testing disallowed CLI arguments in ghff819"""
fdat = util.getpath("tests", "src", "cli", "gh_22819.pyf").read_text()
fn = tmpdir_factory.getbasetemp() / "gh_22819.pyf"
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
#########
# Tests #
#########
def test_gh22819_cli(capfd, gh22819_cli, monkeypatch):
"""Check that module names are handled correctly
gh-22819
Essentially, the -m name cannot be used to import the module, so the module
named in the .pyf needs to be used instead
CLI :: -m and a .pyf file
"""
ipath = Path(gh22819_cli)
monkeypatch.setattr(sys, "argv", f"f2py -m blah {ipath}".split())
with util.switchdir(ipath.parent):
f2pycli()
gen_paths = [item.name for item in ipath.parent.rglob("*") if item.is_file()]
assert "blahmodule.c" not in gen_paths # shouldn't be generated
assert "blah-f2pywrappers.f" not in gen_paths
assert "test_22819-f2pywrappers.f" in gen_paths
assert "test_22819module.c" in gen_paths
assert "Ignoring blah"
def test_gh22819_many_pyf(capfd, gh22819_cli, monkeypatch):
"""Only one .pyf file allowed
gh-22819
CLI :: .pyf files
"""
ipath = Path(gh22819_cli)
monkeypatch.setattr(sys, "argv", f"f2py -m blah {ipath} hello.pyf".split())
with util.switchdir(ipath.parent):
with pytest.raises(ValueError, match="Only one .pyf file per call"):
f2pycli()
def test_gh23598_warn(capfd, gh23598_warn, monkeypatch):
foutl = get_io_paths(gh23598_warn, mname="test")
ipath = foutl.f90inp
monkeypatch.setattr(
sys, "argv",
f'f2py {ipath} -m test'.split())
with util.switchdir(ipath.parent):
f2pycli() # Generate files
wrapper = foutl.wrap90.read_text()
assert "intproductf2pywrap, intpr" not in wrapper
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.skipif(sys.version_info <= (3, 12), reason="Python 3.12 required")
def test_untitled_cli(capfd, hello_world_f90, monkeypatch):
"""Check that modules are named correctly
CLI :: defaults
"""
ipath = Path(hello_world_f90)
monkeypatch.setattr(sys, "argv", f"f2py --backend meson -c {ipath}".split())
with util.switchdir(ipath.parent):
compiler_check_f2pycli()
out, _ = capfd.readouterr()
assert "untitledmodule.c" in out
@pytest.mark.skipif((platform.system() != 'Linux') or (sys.version_info <= (3, 12)), reason='Compiler and 3.12 required')
def test_no_py312_distutils_fcompiler(capfd, hello_world_f90, monkeypatch):
"""Check that no distutils imports are performed on 3.12
CLI :: --fcompiler --help-link --backend distutils
"""
MNAME = "hi"
foutl = get_io_paths(hello_world_f90, mname=MNAME)
ipath = foutl.f90inp
monkeypatch.setattr(
sys, "argv", f"f2py {ipath} -c --fcompiler=gfortran -m {MNAME}".split()
)
with util.switchdir(ipath.parent):
compiler_check_f2pycli()
out, _ = capfd.readouterr()
assert "--fcompiler cannot be used with meson" in out
monkeypatch.setattr(
sys, "argv", f"f2py --help-link".split()
)
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert "Use --dep for meson builds" in out
MNAME = "hi2" # Needs to be different for a new -c
monkeypatch.setattr(
sys, "argv", f"f2py {ipath} -c -m {MNAME} --backend distutils".split()
)
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert "Cannot use distutils backend with Python>=3.12" in out
@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_mod_gen_gh25263(capfd, hello_world_f77, monkeypatch):
"""Check that pyf files are correctly generated with module structure
CLI :: -m <name> -h pyf_file
BUG: numpy-gh #20520
"""
MNAME = "hi"
foutl = get_io_paths(hello_world_f77, mname=MNAME)
ipath = foutl.finp
monkeypatch.setattr(sys, "argv", f'f2py {ipath} -m {MNAME} -h hi.pyf'.split())
with util.switchdir(ipath.parent):
f2pycli()
with Path('hi.pyf').open() as hipyf:
pyfdat = hipyf.read()
assert "python module hi" in pyfdat
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.skip(reason="Consistently fails on CI; noisy so skip not xfail.")
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(f"{sys.executable} -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.skip(reason="Consistently fails on CI; noisy so skip not xfail.")
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(f"{sys.executable} -c \"import blah; blah.hi()\"")
rout = subprocess.run(cmd_run, capture_output=True, encoding='UTF-8')
eout = ' Hello World\n'
assert rout.stdout == eout
@pytest.mark.skipif((platform.system() != 'Linux') or sys.version_info <= (3, 12),
reason='Compiler and Python 3.12 or newer required')
def test_no_freethreading_compatible(hello_world_f90, monkeypatch):
"""
CLI :: --no-freethreading-compatible
"""
ipath = Path(hello_world_f90)
monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} -c --no-freethreading-compatible'.split())
with util.switchdir(ipath.parent):
compiler_check_f2pycli()
cmd = f"{sys.executable} -c \"import blah; blah.hi();"
if NOGIL_BUILD:
cmd += "import sys; assert sys._is_gil_enabled() is True\""
else:
cmd += "\""
cmd_run = shlex.split(cmd)
rout = subprocess.run(cmd_run, capture_output=True, encoding='UTF-8')
eout = ' Hello World\n'
assert rout.stdout == eout
if NOGIL_BUILD:
assert "The global interpreter lock (GIL) has been enabled to load module 'blah'" in rout.stderr
assert rout.returncode == 0
@pytest.mark.skipif((platform.system() != 'Linux') or sys.version_info <= (3, 12),
reason='Compiler and Python 3.12 or newer required')
def test_freethreading_compatible(hello_world_f90, monkeypatch):
"""
CLI :: --freethreading_compatible
"""
ipath = Path(hello_world_f90)
monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} -c --freethreading-compatible'.split())
with util.switchdir(ipath.parent):
compiler_check_f2pycli()
cmd = f"{sys.executable} -c \"import blah; blah.hi();"
if NOGIL_BUILD:
cmd += "import sys; assert sys._is_gil_enabled() is False\""
else:
cmd += "\""
cmd_run = shlex.split(cmd)
rout = subprocess.run(cmd_run, capture_output=True, encoding='UTF-8')
eout = ' Hello World\n'
assert rout.stdout == eout
assert rout.stderr == ""
assert rout.returncode == 0
# 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

View File

@ -0,0 +1,53 @@
from . import util
import numpy as np
import pytest
from numpy.testing import assert_allclose
class TestISOC(util.F2PyTest):
sources = [
util.getpath("tests", "src", "isocintrin", "isoCtests.f90"),
]
# gh-24553
@pytest.mark.slow
def test_c_double(self):
out = self.module.coddity.c_add(1, 2)
exp_out = 3
assert out == exp_out
# gh-9693
def test_bindc_function(self):
out = self.module.coddity.wat(1, 20)
exp_out = 8
assert out == exp_out
# gh-25207
def test_bindc_kinds(self):
out = self.module.coddity.c_add_int64(1, 20)
exp_out = 21
assert out == exp_out
# gh-25207
def test_bindc_add_arr(self):
a = np.array([1,2,3])
b = np.array([1,2,3])
out = self.module.coddity.add_arr(a, b)
exp_out = a*2
assert_allclose(out, exp_out)
def test_process_f2cmap_dict():
from numpy.f2py.auxfuncs import process_f2cmap_dict
f2cmap_all = {"integer": {"8": "rubbish_type"}}
new_map = {"INTEGER": {"4": "int"}}
c2py_map = {"int": "int", "rubbish_type": "long"}
exp_map, exp_maptyp = ({"integer": {"8": "rubbish_type", "4": "int"}}, ["int"])
# Call the function
res_map, res_maptyp = process_f2cmap_dict(f2cmap_all, new_map, c2py_map)
# Assert the result is as expected
assert res_map == exp_map
assert res_maptyp == exp_maptyp

Some files were not shown because too many files have changed in this diff Show More