How to: call Fortran from Python

I needed to call Fortran functions from Python-Cython. To do this, I adapted a method described in fortran90.org and some lines of code from this Stackoverflow page. Since it didn't work without modification, I present the result here:

A Fortran file modf.f90:

module wrapper_f_to_c

use iso_c_binding, only: c_double, c_int

implicit none

contains

subroutine c_func(a, b, input, output) bind(c)
real(c_double), intent(in) :: a
real(c_double), intent(in) :: b
integer(c_int), intent(in) :: input
real(c_double), intent(out) :: output

write(*,*) a*b
output = 10*input

end subroutine

end module

A Cython file mod.pyx:

from numpy cimport ndarray
from numpy import empty

cdef extern:
    void c_func(double *a, double *b, int *input, int *output)

def func(double a, double b, int input):
    int output
    c_func(&a, &b, &input, &output)
    return output

A Python file for building Fortran code with a setup.py file:

import os
import platform
import subprocess

libs_gfortran = ['gfortran']
libs_mpifortran = ['mpi_f90', 'mpi_f77']

path_base = os.path.join(
    'build',
    'temp.' + platform.system().lower() + '-'
    + platform.machine() + '-'
    + '.'.join(platform.python_version_tuple()[:2]))

def build_objects_from_fortran(sources):
    objects = []

    for source in sources:
        path_dir, name = source.rsplit(os.path.sep, 1)
        path_dir_objet = os.path.join(path_base, path_dir)
        if not os.path.exists(path_dir_objet):
            os.makedirs(path_dir_objet)
        path_objet = os.path.join(
            path_dir_objet,
            os.path.splitext(name)[0] + '.o')
        objects.append(os.path.relpath(path_objet))
        command_compile_fortran_mod = (
            'mpif90 ' + ' -O3 -fPIC -J ' + path_dir_objet + ' '
            + source + ' -c -o ' + path_objet)
        print(command_compile_fortran_mod)

        code = subprocess.check_output(command_compile_fortran_mod, shell=True)

        print(code)

    return objects

if __name__ == '__main__':
    path_source = 'pack0/source'
    objects = build_objects_from_fortran([path_source + '/modf.f90'])
    print(objects)

The setup.py file:

from setuptools import setup
from Cython.Distutils import build_ext
from Cython.Distutils.extension import Extension

import os
os.environ['CC'] = 'mpicc'

path_source = 'pack0/source'

from setup_fortran import (
  build_objects_from_fortran, libs_gfortran, libs_mpifortran)
objects = build_objects_from_fortran([path_source + '/modf.f90'])

# for include directories
import numpy
import mpi4py

files = ['mod.pyx']

ext = Extension(
    name='pack0.m0',
    libraries=['mpi'] + libs_gfortran + libs_mpifortran,
    sources=[path_source + '/' + f for f in files],
    include_dirs=[numpy.get_include(),
                  mpi4py.get_include(),
                'include'],
    extra_objects=objects)

setup(
    name='pack0',
    packages=['pack0'],
    cmdclass={'build_ext': build_ext},
    ext_modules=[ext])

Build with python setup.py build_ext --inplace.