F2PY Tests

I have talked previously about building fast libraries for Python using Fortran. The key software that facilitates easy integration of Fortran code into Python is F2PY. The user can write fast Fortran code with F2PY specific comments and compile it using F2PY to produce a high-speed ready-to-use Python library.

F2PY Link to heading

F2PY is an open-source utilty tool that provides an easy connection between Python and Fortran languages. It was written in the late 90s by Dr. Pearu Peterson. The first version of this software was released in 1999 when it was called Fortran to Python Interface Generator (FPIG). It remained a stand-alone software till 2007 when it was moved to the NumPy project. F2PY is currently maintained by the NumPy developer team and users can install numpy to use it.

Current status Link to heading

F2PY’s codebase is a bit older than the rest of Numpy. It has not had any major releases since 2009 ever since it became feature complete for Fortran 77. Beginners browsing its codebase in the current state mightfind it a bit difficult to read, understand and contribute to. Recently, efforts have been made to modernise its CLI and backend compilation process. The F2PY you will get to use will be a more user-friendly and faster CLI.

Codebase Link to heading

If you explore the current directory numpy/f2py you will open up a directory that looks likes this:-

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.
├── auxfuncs.py
├── capi_maps.py
├── cb_rules.py
├── cfuncs.py
├── common_rules.py
├── crackfortran.py
├── diagnose.py
├── f2py2e.py
├── f2py_testing.py
├── f90mod_rules.py
├── func2subr.py
├── __init__.py
├── __init__.pyi
├── __main__.py
├── rules.py
├── setup.cfg
├── setup.py
├── src
├── symbolic.py
├── tests
├── use_rules.py
└── __version__.py

Info
crackfortran.py file is the heart of f2py. It is responsible of reading Fortran code and extracting declaration information which is used to create .pyf files. f2py2e.py stands for F2PY Second edition. It is the CLI program that sits on top of crackfortran.py and provides user interface.

We will talk mostly about the test suite of F2PY in this blog which consists of the tests directory.

Testing f2py Link to heading

The file tree of tests directory presents us with these:-

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
./tests/
├── __init__.py
├── src
   ├── abstract_interface
   ├── array_from_pyobj
   ├── // ... several test folders
   └── string
├── test_abstract_interface.py
├── test_array_from_pyobj.py
├── // ... several test files
├── test_symbolic.py
└── util.py

Files starting with test_ contain tests for various aspects of f2py from parsing Fortran files to checking modules' documentation. src directory contains the Fortran source files upon which we do the testing. util.py is an interesting file. It contains superclass for class based tests in f2py which we will talk about later.

Adding tests Link to heading

If you wander through the tests folder snooping inside the files, you will find most tests are classes that inherit from util.F2PyTest superclass. This superclass is present in util.py file.

327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
class F2PyTest:
    code = None
    sources = None
    options = []
    skip = []
    only = []
    suffix = ".f"
    module = None
    module_name = None

    def setup(self):
        if sys.platform == "win32":
	.
	.
	.

This superclass contains many helper functions responsible for parsing and compiling test source files. Its child classes can override its sources data member to provide their own source files. This superclass will then compile the added source files upon object creation and their functions will be appended to self.module data member. Thus, the child classes will be able to access the fortran functions specified in source file by calling self.module.[fortran_function_name].

Anyone looking to add more tests can follow the same strategy. The user can add new test classes to the appropriate file in tests directory inheriting from util.F2PyTest superclass and test the fortran function by calling self.module.[fortran_function_name].

Example Link to heading

Suppose you found this bug in f2py. You corrected it and now want to add tests for it. How do you do that?

Well, first of all you would have to identify the correct file to add the test. This bug was related to incorrect parsing of Fortran code where subroutines ending with endsubroutine were not parsed correctly and therefore not compiled. Upon importing such subroutines, the user would encounter a missing attribute error. Since crackfortran.py file is responsible for parsing, we add the test to test_crackfortran.py file. The testing source file should also be created in numpy/f2py/tests/src/crackfortran/ directory. Similarly other tests can be added to already present test_ files based on user’s discretion.

First lets create a file my_source_file.f containing subroutines ending with endsubroutine. This file contains two self-explanatory Fortran subroutines :-

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
        subroutine subb(k)
          real(8), intent(inout) :: k(:)
          k=k+1
        endsubroutine

        subroutine subc(w,k)
          real(8), intent(in) :: w(:)
          real(8), intent(out) :: k(size(w))
          k=w+1
        endsubroutine

We will compile this Fortran code and test that it is working.

Open test_crackfortran.py to add the test for the previously added Fortran file:-

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class TestNoSpace(util.F2PyTest):
    sources = [util.getpath("tests", "src", "crackfortran", "my_source_file.f")]

    def test_module(self):
        k = np.array([1, 2, 3], dtype=np.float64)
        w = np.array([1, 2, 3], dtype=np.float64)
        self.module.subb(k)
        assert np.allclose(k, w + 1)
        self.module.subc([w, k])
        assert np.allclose(k, w + 1)

We override the sources data member to provide the source file. The source files are compiled and subroutines are attached to module data member when the class object is created. The test_module function calls the subroutines and tests their results.

Conclusion Link to heading

The current test suite of F2PY is a bit old. Modern python programmers accustomed to pytest might find its lack of fixtures, setups and tear downs little unappealing. However, once you get the hang of it, its easy to understand and improve. It is not an exhaustive test suite, but it is improving everyday. A new test suite for testing the CLI program f2py2e.py is being developed with modern pytest style testing and you can have a look at it here. F2PY and its test suite are sure to undergo heavy refactoring in the coming times.

See also in Beginning with F2PY Link to heading

comments powered by Disqus