Repository: numpy/numpy

Issue:
f2py sometimes generates python3-incompatible C wrappings
f2py uses deprecated CPython functions (notably `PyString_FromStringAndSize`) when wrapping callbacks with string parameters. An example is the FORTRAN-77 subroutine

``` fortran
       SUBROUTINE BUG(CALLBACK, A)
          IMPLICIT NONE
          EXTERNAL CALLBACK
          DOUBLE PRECISION CALLBACK
          DOUBLE PRECISION A
          CHARACTER*1 R
          R = 'R'
!f2py intent(out) A
          A = CALLBACK(R)
       END SUBROUTINE
```

compiled with

```
f2py -c -m bug test.f # on Arch Linux, this is the python3 version of f2py
```

with the Python wrapping

``` python
#!/usr/bin/env python
from __future__ import print_function
import bug

def callback(code):
    if code == 'F':
        return 2.0
    else:
        return 3.0

print(bug.bug(callback))
```

Calling the Python2 version of `f2py` and running the script under Python 2.7 yields the correct answer. However, under Python3 we get

```
~/Documents/Code/Fortran/f2pyBug $ python3 test.py
Traceback (most recent call last):
  File "test.py", line 4, in <module>
    import bug
ImportError: /home/drwells/Documents/Code/Fortran/f2pyBug/bug.cpython-33m.so: undefined symbol: PyString_FromStringAndSize
```

I tested this under Arch Linux (fully updated) with Python 3.3.3 and Python 2.7.6. In both cases I used Numpy 1.8. I also tested this under Ubuntu (Quantal) with Python 3.2.3 and Python 2.7.3. In both cases I used Numpy 1.6.2.



Discussion:
Fyi, unfortunately f2py is not regularly maintained at this point, so you
may or may not find someone stepping up to fix it. Your best bet might be
to see if you can figure out a patch yourself - we'd definitely be
interested in merging such a thing.
On 3 Feb 2014 13:51, "drwells" notifications@github.com wrote:

> f2py uses deprecated CPython functions (notably PyString_FromStringAndSize)
> when wrapping callbacks with string parameters. An example is the
> FORTRAN-77 subroutine
> 
> ```
>    SUBROUTINE BUG(CALLBACK, A)
>       IMPLICIT NONE          EXTERNAL CALLBACK
>       DOUBLE PRECISION CALLBACK
>       DOUBLE PRECISION A
>       CHARACTER*1 R
>       R = 'R'!f2py intent(out) A
>       A = CALLBACK(R)
>    END SUBROUTINE
> ```
> 
> compiled with
> 
> f2py -c -m bug test.f # on Arch Linux, this is the python3 version of f2py
> 
> with the Python wrapping
> 
> #!/usr/bin/env pythonfrom **future** import print_functionimport bug
> def callback(code):
>     if code == 'F':
>         return 2.0
>     else:
>         return 3.0
> print(bug.bug(callback))
> 
> Calling the Python2 version of f2py and running the script under Python
> 2.7 yields the correct answer. However, under Python3 we get
> 
> ~/Documents/Code/Fortran/f2pyBug $ python3 test.py
> Traceback (most recent call last):
>   File "test.py", line 4, in <module>
>     import bug
> ImportError: /home/drwells/Documents/Code/Fortran/f2pyBug/bug.cpython-33m.so: undefined symbol: PyString_FromStringAndSize
> 
> I tested this under Arch Linux (fully updated) with Python 3.3.3 and
> Python 2.7.6. In both cases I used Numpy 1.8. I also tested this under
> Ubuntu (Quantal) with Python 3.2.3 and Python 2.7.3. In both cases I used
> Numpy 1.6.2.
> 
> —
> Reply to this email directly or view it on GitHubhttps://github.com/numpy/numpy/issues/4256
> .

These are likely easy to fix by adding the missing defines to
numpy/f2py/src/fortranobject.h


Resolving patch:
diff --git a/numpy/f2py/cfuncs.py b/numpy/f2py/cfuncs.py
--- a/numpy/f2py/cfuncs.py
+++ b/numpy/f2py/cfuncs.py
@@ -312,7 +312,7 @@
 needs['pyobj_from_string1']=['string']
 cppmacros['pyobj_from_string1']='#define pyobj_from_string1(v) (PyString_FromString((char *)v))'
 needs['pyobj_from_string1size']=['string']
-cppmacros['pyobj_from_string1size']='#define pyobj_from_string1size(v,len) (PyString_FromStringAndSize((char *)v, len))'
+cppmacros['pyobj_from_string1size']='#define pyobj_from_string1size(v,len) (PyUString_FromStringAndSize((char *)v, len))'
 needs['TRYPYARRAYTEMPLATE']=['PRINTPYOBJERR']
 cppmacros['TRYPYARRAYTEMPLATE']="""\
 /* New SciPy */


Test patch:
diff --git a/numpy/f2py/tests/test_callback.py b/numpy/f2py/tests/test_callback.py
--- a/numpy/f2py/tests/test_callback.py
+++ b/numpy/f2py/tests/test_callback.py
@@ -34,6 +34,17 @@ class TestF77Callback(util.F2PyTest):
        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
+
     """
 
     @dec.slow
@@ -103,6 +114,19 @@ def mth(self):
         r = t(a.mth)
         assert_( r==9, repr(r))
 
+    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, repr(r))
+
+
 if __name__ == "__main__":
     import nose
     nose.runmodule()