The problem appears to be caused by a mismatch between the expected and actual data types of the external function FUN:
- According to the implicit data typing rules of Fortran, in
CALLBACK.F, the EXTERNAL function FUN has an implicit type REAL (since there is no explicit type, or IMPLICIT NONE statement).
- However, also looking at the details of the second example in the documentation, where the F2PY wrapper is created explicitly using
f2py -m callback2 -h callback2.pyf callback.f, you will note that the result r of the external function fun is defined as having type real*8 :: r (this is also true for the unmodified callback2.pyf signature file, so this is the default F2PY behaviour).
In short the problem is that FOO expects FUN to return a REAL result, while from the Python and F2PY wrapper's side the external function is defined to return a REAL*8 result. A solution is therefore to ensure that FUN has the same return type in Fortran and the Python/F2PY wrapper. This can be achieved in several ways, for example by adding a data type specification REAL*8 FUN in CALLBACK.F:
C FILE: CALLBACK.F
SUBROUTINE FOO(FUN,R)
REAL*8 FUN
EXTERNAL FUN
INTEGER I
REAL*8 R
Cf2py intent(out) r
R = 0D0
DO I=-5,5
R = R + FUN(I)
ENDDO
END
C END OF FILE CALLBACK.F
Wrapping this modified CALLBACK.F with python -m numpy.f2py -c -m callback callback.f as in the example, now gives the desired output:
python
>>> import callback
>>> def f(i): return i*i
...
>>> print(callback.foo(f))
110.0
For interest sake, the same behaviour that you saw in Python/F2PY can be produced in pure Fortran using two files prog.f and fun.f:
C FILE: PROG.F, including subroutine FOO previously in CALLBACK.F
PROGRAM MAIN
C REAL*8 FUN
EXTERNAL FUN
REAL*8 R
R = 0
PRINT *, "BEFORE: ", R
CALL FOO(FUN, R)
PRINT *, "AFTER: ", R
END
C This is copied from CALLBACK.F
SUBROUTINE FOO(FUN,R)
C REAL*8 FUN
EXTERNAL FUN
INTEGER I
REAL*8 R
Cf2py intent(out) r
R = 0D0
DO I=-5,5
R = R + FUN(I)
ENDDO
END
C END OF FILE CALLBACK.F
and
C FILE: FUN.F containing the function to be called by FOO
REAL*8 FUNCTION FUN(I)
INTEGER I
FUN = I*I
END
Compiled using gfortran -fcheck=all -Wall -g func.f prog.f, it gives the following output (in effect the same as your problematic Python example):
./a.out
BEFORE: 0.0000000000000000
AFTER: 0.0000000000000000
Uncommenting both instances of REAL*8 FUN in prog.f and recompiling solves the problem:
./a.out
BEFORE: 0.0000000000000000
AFTER: 110.00000000000000
fooreturns 0 where 110 is expected. Python 3.7 NumPy 1.16.0