Adding all project files

This commit is contained in:
Martina Burlando 2025-08-02 02:00:33 +02:00
parent 6c9e127bdc
commit cd4316ad0f
42289 changed files with 8009643 additions and 0 deletions

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,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,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,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,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,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 @@
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
Cf2py intent(hide) :: n
res = sum(x)
end

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,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,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,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,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,22 @@
from pathlib import Path
import textwrap
from . import util
from numpy.f2py import crackfortran
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,627 @@
import os
import sys
import copy
import platform
import pytest
import numpy as np
from numpy.core.multiarray import typeinfo
from . import util
wrap = None
def setup_module():
"""
Build the required testing extension module
"""
global wrap
# Check compiler availability first
if not util.has_c_compiler():
pytest.skip("No C compiler available")
if wrap is None:
config_code = """
config.add_extension('test_array_from_pyobj_ext',
sources=['wrapmodule.c', 'fortranobject.c'],
define_macros=[])
"""
d = os.path.dirname(__file__)
src = [
util.getpath("tests", "src", "array_from_pyobj", "wrapmodule.c"),
util.getpath("src", "fortranobject.c"),
util.getpath("src", "fortranobject.h"),
]
wrap = util.build_module_distutils(src, config_code,
"test_array_from_pyobj_ext")
def flags_info(arr):
flags = wrap.array_attrs(arr)[6]
return flags2names(flags)
def flags2names(flags):
info = []
for flagname in [
"CONTIGUOUS",
"FORTRAN",
"OWNDATA",
"ENSURECOPY",
"ENSUREARRAY",
"ALIGNED",
"NOTSWAPPED",
"WRITEABLE",
"WRITEBACKIFCOPY",
"BEHAVED",
"BEHAVED_RO",
"CARRAY",
"FARRAY",
]:
if abs(flags) & getattr(wrap, flagname, 0):
info.append(flagname)
return info
class Intent:
def __init__(self, intent_list=[]):
self.intent_list = intent_list[:]
flags = 0
for i in intent_list:
if i == "optional":
flags |= wrap.F2PY_OPTIONAL
else:
flags |= getattr(wrap, "F2PY_INTENT_" + i.upper())
self.flags = flags
def __getattr__(self, name):
name = name.lower()
if name == "in_":
name = "in"
return self.__class__(self.intent_list + [name])
def __str__(self):
return "intent(%s)" % (",".join(self.intent_list))
def __repr__(self):
return "Intent(%r)" % (self.intent_list)
def is_intent(self, *names):
for name in names:
if name not in self.intent_list:
return False
return True
def is_intent_exact(self, *names):
return len(self.intent_list) == len(names) and self.is_intent(*names)
intent = Intent()
_type_names = [
"BOOL",
"BYTE",
"UBYTE",
"SHORT",
"USHORT",
"INT",
"UINT",
"LONG",
"ULONG",
"LONGLONG",
"ULONGLONG",
"FLOAT",
"DOUBLE",
"CFLOAT",
]
_cast_dict = {"BOOL": ["BOOL"]}
_cast_dict["BYTE"] = _cast_dict["BOOL"] + ["BYTE"]
_cast_dict["UBYTE"] = _cast_dict["BOOL"] + ["UBYTE"]
_cast_dict["BYTE"] = ["BYTE"]
_cast_dict["UBYTE"] = ["UBYTE"]
_cast_dict["SHORT"] = _cast_dict["BYTE"] + ["UBYTE", "SHORT"]
_cast_dict["USHORT"] = _cast_dict["UBYTE"] + ["BYTE", "USHORT"]
_cast_dict["INT"] = _cast_dict["SHORT"] + ["USHORT", "INT"]
_cast_dict["UINT"] = _cast_dict["USHORT"] + ["SHORT", "UINT"]
_cast_dict["LONG"] = _cast_dict["INT"] + ["LONG"]
_cast_dict["ULONG"] = _cast_dict["UINT"] + ["ULONG"]
_cast_dict["LONGLONG"] = _cast_dict["LONG"] + ["LONGLONG"]
_cast_dict["ULONGLONG"] = _cast_dict["ULONG"] + ["ULONGLONG"]
_cast_dict["FLOAT"] = _cast_dict["SHORT"] + ["USHORT", "FLOAT"]
_cast_dict["DOUBLE"] = _cast_dict["INT"] + ["UINT", "FLOAT", "DOUBLE"]
_cast_dict["CFLOAT"] = _cast_dict["FLOAT"] + ["CFLOAT"]
# 32 bit system malloc typically does not provide the alignment required by
# 16 byte long double types this means the inout intent cannot be satisfied
# and several tests fail as the alignment flag can be randomly true or fals
# when numpy gains an aligned allocator the tests could be enabled again
#
# Furthermore, on macOS ARM64, LONGDOUBLE is an alias for DOUBLE.
if ((np.intp().dtype.itemsize != 4 or np.clongdouble().dtype.alignment <= 8)
and sys.platform != "win32"
and (platform.system(), platform.processor()) != ("Darwin", "arm")):
_type_names.extend(["LONGDOUBLE", "CDOUBLE", "CLONGDOUBLE"])
_cast_dict["LONGDOUBLE"] = _cast_dict["LONG"] + [
"ULONG",
"FLOAT",
"DOUBLE",
"LONGDOUBLE",
]
_cast_dict["CLONGDOUBLE"] = _cast_dict["LONGDOUBLE"] + [
"CFLOAT",
"CDOUBLE",
"CLONGDOUBLE",
]
_cast_dict["CDOUBLE"] = _cast_dict["DOUBLE"] + ["CFLOAT", "CDOUBLE"]
class Type:
_type_cache = {}
def __new__(cls, name):
if isinstance(name, np.dtype):
dtype0 = name
name = None
for n, i in typeinfo.items():
if not isinstance(i, type) and dtype0.type is i.type:
name = n
break
obj = cls._type_cache.get(name.upper(), None)
if obj is not None:
return obj
obj = object.__new__(cls)
obj._init(name)
cls._type_cache[name.upper()] = obj
return obj
def _init(self, name):
self.NAME = name.upper()
info = typeinfo[self.NAME]
self.type_num = getattr(wrap, "NPY_" + self.NAME)
assert self.type_num == info.num
self.dtype = np.dtype(info.type)
self.type = info.type
self.elsize = info.bits / 8
self.dtypechar = info.char
def cast_types(self):
return [self.__class__(_m) for _m in _cast_dict[self.NAME]]
def all_types(self):
return [self.__class__(_m) for _m in _type_names]
def smaller_types(self):
bits = typeinfo[self.NAME].alignment
types = []
for name in _type_names:
if typeinfo[name].alignment < bits:
types.append(Type(name))
return types
def equal_types(self):
bits = typeinfo[self.NAME].alignment
types = []
for name in _type_names:
if name == self.NAME:
continue
if typeinfo[name].alignment == bits:
types.append(Type(name))
return types
def larger_types(self):
bits = typeinfo[self.NAME].alignment
types = []
for name in _type_names:
if typeinfo[name].alignment > bits:
types.append(Type(name))
return types
class Array:
def __init__(self, typ, dims, intent, obj):
self.type = typ
self.dims = dims
self.intent = intent
self.obj_copy = copy.deepcopy(obj)
self.obj = obj
# arr.dtypechar may be different from typ.dtypechar
self.arr = wrap.call(typ.type_num, dims, intent.flags, obj)
assert isinstance(self.arr, np.ndarray)
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:] # 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:
num2seq = [1, 2]
num23seq = [[1, 2, 3], [4, 5, 6]]
@pytest.fixture(autouse=True, scope="class", params=_type_names)
def setup_type(self, request):
request.cls.type = Type(request.param)
request.cls.array = lambda self, dims, intent, obj: Array(
Type(request.param), dims, intent, obj)
def test_in_from_2seq(self):
a = self.array([2], intent.in_, self.num2seq)
assert not a.has_shared_memory()
def test_in_from_2casttype(self):
for t in self.type.cast_types():
obj = np.array(self.num2seq, dtype=t.dtype)
a = self.array([len(self.num2seq)], intent.in_, obj)
if t.elsize == self.type.elsize:
assert a.has_shared_memory(), repr((self.type.dtype, t.dtype))
else:
assert not a.has_shared_memory()
@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.elsize >= self.type.elsize:
continue
obj = np.array(self.num2seq, dtype=t.dtype)
shape = (len(self.num2seq), )
try:
self.array(shape, intent.in_.cache, obj) # Should succeed
except ValueError as msg:
if not str(msg).startswith(
"failed to initialize intent(cache) array"):
raise
else:
raise SystemError(
"intent(cache) should have failed on smaller array")
def test_cache_hidden(self):
shape = (2, )
a = self.array(shape, intent.cache.hide, None)
assert a.arr.shape == shape
shape = (2, 3)
a = self.array(shape, intent.cache.hide, None)
assert a.arr.shape == shape
shape = (-1, 3)
try:
a = self.array(shape, intent.cache.hide, None)
except ValueError as msg:
if not str(msg).startswith(
"failed to create intent(cache|hide)|optional array"):
raise
else:
raise SystemError(
"intent(cache) should have failed on undefined dimensions")
def test_hidden(self):
shape = (2, )
a = self.array(shape, intent.hide, None)
assert a.arr.shape == shape
assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))
shape = (2, 3)
a = self.array(shape, intent.hide, None)
assert a.arr.shape == shape
assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))
assert a.arr.flags["FORTRAN"] and not a.arr.flags["CONTIGUOUS"]
shape = (2, 3)
a = self.array(shape, intent.c.hide, None)
assert a.arr.shape == shape
assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))
assert not a.arr.flags["FORTRAN"] and a.arr.flags["CONTIGUOUS"]
shape = (-1, 3)
try:
a = self.array(shape, intent.hide, None)
except ValueError as msg:
if not str(msg).startswith(
"failed to create intent(cache|hide)|optional array"):
raise
else:
raise SystemError(
"intent(hide) should have failed on undefined dimensions")
def test_optional_none(self):
shape = (2, )
a = self.array(shape, intent.optional, None)
assert a.arr.shape == shape
assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))
shape = (2, 3)
a = self.array(shape, intent.optional, None)
assert a.arr.shape == shape
assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))
assert a.arr.flags["FORTRAN"] and not a.arr.flags["CONTIGUOUS"]
shape = (2, 3)
a = self.array(shape, intent.c.optional, None)
assert a.arr.shape == shape
assert a.arr_equal(a.arr, np.zeros(shape, dtype=self.type.dtype))
assert not a.arr.flags["FORTRAN"] and a.arr.flags["CONTIGUOUS"]
def test_optional_from_2seq(self):
obj = self.num2seq
shape = (len(obj), )
a = self.array(shape, intent.optional, obj)
assert a.arr.shape == shape
assert not a.has_shared_memory()
def test_optional_from_23seq(self):
obj = self.num23seq
shape = (len(obj), len(obj[0]))
a = self.array(shape, intent.optional, obj)
assert a.arr.shape == shape
assert not a.has_shared_memory()
a = self.array(shape, intent.optional.c, obj)
assert a.arr.shape == shape
assert not a.has_shared_memory()
def test_inplace(self):
obj = np.array(self.num23seq, dtype=self.type.dtype)
assert not obj.flags["FORTRAN"] and obj.flags["CONTIGUOUS"]
shape = obj.shape
a = self.array(shape, intent.inplace, obj)
assert obj[1][2] == a.arr[1][2], repr((obj, a.arr))
a.arr[1][2] = 54
assert obj[1][2] == a.arr[1][2] == np.array(54, dtype=self.type.dtype)
assert a.arr is obj
assert obj.flags["FORTRAN"] # obj attributes are changed inplace!
assert not obj.flags["CONTIGUOUS"]
def test_inplace_from_casttype(self):
for t in self.type.cast_types():
if t is self.type:
continue
obj = np.array(self.num23seq, dtype=t.dtype)
assert obj.dtype.type == t.type
assert obj.dtype.type is not self.type.type
assert not obj.flags["FORTRAN"] and obj.flags["CONTIGUOUS"]
shape = obj.shape
a = self.array(shape, intent.inplace, obj)
assert obj[1][2] == a.arr[1][2], repr((obj, a.arr))
a.arr[1][2] = 54
assert obj[1][2] == a.arr[1][2] == np.array(54,
dtype=self.type.dtype)
assert a.arr is obj
assert obj.flags["FORTRAN"] # obj attributes changed inplace!
assert not obj.flags["CONTIGUOUS"]
assert obj.dtype.type is self.type.type # obj changed inplace!

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(self):
# Use a custom file name for .f2py_f2cmap
self.sources = list(self.sources)
f2cmap_src = self.sources.pop(-1)
self.f2cmap_file = tempfile.NamedTemporaryFile(delete=False)
with open(f2cmap_src, "rb") as f:
self.f2cmap_file.write(f.read())
self.f2cmap_file.close()
self.sources.append(self.f2cmap_file.name)
self.options = ["--f2cmap", self.f2cmap_file.name]
super().setup()
def teardown(self):
os.unlink(self.f2cmap_file.name)

View file

@ -0,0 +1,17 @@
import sys
import pytest
from . import util
from numpy.testing import IS_PYPY
class TestBlockDocString(util.F2PyTest):
sources = [util.getpath("tests", "src", "block_docstring", "foo.f")]
@pytest.mark.skipif(sys.platform == "win32",
reason="Fails with MinGW64 Gfortran (Issue #9673)")
@pytest.mark.xfail(IS_PYPY,
reason="PyPy cannot modify tp_doc after PyType_Ready")
def test_block_docstring(self):
expected = "bar : 'i'-array(2,3)\n"
assert self.module.block.__doc__ == expected

View file

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

View file

@ -0,0 +1,18 @@
import os
import sys
import pytest
import numpy as np
from . import util
class TestCommonBlock(util.F2PyTest):
sources = [util.getpath("tests", "src", "common", "block.f")]
@pytest.mark.skipif(sys.platform == "win32",
reason="Fails with MinGW64 Gfortran (Issue #9673)")
def test_common_block(self):
self.module.initcb()
assert self.module.block.long_bn == np.array(1.0, dtype=np.float64)
assert self.module.block.string_bn == np.array("2", dtype="|S1")
assert self.module.block.ok == np.array(3, dtype=np.int32)

View file

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

View file

@ -0,0 +1,233 @@
import pytest
import numpy as np
from numpy.f2py.crackfortran import markinnerspaces
from . import util
from numpy.f2py import crackfortran
import textwrap
class TestNoSpace(util.F2PyTest):
# issue gh-15035: add handling for endsubroutine, endfunction with no space
# between "end" and the block name
sources = [util.getpath("tests", "src", "crackfortran", "gh15035.f")]
def test_module(self):
k = np.array([1, 2, 3], dtype=np.float64)
w = np.array([1, 2, 3], dtype=np.float64)
self.module.subb(k)
assert np.allclose(k, w + 1)
self.module.subc([w, k])
assert np.allclose(k, w + 1)
assert self.module.t0(23) == b"2"
class TestPublicPrivate:
def test_defaultPrivate(self):
fpath = util.getpath("tests", "src", "crackfortran", "privatemod.f90")
mod = crackfortran.crackfortran([str(fpath)])
assert len(mod) == 1
mod = mod[0]
assert "private" in mod["vars"]["a"]["attrspec"]
assert "public" not in mod["vars"]["a"]["attrspec"]
assert "private" in mod["vars"]["b"]["attrspec"]
assert "public" not in mod["vars"]["b"]["attrspec"]
assert "private" not in mod["vars"]["seta"]["attrspec"]
assert "public" in mod["vars"]["seta"]["attrspec"]
def test_defaultPublic(self, tmp_path):
fpath = util.getpath("tests", "src", "crackfortran", "publicmod.f90")
mod = crackfortran.crackfortran([str(fpath)])
assert len(mod) == 1
mod = mod[0]
assert "private" in mod["vars"]["a"]["attrspec"]
assert "public" not in mod["vars"]["a"]["attrspec"]
assert "private" not in mod["vars"]["seta"]["attrspec"]
assert "public" in mod["vars"]["seta"]["attrspec"]
def test_access_type(self, tmp_path):
fpath = util.getpath("tests", "src", "crackfortran", "accesstype.f90")
mod = crackfortran.crackfortran([str(fpath)])
assert len(mod) == 1
tt = mod[0]['vars']
assert set(tt['a']['attrspec']) == {'private', 'bind(c)'}
assert set(tt['b_']['attrspec']) == {'public', 'bind(c)'}
assert set(tt['c']['attrspec']) == {'public'}
class TestModuleProcedure():
def test_moduleOperators(self, tmp_path):
fpath = util.getpath("tests", "src", "crackfortran", "operators.f90")
mod = crackfortran.crackfortran([str(fpath)])
assert len(mod) == 1
mod = mod[0]
assert "body" in mod and len(mod["body"]) == 9
assert mod["body"][1]["name"] == "operator(.item.)"
assert "implementedby" in mod["body"][1]
assert mod["body"][1]["implementedby"] == \
["item_int", "item_real"]
assert mod["body"][2]["name"] == "operator(==)"
assert "implementedby" in mod["body"][2]
assert mod["body"][2]["implementedby"] == ["items_are_equal"]
assert mod["body"][3]["name"] == "assignment(=)"
assert "implementedby" in mod["body"][3]
assert mod["body"][3]["implementedby"] == \
["get_int", "get_real"]
class TestExternal(util.F2PyTest):
# issue gh-17859: add external attribute support
sources = [util.getpath("tests", "src", "crackfortran", "gh17859.f")]
def test_external_as_statement(self):
def incr(x):
return x + 123
r = self.module.external_as_statement(incr)
assert r == 123
def test_external_as_attribute(self):
def incr(x):
return x + 123
r = self.module.external_as_attribute(incr)
assert r == 123
class TestCrackFortran(util.F2PyTest):
# gh-2848: commented lines between parameters in subroutine parameter lists
sources = [util.getpath("tests", "src", "crackfortran", "gh2848.f90")]
def test_gh2848(self):
r = self.module.gh2848(1, 2)
assert r == (1, 2)
class TestMarkinnerspaces:
# gh-14118: markinnerspaces does not handle multiple quotations
def test_do_not_touch_normal_spaces(self):
test_list = ["a ", " a", "a b c", "'abcdefghij'"]
for i in test_list:
assert markinnerspaces(i) == i
def test_one_relevant_space(self):
assert markinnerspaces("a 'b c' \\' \\'") == "a 'b@_@c' \\' \\'"
assert markinnerspaces(r'a "b c" \" \"') == r'a "b@_@c" \" \"'
def test_ignore_inner_quotes(self):
assert markinnerspaces("a 'b c\" \" d' e") == "a 'b@_@c\"@_@\"@_@d' e"
assert markinnerspaces("a \"b c' ' d\" e") == "a \"b@_@c'@_@'@_@d\" e"
def test_multiple_relevant_spaces(self):
assert markinnerspaces("a 'b c' 'd e'") == "a 'b@_@c' 'd@_@e'"
assert markinnerspaces(r'a "b c" "d e"') == r'a "b@_@c" "d@_@e"'
class TestDimSpec(util.F2PyTest):
"""This test suite tests various expressions that are used as dimension
specifications.
There exists two usage cases where analyzing dimensions
specifications are important.
In the first case, the size of output arrays must be defined based
on the inputs to a Fortran function. Because Fortran supports
arbitrary bases for indexing, for instance, `arr(lower:upper)`,
f2py has to evaluate an expression `upper - lower + 1` where
`lower` and `upper` are arbitrary expressions of input parameters.
The evaluation is performed in C, so f2py has to translate Fortran
expressions to valid C expressions (an alternative approach is
that a developer specifies the corresponding C expressions in a
.pyf file).
In the second case, when user provides an input array with a given
size but some hidden parameters used in dimensions specifications
need to be determined based on the input array size. This is a
harder problem because f2py has to solve the inverse problem: find
a parameter `p` such that `upper(p) - lower(p) + 1` equals to the
size of input array. In the case when this equation cannot be
solved (e.g. because the input array size is wrong), raise an
error before calling the Fortran function (that otherwise would
likely crash Python process when the size of input arrays is
wrong). f2py currently supports this case only when the equation
is linear with respect to unknown parameter.
"""
suffix = ".f90"
code_template = textwrap.dedent("""
function get_arr_size_{count}(a, n) result (length)
integer, intent(in) :: n
integer, dimension({dimspec}), intent(out) :: a
integer length
length = size(a)
end function
subroutine get_inv_arr_size_{count}(a, n)
integer :: n
! the value of n is computed in f2py wrapper
!f2py intent(out) n
integer, dimension({dimspec}), intent(in) :: a
if (a({first}).gt.0) then
print*, "a=", a
endif
end subroutine
""")
linear_dimspecs = [
"n", "2*n", "2:n", "n/2", "5 - n/2", "3*n:20", "n*(n+1):n*(n+5)",
"2*n, n"
]
nonlinear_dimspecs = ["2*n:3*n*n+2*n"]
all_dimspecs = linear_dimspecs + nonlinear_dimspecs
code = ""
for count, dimspec in enumerate(all_dimspecs):
lst = [(d.split(":")[0] if ":" in d else "1") for d in dimspec.split(',')]
code += code_template.format(
count=count,
dimspec=dimspec,
first=", ".join(lst),
)
@pytest.mark.parametrize("dimspec", all_dimspecs)
def test_array_size(self, dimspec):
count = self.all_dimspecs.index(dimspec)
get_arr_size = getattr(self.module, f"get_arr_size_{count}")
for n in [1, 2, 3, 4, 5]:
sz, a = get_arr_size(n)
assert a.size == sz
@pytest.mark.parametrize("dimspec", all_dimspecs)
def test_inv_array_size(self, dimspec):
count = self.all_dimspecs.index(dimspec)
get_arr_size = getattr(self.module, f"get_arr_size_{count}")
get_inv_arr_size = getattr(self.module, f"get_inv_arr_size_{count}")
for n in [1, 2, 3, 4, 5]:
sz, a = get_arr_size(n)
if dimspec in self.nonlinear_dimspecs:
# one must specify n as input, the call we'll ensure
# that a and n are compatible:
n1 = get_inv_arr_size(a, n)
else:
# in case of linear dependence, n can be determined
# from the shape of a:
n1 = get_inv_arr_size(a)
# n1 may be different from n (for instance, when `a` size
# is a function of some `n` fraction) but it must produce
# the same sized array
sz1, _ = get_arr_size(n1)
assert sz == sz1, (n, n1, sz, sz1)
class TestModuleDeclaration:
def test_dependencies(self, tmp_path):
fpath = util.getpath("tests", "src", "crackfortran", "foo_deps.f90")
mod = crackfortran.crackfortran([str(fpath)])
assert len(mod) == 1
assert mod[0]["vars"]["abar"]["="] == "bar('abar')"

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

View file

@ -0,0 +1,748 @@
import textwrap, re, sys, subprocess, shlex
from pathlib import Path
from collections import namedtuple
import pytest
from . import util
from numpy.f2py.f2py2e import main as f2pycli
#########################
# CLI utils and classes #
#########################
PPaths = namedtuple("PPaths", "finp, f90inp, pyf, wrap77, wrap90, cmodf")
def get_io_paths(fname_inp, mname="untitled"):
"""Takes in a temporary file for testing and returns the expected output and input paths
Here expected output is essentially one of any of the possible generated
files.
..note::
Since this does not actually run f2py, none of these are guaranteed to
exist, and module names are typically incorrect
Parameters
----------
fname_inp : str
The input filename
mname : str, optional
The name of the module, untitled by default
Returns
-------
genp : NamedTuple PPaths
The possible paths which are generated, not all of which exist
"""
bpath = Path(fname_inp)
return PPaths(
finp=bpath.with_suffix(".f"),
f90inp=bpath.with_suffix(".f90"),
pyf=bpath.with_suffix(".pyf"),
wrap77=bpath.with_name(f"{mname}-f2pywrappers.f"),
wrap90=bpath.with_name(f"{mname}-f2pywrappers2.f90"),
cmodf=bpath.with_name(f"{mname}module.c"),
)
##############
# CLI Fixtures and Tests #
#############
@pytest.fixture(scope="session")
def hello_world_f90(tmpdir_factory):
"""Generates a single f90 file for testing"""
fdat = util.getpath("tests", "src", "cli", "hiworld.f90").read_text()
fn = tmpdir_factory.getbasetemp() / "hello.f90"
fn.write_text(fdat, encoding="ascii")
return fn
@pytest.fixture(scope="session")
def hello_world_f77(tmpdir_factory):
"""Generates a single f77 file for testing"""
fdat = util.getpath("tests", "src", "cli", "hi77.f").read_text()
fn = tmpdir_factory.getbasetemp() / "hello.f"
fn.write_text(fdat, encoding="ascii")
return fn
@pytest.fixture(scope="session")
def retreal_f77(tmpdir_factory):
"""Generates a single f77 file for testing"""
fdat = util.getpath("tests", "src", "return_real", "foo77.f").read_text()
fn = tmpdir_factory.getbasetemp() / "foo.f"
fn.write_text(fdat, encoding="ascii")
return fn
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
def test_gen_pyf_no_overwrite(capfd, hello_world_f90, monkeypatch):
"""Ensures that the CLI refuses to overwrite signature files
CLI :: -h without --overwrite-signature
"""
ipath = Path(hello_world_f90)
monkeypatch.setattr(sys, "argv", f'f2py -h faker.pyf {ipath}'.split())
with util.switchdir(ipath.parent):
Path("faker.pyf").write_text("Fake news", encoding="ascii")
with pytest.raises(SystemExit):
f2pycli() # Refuse to overwrite
_, err = capfd.readouterr()
assert "Use --overwrite-signature to overwrite" in err
@pytest.mark.xfail
def test_f2py_skip(capfd, retreal_f77, monkeypatch):
"""Tests that functions can be skipped
CLI :: skip:
"""
foutl = get_io_paths(retreal_f77, mname="test")
ipath = foutl.finp
toskip = "t0 t4 t8 sd s8 s4"
remaining = "td s0"
monkeypatch.setattr(
sys, "argv",
f'f2py {ipath} -m test skip: {toskip}'.split())
with util.switchdir(ipath.parent):
f2pycli()
out, err = capfd.readouterr()
for skey in toskip.split():
assert (
f'buildmodule: Could not found the body of interfaced routine "{skey}". Skipping.'
in err)
for rkey in remaining.split():
assert f'Constructing wrapper function "{rkey}"' in out
def test_f2py_only(capfd, retreal_f77, monkeypatch):
"""Test that functions can be kept by only:
CLI :: only:
"""
foutl = get_io_paths(retreal_f77, mname="test")
ipath = foutl.finp
toskip = "t0 t4 t8 sd s8 s4"
tokeep = "td s0"
monkeypatch.setattr(
sys, "argv",
f'f2py {ipath} -m test only: {tokeep}'.split())
with util.switchdir(ipath.parent):
f2pycli()
out, err = capfd.readouterr()
for skey in toskip.split():
assert (
f'buildmodule: Could not find the body of interfaced routine "{skey}". Skipping.'
in err)
for rkey in tokeep.split():
assert f'Constructing wrapper function "{rkey}"' in out
def test_file_processing_switch(capfd, hello_world_f90, retreal_f77,
monkeypatch):
"""Tests that it is possible to return to file processing mode
CLI :: :
BUG: numpy-gh #20520
"""
foutl = get_io_paths(retreal_f77, mname="test")
ipath = foutl.finp
toskip = "t0 t4 t8 sd s8 s4"
ipath2 = Path(hello_world_f90)
tokeep = "td s0 hi" # hi is in ipath2
mname = "blah"
monkeypatch.setattr(
sys,
"argv",
f'f2py {ipath} -m {mname} only: {tokeep} : {ipath2}'.split(
),
)
with util.switchdir(ipath.parent):
f2pycli()
out, err = capfd.readouterr()
for skey in toskip.split():
assert (
f'buildmodule: Could not find the body of interfaced routine "{skey}". Skipping.'
in err)
for rkey in tokeep.split():
assert f'Constructing wrapper function "{rkey}"' in out
def test_mod_gen_f77(capfd, hello_world_f90, monkeypatch):
"""Checks the generation of files based on a module name
CLI :: -m
"""
MNAME = "hi"
foutl = get_io_paths(hello_world_f90, mname=MNAME)
ipath = foutl.f90inp
monkeypatch.setattr(sys, "argv", f'f2py {ipath} -m {MNAME}'.split())
with util.switchdir(ipath.parent):
f2pycli()
# Always generate C module
assert Path.exists(foutl.cmodf)
# File contains a function, check for F77 wrappers
assert Path.exists(foutl.wrap77)
def test_lower_cmod(capfd, hello_world_f77, monkeypatch):
"""Lowers cases by flag or when -h is present
CLI :: --[no-]lower
"""
foutl = get_io_paths(hello_world_f77, mname="test")
ipath = foutl.finp
capshi = re.compile(r"HI\(\)")
capslo = re.compile(r"hi\(\)")
# Case I: --lower is passed
monkeypatch.setattr(sys, "argv", f'f2py {ipath} -m test --lower'.split())
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert capslo.search(out) is not None
assert capshi.search(out) is None
# Case II: --no-lower is passed
monkeypatch.setattr(sys, "argv",
f'f2py {ipath} -m test --no-lower'.split())
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert capslo.search(out) is None
assert capshi.search(out) is not None
def test_lower_sig(capfd, hello_world_f77, monkeypatch):
"""Lowers cases in signature files by flag or when -h is present
CLI :: --[no-]lower -h
"""
foutl = get_io_paths(hello_world_f77, mname="test")
ipath = foutl.finp
# Signature files
capshi = re.compile(r"Block: HI")
capslo = re.compile(r"Block: hi")
# Case I: --lower is implied by -h
# TODO: Clean up to prevent passing --overwrite-signature
monkeypatch.setattr(
sys,
"argv",
f'f2py {ipath} -h {foutl.pyf} -m test --overwrite-signature'.split(),
)
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert capslo.search(out) is not None
assert capshi.search(out) is None
# Case II: --no-lower overrides -h
monkeypatch.setattr(
sys,
"argv",
f'f2py {ipath} -h {foutl.pyf} -m test --overwrite-signature --no-lower'
.split(),
)
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert capslo.search(out) is None
assert capshi.search(out) is not None
def test_build_dir(capfd, hello_world_f90, monkeypatch):
"""Ensures that the build directory can be specified
CLI :: --build-dir
"""
ipath = Path(hello_world_f90)
mname = "blah"
odir = "tttmp"
monkeypatch.setattr(sys, "argv",
f'f2py -m {mname} {ipath} --build-dir {odir}'.split())
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert f"Wrote C/API module \"{mname}\"" in out
def test_overwrite(capfd, hello_world_f90, monkeypatch):
"""Ensures that the build directory can be specified
CLI :: --overwrite-signature
"""
ipath = Path(hello_world_f90)
monkeypatch.setattr(
sys, "argv",
f'f2py -h faker.pyf {ipath} --overwrite-signature'.split())
with util.switchdir(ipath.parent):
Path("faker.pyf").write_text("Fake news", encoding="ascii")
f2pycli()
out, _ = capfd.readouterr()
assert "Saving signatures to file" in out
def test_latexdoc(capfd, hello_world_f90, monkeypatch):
"""Ensures that TeX documentation is written out
CLI :: --latex-doc
"""
ipath = Path(hello_world_f90)
mname = "blah"
monkeypatch.setattr(sys, "argv",
f'f2py -m {mname} {ipath} --latex-doc'.split())
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert "Documentation is saved to file" in out
with Path(f"{mname}module.tex").open() as otex:
assert "\\documentclass" in otex.read()
def test_nolatexdoc(capfd, hello_world_f90, monkeypatch):
"""Ensures that TeX documentation is written out
CLI :: --no-latex-doc
"""
ipath = Path(hello_world_f90)
mname = "blah"
monkeypatch.setattr(sys, "argv",
f'f2py -m {mname} {ipath} --no-latex-doc'.split())
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert "Documentation is saved to file" not in out
def test_shortlatex(capfd, hello_world_f90, monkeypatch):
"""Ensures that truncated documentation is written out
TODO: Test to ensure this has no effect without --latex-doc
CLI :: --latex-doc --short-latex
"""
ipath = Path(hello_world_f90)
mname = "blah"
monkeypatch.setattr(
sys,
"argv",
f'f2py -m {mname} {ipath} --latex-doc --short-latex'.split(),
)
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert "Documentation is saved to file" in out
with Path(f"./{mname}module.tex").open() as otex:
assert "\\documentclass" not in otex.read()
def test_restdoc(capfd, hello_world_f90, monkeypatch):
"""Ensures that RsT documentation is written out
CLI :: --rest-doc
"""
ipath = Path(hello_world_f90)
mname = "blah"
monkeypatch.setattr(sys, "argv",
f'f2py -m {mname} {ipath} --rest-doc'.split())
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert "ReST Documentation is saved to file" in out
with Path(f"./{mname}module.rest").open() as orst:
assert r".. -*- rest -*-" in orst.read()
def test_norestexdoc(capfd, hello_world_f90, monkeypatch):
"""Ensures that TeX documentation is written out
CLI :: --no-rest-doc
"""
ipath = Path(hello_world_f90)
mname = "blah"
monkeypatch.setattr(sys, "argv",
f'f2py -m {mname} {ipath} --no-rest-doc'.split())
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert "ReST Documentation is saved to file" not in out
def test_debugcapi(capfd, hello_world_f90, monkeypatch):
"""Ensures that debugging wrappers are written
CLI :: --debug-capi
"""
ipath = Path(hello_world_f90)
mname = "blah"
monkeypatch.setattr(sys, "argv",
f'f2py -m {mname} {ipath} --debug-capi'.split())
with util.switchdir(ipath.parent):
f2pycli()
with Path(f"./{mname}module.c").open() as ocmod:
assert r"#define DEBUGCFUNCS" in ocmod.read()
@pytest.mark.xfail(reason="Consistently fails on CI.")
def test_debugcapi_bld(hello_world_f90, monkeypatch):
"""Ensures that debugging wrappers work
CLI :: --debug-capi -c
"""
ipath = Path(hello_world_f90)
mname = "blah"
monkeypatch.setattr(sys, "argv",
f'f2py -m {mname} {ipath} -c --debug-capi'.split())
with util.switchdir(ipath.parent):
f2pycli()
cmd_run = shlex.split("python3 -c \"import blah; blah.hi()\"")
rout = subprocess.run(cmd_run, capture_output=True, encoding='UTF-8')
eout = ' Hello World\n'
eerr = textwrap.dedent("""\
debug-capi:Python C/API function blah.hi()
debug-capi:float hi=:output,hidden,scalar
debug-capi:hi=0
debug-capi:Fortran subroutine `f2pywraphi(&hi)'
debug-capi:hi=0
debug-capi:Building return value.
debug-capi:Python C/API function blah.hi: successful.
debug-capi:Freeing memory.
""")
assert rout.stdout == eout
assert rout.stderr == eerr
def test_wrapfunc_def(capfd, hello_world_f90, monkeypatch):
"""Ensures that fortran subroutine wrappers for F77 are included by default
CLI :: --[no]-wrap-functions
"""
# Implied
ipath = Path(hello_world_f90)
mname = "blah"
monkeypatch.setattr(sys, "argv", f'f2py -m {mname} {ipath}'.split())
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert r"Fortran 77 wrappers are saved to" in out
# Explicit
monkeypatch.setattr(sys, "argv",
f'f2py -m {mname} {ipath} --wrap-functions'.split())
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert r"Fortran 77 wrappers are saved to" in out
def test_nowrapfunc(capfd, hello_world_f90, monkeypatch):
"""Ensures that fortran subroutine wrappers for F77 can be disabled
CLI :: --no-wrap-functions
"""
ipath = Path(hello_world_f90)
mname = "blah"
monkeypatch.setattr(sys, "argv",
f'f2py -m {mname} {ipath} --no-wrap-functions'.split())
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert r"Fortran 77 wrappers are saved to" not in out
def test_inclheader(capfd, hello_world_f90, monkeypatch):
"""Add to the include directories
CLI :: -include
TODO: Document this in the help string
"""
ipath = Path(hello_world_f90)
mname = "blah"
monkeypatch.setattr(
sys,
"argv",
f'f2py -m {mname} {ipath} -include<stdbool.h> -include<stdio.h> '.
split(),
)
with util.switchdir(ipath.parent):
f2pycli()
with Path(f"./{mname}module.c").open() as ocmod:
ocmr = ocmod.read()
assert "#include <stdbool.h>" in ocmr
assert "#include <stdio.h>" in ocmr
def test_inclpath():
"""Add to the include directories
CLI :: --include-paths
"""
# TODO: populate
pass
def test_hlink():
"""Add to the include directories
CLI :: --help-link
"""
# TODO: populate
pass
def test_f2cmap():
"""Check that Fortran-to-Python KIND specs can be passed
CLI :: --f2cmap
"""
# TODO: populate
pass
def test_quiet(capfd, hello_world_f90, monkeypatch):
"""Reduce verbosity
CLI :: --quiet
"""
ipath = Path(hello_world_f90)
monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} --quiet'.split())
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert len(out) == 0
def test_verbose(capfd, hello_world_f90, monkeypatch):
"""Increase verbosity
CLI :: --verbose
"""
ipath = Path(hello_world_f90)
monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} --verbose'.split())
with util.switchdir(ipath.parent):
f2pycli()
out, _ = capfd.readouterr()
assert "analyzeline" in out
def test_version(capfd, monkeypatch):
"""Ensure version
CLI :: -v
"""
monkeypatch.setattr(sys, "argv", 'f2py -v'.split())
# TODO: f2py2e should not call sys.exit() after printing the version
with pytest.raises(SystemExit):
f2pycli()
out, _ = capfd.readouterr()
import numpy as np
assert np.__version__ == out.strip()
@pytest.mark.xfail(reason="Consistently fails on CI.")
def test_npdistop(hello_world_f90, monkeypatch):
"""
CLI :: -c
"""
ipath = Path(hello_world_f90)
monkeypatch.setattr(sys, "argv", f'f2py -m blah {ipath} -c'.split())
with util.switchdir(ipath.parent):
f2pycli()
cmd_run = shlex.split("python -c \"import blah; blah.hi()\"")
rout = subprocess.run(cmd_run, capture_output=True, encoding='UTF-8')
eout = ' Hello World\n'
assert rout.stdout == eout
# Numpy distutils flags
# TODO: These should be tested separately
def test_npd_fcompiler():
"""
CLI :: -c --fcompiler
"""
# TODO: populate
pass
def test_npd_compiler():
"""
CLI :: -c --compiler
"""
# TODO: populate
pass
def test_npd_help_fcompiler():
"""
CLI :: -c --help-fcompiler
"""
# TODO: populate
pass
def test_npd_f77exec():
"""
CLI :: -c --f77exec
"""
# TODO: populate
pass
def test_npd_f90exec():
"""
CLI :: -c --f90exec
"""
# TODO: populate
pass
def test_npd_f77flags():
"""
CLI :: -c --f77flags
"""
# TODO: populate
pass
def test_npd_f90flags():
"""
CLI :: -c --f90flags
"""
# TODO: populate
pass
def test_npd_opt():
"""
CLI :: -c --opt
"""
# TODO: populate
pass
def test_npd_arch():
"""
CLI :: -c --arch
"""
# TODO: populate
pass
def test_npd_noopt():
"""
CLI :: -c --noopt
"""
# TODO: populate
pass
def test_npd_noarch():
"""
CLI :: -c --noarch
"""
# TODO: populate
pass
def test_npd_debug():
"""
CLI :: -c --debug
"""
# TODO: populate
pass
def test_npd_link_auto():
"""
CLI :: -c --link-<resource>
"""
# TODO: populate
pass
def test_npd_lib():
"""
CLI :: -c -L/path/to/lib/ -l<libname>
"""
# TODO: populate
pass
def test_npd_define():
"""
CLI :: -D<define>
"""
# TODO: populate
pass
def test_npd_undefine():
"""
CLI :: -U<name>
"""
# TODO: populate
pass
def test_npd_incl():
"""
CLI :: -I/path/to/include/
"""
# TODO: populate
pass
def test_npd_linker():
"""
CLI :: <filename>.o <filename>.so <filename>.a
"""
# TODO: populate
pass

View file

@ -0,0 +1,26 @@
import os
import pytest
from numpy.f2py.crackfortran import (
_selected_int_kind_func as selected_int_kind,
_selected_real_kind_func as selected_real_kind,
)
from . import util
class TestKind(util.F2PyTest):
sources = [util.getpath("tests", "src", "kind", "foo.f90")]
def test_all(self):
selectedrealkind = self.module.selectedrealkind
selectedintkind = self.module.selectedintkind
for i in range(40):
assert selectedintkind(i) == selected_int_kind(
i
), f"selectedintkind({i}): expected {selected_int_kind(i)!r} but got {selectedintkind(i)!r}"
for i in range(20):
assert selectedrealkind(i) == selected_real_kind(
i
), f"selectedrealkind({i}): expected {selected_real_kind(i)!r} but got {selectedrealkind(i)!r}"

View file

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

View file

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

View file

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

View file

@ -0,0 +1,16 @@
"""See https://github.com/numpy/numpy/pull/10676.
"""
import sys
import pytest
from . import util
class TestQuotedCharacter(util.F2PyTest):
sources = [util.getpath("tests", "src", "quoted_character", "foo.f")]
@pytest.mark.skipif(sys.platform == "win32",
reason="Fails with MinGW64 Gfortran (Issue #9673)")
def test_quoted_character(self):
assert self.module.foo() == (b"'", b'"', b";", b"!", b"(", b")")

View file

@ -0,0 +1,66 @@
import os
import pytest
import numpy as np
from . import util
class TestIntentInOut(util.F2PyTest):
# Check that intent(in out) translates as intent(inout)
sources = [util.getpath("tests", "src", "regression", "inout.f90")]
@pytest.mark.slow
def test_inout(self):
# non-contiguous should raise error
x = np.arange(6, dtype=np.float32)[::2]
pytest.raises(ValueError, self.module.foo, x)
# check values with contiguous array
x = np.arange(3, dtype=np.float32)
self.module.foo(x)
assert np.allclose(x, [3, 1, 2])
class TestNegativeBounds(util.F2PyTest):
# Check that negative bounds work correctly
sources = [util.getpath("tests", "src", "negative_bounds", "issue_20853.f90")]
@pytest.mark.slow
def test_negbound(self):
xvec = np.arange(12)
xlow = -6
xhigh = 4
# Calculate the upper bound,
# Keeping the 1 index in mind
def ubound(xl, xh):
return xh - xl + 1
rval = self.module.foo(is_=xlow, ie_=xhigh,
arr=xvec[:ubound(xlow, xhigh)])
expval = np.arange(11, dtype = np.float32)
assert np.allclose(rval, expval)
class TestNumpyVersionAttribute(util.F2PyTest):
# Check that th attribute __f2py_numpy_version__ is present
# in the compiled module and that has the value np.__version__.
sources = [util.getpath("tests", "src", "regression", "inout.f90")]
@pytest.mark.slow
def test_numpy_version_attribute(self):
# Check that self.module has an attribute named "__f2py_numpy_version__"
assert hasattr(self.module, "__f2py_numpy_version__")
# Check that the attribute __f2py_numpy_version__ is a string
assert isinstance(self.module.__f2py_numpy_version__, str)
# Check that __f2py_numpy_version__ has the value numpy.__version__
assert np.__version__ == self.module.__f2py_numpy_version__
def test_include_path():
incdir = np.f2py.get_include()
fnames_in_dir = os.listdir(incdir)
for fname in ("fortranobject.c", "fortranobject.h"):
assert fname in fnames_in_dir

View file

@ -0,0 +1,45 @@
import pytest
from numpy import array
from . import util
import platform
IS_S390X = platform.machine() == "s390x"
class TestReturnCharacter(util.F2PyTest):
def check_function(self, t, tname):
if tname in ["t0", "t1", "s0", "s1"]:
assert t(23) == b"2"
r = t("ab")
assert r == b"a"
r = t(array("ab"))
assert r == b"a"
r = t(array(77, "u1"))
assert r == b"M"
elif tname in ["ts", "ss"]:
assert t(23) == b"23"
assert t("123456789abcdef") == b"123456789a"
elif tname in ["t5", "s5"]:
assert t(23) == b"23"
assert t("ab") == b"ab"
assert t("123456789abcdef") == b"12345"
else:
raise NotImplementedError
class TestFReturnCharacter(TestReturnCharacter):
sources = [
util.getpath("tests", "src", "return_character", "foo77.f"),
util.getpath("tests", "src", "return_character", "foo90.f90"),
]
@pytest.mark.xfail(IS_S390X, reason="callback returns ' '")
@pytest.mark.parametrize("name", "t0,t1,t5,s0,s1,s5,ss".split(","))
def test_all_f77(self, name):
self.check_function(getattr(self.module, name), name)
@pytest.mark.xfail(IS_S390X, reason="callback returns ' '")
@pytest.mark.parametrize("name", "t0,t1,t5,ts,s0,s1,s5,ss".split(","))
def test_all_f90(self, name):
self.check_function(getattr(self.module.f90_return_char, name), name)

View file

@ -0,0 +1,65 @@
import pytest
from numpy import array
from . import util
class TestReturnComplex(util.F2PyTest):
def check_function(self, t, tname):
if tname in ["t0", "t8", "s0", "s8"]:
err = 1e-5
else:
err = 0.0
assert abs(t(234j) - 234.0j) <= err
assert abs(t(234.6) - 234.6) <= err
assert abs(t(234) - 234.0) <= err
assert abs(t(234.6 + 3j) - (234.6 + 3j)) <= err
# assert abs(t('234')-234.)<=err
# assert abs(t('234.6')-234.6)<=err
assert abs(t(-234) + 234.0) <= err
assert abs(t([234]) - 234.0) <= err
assert abs(t((234, )) - 234.0) <= err
assert abs(t(array(234)) - 234.0) <= err
assert abs(t(array(23 + 4j, "F")) - (23 + 4j)) <= err
assert abs(t(array([234])) - 234.0) <= err
assert abs(t(array([[234]])) - 234.0) <= err
assert abs(t(array([234], "b")) + 22.0) <= err
assert abs(t(array([234], "h")) - 234.0) <= err
assert abs(t(array([234], "i")) - 234.0) <= err
assert abs(t(array([234], "l")) - 234.0) <= err
assert abs(t(array([234], "q")) - 234.0) <= err
assert abs(t(array([234], "f")) - 234.0) <= err
assert abs(t(array([234], "d")) - 234.0) <= err
assert abs(t(array([234 + 3j], "F")) - (234 + 3j)) <= err
assert abs(t(array([234], "D")) - 234.0) <= err
# pytest.raises(TypeError, t, array([234], 'a1'))
pytest.raises(TypeError, t, "abc")
pytest.raises(IndexError, t, [])
pytest.raises(IndexError, t, ())
pytest.raises(TypeError, t, t)
pytest.raises(TypeError, t, {})
try:
r = t(10**400)
assert repr(r) in ["(inf+0j)", "(Infinity+0j)"]
except OverflowError:
pass
class TestFReturnComplex(TestReturnComplex):
sources = [
util.getpath("tests", "src", "return_complex", "foo77.f"),
util.getpath("tests", "src", "return_complex", "foo90.f90"),
]
@pytest.mark.parametrize("name", "t0,t8,t16,td,s0,s8,s16,sd".split(","))
def test_all_f77(self, name):
self.check_function(getattr(self.module, name), name)
@pytest.mark.parametrize("name", "t0,t8,t16,td,s0,s8,s16,sd".split(","))
def test_all_f90(self, name):
self.check_function(getattr(self.module.f90_return_complex, name),
name)

View file

@ -0,0 +1,55 @@
import pytest
from numpy import array
from . import util
class TestReturnInteger(util.F2PyTest):
def check_function(self, t, tname):
assert t(123) == 123
assert t(123.6) == 123
assert t("123") == 123
assert t(-123) == -123
assert t([123]) == 123
assert t((123, )) == 123
assert t(array(123)) == 123
assert t(array([123])) == 123
assert t(array([[123]])) == 123
assert t(array([123], "b")) == 123
assert t(array([123], "h")) == 123
assert t(array([123], "i")) == 123
assert t(array([123], "l")) == 123
assert t(array([123], "B")) == 123
assert t(array([123], "f")) == 123
assert t(array([123], "d")) == 123
# pytest.raises(ValueError, t, array([123],'S3'))
pytest.raises(ValueError, t, "abc")
pytest.raises(IndexError, t, [])
pytest.raises(IndexError, t, ())
pytest.raises(Exception, t, t)
pytest.raises(Exception, t, {})
if tname in ["t8", "s8"]:
pytest.raises(OverflowError, t, 100000000000000000000000)
pytest.raises(OverflowError, t, 10000000011111111111111.23)
class TestFReturnInteger(TestReturnInteger):
sources = [
util.getpath("tests", "src", "return_integer", "foo77.f"),
util.getpath("tests", "src", "return_integer", "foo90.f90"),
]
@pytest.mark.parametrize("name",
"t0,t1,t2,t4,t8,s0,s1,s2,s4,s8".split(","))
def test_all_f77(self, name):
self.check_function(getattr(self.module, name), name)
@pytest.mark.parametrize("name",
"t0,t1,t2,t4,t8,s0,s1,s2,s4,s8".split(","))
def test_all_f90(self, name):
self.check_function(getattr(self.module.f90_return_integer, name),
name)

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