[f2py] Unexpected text pyfunction_cptr encountered

Pearu Peterson pearu.peterson at gmail.com
Sat Oct 16 12:42:26 EEST 2010


Hi,
You can use the f2py intent(callback) feature to achieve what you have
described. Here follows an example:

c foo.f
      SUBROUTINE A()
c     the following 2 lines say that f2py should construct the
run_python function
cf2py intent(callback) RUN_PYTHON
      EXTERNAL RUN_PYTHON
      CHARACTER*10 ASTRING
c     the following line defines the signature for run_python function
cf2py call run_python(astring)
      PRINT*, "ENTERING A"
      ASTRING = "abcdefghijklm"
      CALL B(ASTRING)
      PRINT*, "LEAVING A"
      RETURN
      END

      SUBROUTINE B(ASTRING)
c
c       Cannot change this routine
c
      CHARACTER*10 ASTRING
      PRINT*, "ENTERING B"
      CALL C(ASTRING)
      PRINT*, "LEAVING B"
      RETURN
      END

      SUBROUTINE C(ASTRING)
      EXTERNAL RUN_PYTHON
      CHARACTER*10 ASTRING
      PRINT*, "ENTERING C"
      CALL RUN_PYTHON(ASTRING)
      PRINT*, "LEAVING C"
      RETURN
      END
c end of foo.f

Build the extension module:

f2py -c -m foo foo.f

Usage from Python:

>>> import foo
>>> def func(string): print "entering func(%r)" % (string)
...
>>> foo.a(func)
 ENTERING A
 ENTERING B
 ENTERING C
entering func('abcdefghijE')
 LEAVING C
 LEAVING B
 LEAVING A
>>> # note that function `a` takes callable argument while fortran subroutine `a` defined is as external function
>>> print(foo.a.__doc__)
a - Function signature:
  a(run_python,[run_python_extra_args])
Required arguments:
  run_python : call-back function
Optional arguments:
  run_python_extra_args := () input tuple
Call-back functions:
  def run_python(astring): return
  Required arguments:
    astring : input string(len=10)


PS: I am not sure where the ending 'E' comes from, I'll look into it..

HTH,
Pearu



On Fri, Oct 15, 2010 at 10:43 PM, Mark Oberfield
<Mark.Oberfield at noaa.gov> wrote:
> Hi,
>
> What I am attempting to do with f2py is to have a FORTRAN77 subroutine
> call a Python function.  The f2py tutorial/documentation provides an
> simple example of this, but what I want to do is a little more
> elaborate and I running into problems. First, the description of the
> puzzle.
>
> I have three FORTRAN routines, "A","B" & "C".  I have source code for
> routines "A" and "C" and I can change them to accomplish my task.
>
> Subroutine A was originally "PROGRAM A" but now will be called from
> Python while I pass the Python routine, 'pyfunction', that I want to
> be executed.
>
> I cannot modify the B Subroutine; please consider it to be inside a
> library for which I don't have the source code.
>
> Subroutine "C" is where I want to execute the Python routine. So the
> execution stack looks something like this:
>
> python=>A=>B=>C=>pyfunction
>
> Since function names are not allowed in FORTRAN common blocks, I wrote
> simple "C" routines to store and execute the function pointer when
> called from FORTRAN.  I would think this would work but I get an error.
>
> Here are the routines:
>
>      SUBROUTINE A(PYFUNCTION)
>      CHARACTER*10 ASTRING
>      CALL RGSTR_FUNCTION(PYFUNCTION)
>      ASTRING = ' '
>      CALL B(ASTRING)
>      PRINT *, ASTRING
>      RETURN
>      END
>
>      SUBROUTINE B(ASTRING)
>      C
>      C Cannot change this routine
>      C
>      CHARACTER*10 ASTRING
>      CALL C(ASTRING)
>      RETURN
>      END
>
>      SUBROUTINE C(ASTRING)
>      CHARACTER*10 ASTRING
>      CALL RUN_PYTHON(ASTRING)
>      RETURN
>      END
>
> c2py.c:
>        /*
>         * Declare a private static variable, a function pointer which is a
>         * Python routine
>         */
>        static void (*python_function)(char*);
>
>        void rgstr_function(void *fptr)
>        {
>           python_function = (void(*)(char*))fptr;
>        }
>
>        void run_python(char *astring)
>        {
>           (*python_function)(astring);
>        }
>
> I build it okay:
>
> f2py -c --f77flags=-qextname=a:b:c -m fmodule a.f b.f c.f c2py.c
>
> and fmodule.so gets built.
>
> Python 2.7 (r27:82500, Sep 20 2010, 13:40:19) [C] on aix5
> Type "help", "copyright", "credits" or "license" for more information.
>>>> import numpy
>>>> import fmodule
>>>> def simple():
> ...    return 'Hi there!'
> ...
>>>> fmodule.a(simple)
> Traceback (most recent call last):
>  File "<stdin>", line 1, in <module>
> TypeError: fmodule.a() 1st argument (pyfunction) can't be converted to float
>
> So I change "A" to specify that PYFUNCTION is a function:
>
>      SUBROUTINE A(PYFUNCTION)
>      EXTERNAL PYFUNCTION
>      CHARACTER*10 ASTRING
>      ASTRING = ' '
>      CALL RGSTR_FUNCTION(PYFUNCTION)
>      CALL B(ASTRING)
>      PRINT *, ASTRING
>      RETURN
>      END
>
> When I attempt to rebuild fmodule.so I get errors like this:
>
> "/tmp/tmpLEJzd5/src.aix-5.3-2.7/fmodulemodule.c", line 357.22: 1506-275 (S) Unexpected text pyfunction_cptr encountered.
> "/tmp/tmpLEJzd5/src.aix-5.3-2.7/fmodulemodule.c", line 357.3: 1506-045 (S) Undeclared identifier pyfunction_typedef.
> "/tmp/tmpLEJzd5/src.aix-5.3-2.7/fmodulemodule.c", line 371.3: 1506-045 (S) Undeclared identifier pyfunction_cptr.
>
> And fmodule.so does not get built.
>
> I am hoping that someone has done something similar and can pass on
> their wisdom.  Is this the right approach?  Is it even possible? If
> so, is there an easier way to accomplish this kind of task?
>
> Any advice or a solution would be most appreciated.
>
> Thanks!
>
> Mark
> --
> "A mother takes twenty years to make a man of her boy, and another
> woman makes a fool of him in twenty minutes."
>        -- Anonymous
>
> _______________________________________________
> f2py-users mailing list
> f2py-users at cens.ioc.ee
> http://cens.ioc.ee/mailman/listinfo/f2py-users
>



More information about the f2py-users mailing list