[f2py] Unexpected text pyfunction_cptr encountered

Mark Oberfield <[email protected]> Fri, 15 Oct 2010 15:43:21 -0400
Newsgroups gmane.comp.python.f2py.user
Message-ID <[email protected]>
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