PRIK — Python Runtime Interop Kit
Generate native Python bindings for Fortran, with editable .pyi contracts
and Pythonic APIs.
PRIK generates native Python bindings from Fortran projects, producing
importable extensions and editable .pyi contracts for Pythonic APIs.
It preserves modules, derived types, arrays, callbacks, and native behavior so you can shape the resulting API without writing low-level binding code.
Project status: Alpha. Core Fortran wrapper workflows are
implemented and tested across supported compilers, but public APIs may still
change before 1.0.
PRIK starts with Fortran-to-Python. Its semantic contract model is designed to support more native languages over time.
Read the documentation for installation, the user guide, examples, and reference material.
Contents
- Proven on real libraries
- See it in action
- Key Features
- Performance
- Current limitations
- Installation & Quick Start
- How it works
- Native Project Inputs
- Python API
- Development
- Citation
- License
- Documentation
Proven on real libraries
The maintained projects build real numerical libraries with PRIK and validate their Python behavior, not just whether the generated wrapper compiles.
| Project | Validated surface | Capabilities demonstrated |
|---|---|---|
| BLAS | All 155 discovered routines | Scalar, vector, and matrix operations; increments and leading dimensions; in-place updates; independent expectations and f2py comparisons |
| LAPACK | Complete implementation corpus with 127 reviewed double-precision routines | Linear solves, factorizations, eigenproblems, singular values, work arrays, and large multi-source linking |
| FFTPACK | All 31 public procedures | Fourier, cosine, and sine transforms; low-level workspaces; in-place arrays; allocatable results; NumPy and SciPy oracles |
| MINPACK | All 22 public procedures | Python callbacks; nonlinear and least-squares solvers; Jacobian and workspace writeback; immutable module constants |
Together they exercise arrays, callbacks, workspaces, in-place mutation, allocatable results, module constants, and multi-file linking. The dedicated Real Libraries CI lane builds and tests all four projects.
The complete example below builds with one command:
python3 -m prik points.f90 --out geometry
See it in action
Create points.f90:
module points
implicit none
type :: point
real(8) :: x = 0.0d0
real(8) :: y = 0.0d0
end type point
contains
subroutine move(item, dx, dy)
type(point), intent(inout) :: item
real(8), intent(in) :: dx, dy
item%x = item%x + dx
item%y = item%y + dy
end subroutine move
real(8) function norm_squared(item) result(value)
type(point), intent(in) :: item
value = item%x * item%x + item%y * item%y
end function norm_squared
end module points
Generated Python API:
import numpy as np
import geometry.points as points
item = points.point(x=np.float64(3.0), y=np.float64(4.0))
points.move(item, np.float64(1.0), np.float64(-2.0))
print(item.x, item.y) # 4.0 2.0
print(points.norm_squared(item)) # 20.0
No manual bindings are required. From this source, PRIK creates a Python namespace, a class with accessible fields, a mutating procedure, and a function.
Want a different Python API? Edit the generated .pyi contract to rename or
hide exports, flatten namespaces, define constructors and methods, or create
overloads. The
contract guide
shows the available edits.
Key Features
- Native APIs that feel like Python. Fortran modules become Python namespaces, while derived types become classes with fields and methods.
- First-class NumPy array interop. Pass ordinary NumPy arrays to native procedures, including multidimensional and in-place data, with generated dtype, shape, layout, and mutability handling at the language boundary.
- Managed access to native memory. Expose allocatable and pointer arrays without hiding their ownership, lifetime, allocation, or release operations.
- Python callbacks and native overloads. Pass Python callables into Fortran and expose generic interfaces as familiar Python overloads.
- Generated APIs you can reshape. Edit the generated
.pyicontract to rename, hide, reorganize, or overload the public interface, backed by readable generated docstrings. - Unsupported contracts fail before the build. PRIK identifies the exact boundary and reason before attempting code generation or compilation.
Performance
Low wrapper overhead, measured against NumPy's f2py.
The included benchmark suite runs both tools against the same Fortran kernels through their normal generated interfaces. Results are machine-dependent; the charts below come from the latest successfully deployed benchmark snapshot.
Runtime-call performance — values above 1.0× mean PRIK is faster.
Clean end-to-end build time — lower times are better.
See the complete results, test environment, and one-command reproduction instructions.
Current limitations
PRIK does not yet support:
- arrays of derived types;
- procedure pointers, including procedure-pointer module variables and callbacks retained after the wrapped call; or
- polymorphic outputs, mutable polymorphic arguments, polymorphic arrays,
unlimited polymorphism (
class(*)), abstract types, and deferred bindings.
Installation & Quick Start
PRIK requires Python 3.10 or newer, NumPy, Python development headers, standard build tools, and Fortran and C compilers. GNU Fortran is the default and is tested on Linux and macOS. LLVM Flang is tested on both platforms; Intel IFX is tested on Linux.
Install the published PRIK package in a virtual environment:
python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install --upgrade pip
python3 -m pip install prik
Check the installation:
prik --version
python3 -m prik --help
Contributors can instead clone
PyNumLab/prik and install an editable
checkout with python3 -m pip install -e ".[qa]".
With the points.f90 source from above in the current directory, build the
extension:
python3 -m prik points.f90 --out geometry
--out geometry selects the import name and the final shared-library name.
PRIK places the stable import file beside the source and keeps generated build
artifacts under __prik__/:
.
points.f90
geometry.so
__prik__/
geometry.<extension-suffix>.so
generated-wrapper sources
binding_support/
The Python code shown at the top of this README can now import geometry
directly.
Use --out-dir to place the ABI-specific extension and generated files in a
chosen build directory:
python3 -m prik points.f90 \
--out geometry \
--out-dir build/geometry
.
geometry.so
build/geometry/
geometry.<extension-suffix>.so
generated-wrapper sources
binding_support/
Inspect the generated contract
Generate the editable .pyi contract for the same points.f90:
python3 -m prik generate --pyi points.f90 --out contracts
The command preserves the Fortran module as a contract module:
contracts/
__init__.pyi
points.pyi
Generated contracts/points.pyi:
from prik.contracts import Addr, Arg, Float64, native_call
class point:
def __init__(
self,
*,
x: Float64 = 0.0,
y: Float64 = 0.0
) -> None: ...
x: Float64 = 0.0
y: Float64 = 0.0
@native_call([Arg(0), Addr(Arg(1)), Addr(Arg(2))])
def move(
item: point,
dx: Float64,
dy: Float64
) -> None: ...
def norm_squared(
item: point
) -> Float64: ...
The contract describes the generated Python class, fields, functions, exact NumPy scalar types, and native argument order. Editing it changes the wrapper API; it does not change the Fortran implementation.
Build from the contract
After editing the contract, rebuild the same Python API from the package entry and the original Fortran implementation:
python3 -m prik contracts/__init__.pyi \
--native-fortran-sources points.f90 \
--out geometry \
--out-dir build/geometry_from_pyi
The contract build has the same import name and module layout:
.
geometry.so
build/geometry_from_pyi/
geometry.<extension-suffix>.so
generated-wrapper sources
binding_support/
Import the extension from the explicit build directory when needed:
import sys
import numpy as np
sys.path.insert(0, "build/geometry_from_pyi")
import geometry.points as points
item = points.point(x=np.float64(3.0), y=np.float64(4.0))
points.move(item, np.float64(1.0), np.float64(-2.0))
print(points.norm_squared(item)) # 20.0
Inspect the native build
Use --verbose when you want to see the compiler commands and confirm which
wrapper flags reached the build:
python3 -m prik points.f90 \
--out geometry_debug \
--out-dir build/geometry_debug \
--jobs 4 \
--verbose \
--compiler gfortran \
--wrapper-fortran-flags=-O2 \
--wrapper-c-flags=-O2
The verbose output includes native source compilation, generated bridge
compilation, generated Python binding compilation, and the final link command.
Dependency-ready source files and the generated binding may compile
concurrently; --jobs 1 selects a serial diagnostic build.
The custom wrapper flags appear in the relevant command lines:
<fortran compiler> ... -O2 ... generated bridge ...
<python-binding compiler> ... -O2 ... generated Python binding ...
<fortran compiler> -shared ... -O2 ... geometry_debug ...
How it works
Fortran sources
-> compiler preprocessing and target-type probing
-> Fortran parser
-> semantic IR construction
-> post-IR policy completion and ordered wrapper plan
-> direct native-bridge and Python-binding lowering
-> native compilation and shared-library link
-> importable Python extension
For diagnostic and inspection commands beyond the main build path, start with
python3 -m prik --help, then continue to the
CLI command reference.
Native Project Inputs
Fortran builds default to gfortran. For a real project, replace the checked
input path with your source path, use --help to choose the compiler and native
project options you need, and enable --verbose when you want to audit the
exact compiler and linker commands.
Use --out to select generated contract locations, wrapper module names, or
explicit build directories, depending on the command mode.
Python API
Root entrypoints cover normal Fortran extension builds. Advanced parsing,
semantic conversion, and .pyi emission use their owning packages:
from prik import build_fortran_extension
result = build_fortran_extension(
"points.f90",
output_name="geometry",
output_dir="build/geometry_api",
)
print(result.module_name)
print(result.shared_library)
Parser and semantic entrypoints remain available independently for controlled strings, focused tests, and already-preprocessed inputs.
For native projects with macros, includes, or target flags, use the compiler-preprocessed CLI path or an equivalent preprocessing configuration.
Development
PRIK is created and maintained by Said Hadjout, with extensive use of AI-assisted software-development tools, particularly OpenAI Codex, for implementation, refactoring, testing, debugging, documentation, investigation, and review assistance.
Architecture, interoperability semantics, feature design, acceptance criteria, and final integration remain maintainer-directed. AI-assisted changes are subject to the same tests, compiler validation, real-library checks, and quality requirements as other changes.
Run the full suite from the repository root:
PYTHONPATH=. python3 -m pytest -q
Citation
If you use PRIK in research, cite the release you used.
10.5281/zenodo.21881987 covers all
archived releases and links to their version-specific records. Machine-readable
metadata is available in
CITATION.cff.
License
PRIK is distributed under the MIT License. Copyright (c) 2026 Said Hadjout.
Using PRIK does not impose the MIT License on the user's native sources or on wrapper code derived from those inputs. Users may distribute generated wrappers under terms of their choice. Bundled native-support files copied into generated builds remain MIT-licensed and must retain the included license notice when redistributed.
Documentation
- Documentation — Learn how to install and use PRIK
- Getting Started — Installation, verification, standalone procedures, modules, and rebuild workflow
- User Guide — Data types, functions, modules, arrays, derived types, callbacks, ownership, and runtime behavior
- Changelog — User-visible changes by release
Metadata
Release files for prik 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| prik-0.3.0.tar.gz | 697.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| prik-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.4 MB
Release files / prik-0.3.0.tar.gz
| Download URL | prik-0.3.0.tar.gz |
|---|---|
| Size | 697.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3fabc352a052cb464a0c22be8a88a1c68098346a5ecffd56a4311dfa9c550a3b
|
|
BLAKE2b-256 checksum How to use checksums |
7de8819648820c80d68d090b13ecfc637c1252c762f2c8e03927308f62fa41e9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 14, 2026.
Transparency logRelease files / prik-0.3.0-py3-none-any.whl
| Download URL | prik-0.3.0-py3-none-any.whl |
|---|---|
| Size | 727.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e7997313e11d29cb266d999b53153fa978eb6523100879479457f5393084f62a
|
|
BLAKE2b-256 checksum How to use checksums |
f75ec0ed4ce30f59d367566373b25ba7e6260c433a977a66e8b45adfafaf45e2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 14, 2026.
Transparency log