7

Here's my directory structure,

├── test
│   ├── test.f90
│   ├── __init__.py
│   └── test.py

Now I want to make a package from this with an command line tool test. Now I have two options, 1. numpy distutils and 2. setuptools.

Problem with distutils is that it doesn't support entry points and it is also not recomended now. But it does compile the fortran code perfectly. Now as for setuptools I'm trying to use this code,

mod = Extension(name = 'foo.adt', sources = ['test/test.f90'])
setup(
  name = 'foo',
  packages = ['foo'],
  package_dir = {'foo':'test'},
  ext_modules = [mod],
  entry_points={
    'console_scripts': [
        'hello = foo.test:main',
    ],
  }
)

If I try to use this, it's throwing this error

error: unknown file type '.f90' (from 'test/test.f90')

So, I guess setuptools doesn't support fortran files? So, how do I compile the fortran code, create the package and create a entry point for that?

2 Answers 2

7

It's actually a pretty simple trick. Just import setuptools before importing setup from numpy.distutils.core and you're good to go. The explanation for this is that numpy.distutils is much more than just the vanilla distutils with some package-specific tweaks. In particular, numpy.distutils checks whether setuptools is available and if so, uses it where possible under the hood. If you're interested, look at the module's source code, paying attention to the usages of have_setuptools flag.

As usual, a Minimal, Complete, and Verifiable example:

so-55352409/
├── spam
│   ├── __init__.py
│   ├── cli.py
│   └── libfib.f90
└── setup.py

setup.py:

import setuptools  # this is the "magic" import
from numpy.distutils.core import setup, Extension


lib = Extension(name='spam.libfib', sources=['spam/libfib.f90'])

setup(
    name = 'spamlib',
    packages = ['spam'],
    ext_modules = [lib],
    entry_points={
        'console_scripts': [
            'hello = spam.cli:main',
        ],
    }
)

spam/cli.py:

from spam.libfib import fib


def main():
    print(fib(10))

spam/libfib.f90:

C FILE: LIBFIB.F90
      SUBROUTINE FIB(A,N)
C
C     CALCULATE FIRST N FIBONACCI NUMBERS
C
      INTEGER N
      REAL*8 A(N)
Cf2py intent(in) n
Cf2py intent(out) a
Cf2py depend(n) a
      DO I=1,N
         IF (I.EQ.1) THEN
            A(I) = 0.0D0
         ELSEIF (I.EQ.2) THEN
            A(I) = 1.0D0
         ELSE 
            A(I) = A(I-1) + A(I-2)
         ENDIF
      ENDDO
      END
C END FILE LIBFIB.F90

Build and install the package:

$ cd so-55352409
$ python setup.py bdist_wheel
...
$ pip install dist/spamlib-0.0.0-cp36-cp36m-linux_x86_64.whl
...
$ hello
[ 0.  1.  1.  2.  3.  5.  8. 13. 21. 34.]
Sign up to request clarification or add additional context in comments.

3 Comments

Glad I could help :-)
One question, why can't I just do from libfib import fib in the cli.py. If I have multiple files (say just plain python files), being imported multiple times with a complex folder structure then, do I have to iclude the package name in all the import statements ?
You can arrange the imports however you want, just make sure the compiled extension is placed correctly. E.g. if you want to import libfib, pass name=libfib to extension object. You can also place from spam.libfib import fib in spam/__init__.py and the fib function will be available in all submodules of spam without an explicit import statement.
-1

Start by making a folder called test under src/ folder e.g.

src
  -- <package>
    -- test
        --- ***

Then add a MANIFEST.in and add

recursive-include src/<package_name>/test *

Have these two lines included in your setup.py

from setuptools import setup, find_packages
package_dir={'': 'src'},
packages=find_packages('src'),

for your console scripts, do

entry_points={
            'console_scripts': [
                'hello=<package>.test:main',
                ],
        },

5 Comments

can you please explain
It is also explained here: docs.python.org/2/distutils/…. Also I found this here (docs.python.org/2/distutils/…) ``` anything that looks like a test script: test/test*.py (currently, the Distutils don’t do anything with test scripts except include them in source distributions, but in the future there will be a standard for testing Python module distributions) ``` So maybe you need to rename the foledr and files as something else
My problem is distutils doesn't create entry_points and setuptools doesn't compile fortran code, how this answer is solving that?
If you see the comment I made, it says anything that looks like a test script: test/test*.py (currently, the Distutils don’t do anything with test scripts except include them in source distributions, but in the future there will be a standard for testing Python module distributions) ", since your structure looks similar to test/test* maybe it is ignoring it, try renaming the file to something else and try
I did those and changed all the file/folder names , but still getting the same error. I still don't understand how this is going to make setuptools compile fortran files, which is something setuptools doesn't do.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.