What "convert Fortran to Python" usually means
A rewrite. Someone reads the deck and produces a Python file: every loop, every COMMON block, every magic constant transcribed into numpy. The reason this is the expensive plan is that a validated numerical code is a record — what your organization relies on is not merely the text of the routine, but the artifact the auditors signed and the twenty years of production runs behind it. A rewrite makes every consumer rebuild that trust from zero, in a language where 1-based array origins, assumed-size arguments and COMMON-block state are each their own subtle-difference hazard. Re-hosting keeps the compiled numerics and converts the interface around them instead.
| Approach | What it recovers | What it costs |
|---|---|---|
| Hand-rewrite into Python | none of the validated numerics | all regression work starts over; every translated loop is a new subtle-difference risk |
| Hand-written bindings | the numerics keep running | argument intent, array shapes and session lifecycle re-implemented per routine |
| Subprocess wrapper + stdout parsing | numerics and a rough call surface | no library, no schema, no tests; brittle at scale |
| nativegate | numerics unchanged plus generated bindings, tests, wheel, HTTP service, containers | the integration work you wanted anyway, generated once and pinned to source hashes |
What nativegate does on day one
One generator handles a tree: you point
ngate quickstart at a header or a Fortran deck, and
in one command it produces a pip-installable wheel and its
compile artifacts. The same source drives a FastAPI service
(the wheel) and a Dockerfile; you edit nothing twice.
pip install "nativegate[clang,build]"
ngate quickstart legacy/sim.f --name sim_api
pip install services/sim_api/dist/*.whl
Three minutes later, the same library is also an HTTP service:
ngate serve sim_api
curl -X POST "http://localhost:8000/solve?a=1&b=2"
The case, feature by feature
1. Handles 1990s-era sources unmodified
Fixed-form columns 6 and 73, INCLUDE decks, IMPLICIT typing, COMMON blocks, netlib's CS/CD dual-dialect marking, statement labels in the marked copy — all tested against real third-party Fortran, with the dialect resolution and label handling that the netlib specfun contact produced, published as an independently wrapped library.
2. Equality you can pin a build to
Every generated service can record a numerical baseline
(ngate golden record): the return values of each
entry point, tolerance, and the SHA-256 of every native source
byte. ngate golden verify fails a rebuild that
moved a number, and CI fails when a hash moved. For the
specfun showcase, all 14 pinned entry points came back
unchanged when the bindings were regenerated from the pristine
downloads — that is the whole argument for generation over
translation, measured.
3. One workflow, two languages
The same CLI, yaml schema, test suite and golden harness cover C++ (pybind11 through libclang) and Fortran (f2py). The team learns one interface, not one per language.
4. It refuses rather than mis-types
What it cannot bind is recorded as a skip with the reason: derived types it cannot flatten, callbacks (EXTERNAL dummies), dialect files without a chosen half. The list is published (DEFECTS.md), and every defect found in the wild is regression-tested once fixed — the specfun and quadpack contacts alone produced six of them.
5. A service, not just a wheel
The generated service ships optional API-key auth, a schema, request logging, readiness/liveness probes with SIGTERM draining, Kubernetes manifests and a reproducible, hash-locked Docker build — the deployment half a "just load the .so" wrapper leaves to you.
The case against hand-rolled wrappers
It is not that hand-written bindings are impossible — many teams have one, and it works while the API stays small. The cost grows with every new endpoint: array edges, intent, error flags, session lifecycles, all redone per routine and re-verified by nobody in particular. The generated vs hand-written comparison states the trade in measured numbers rather than adjectives.
What nativegate does not do
Stated plainly, because the narrowing helps you plan:
- It does not translate your Fortran to Python. The numerics compile from the original source — that is the point and also the limit.
- Routines taking an
external fcallback are refused for now, with a reason (QUADPACK-style integrators). Scoped in the roadmap. - fparser2 refuses Hollerith-era syntax gfortran compiles with a warning; such decks use the regex reader backend.
- Fortran COMMON-block state means one native call at a time per process — scale comes from more processes behind an ingress, and the generated Kubernetes manifests are shaped accordingly.
Where to look, without being asked to take our word for it
- The generator: source, test suite, DEFECTS.md and ROADMAP.md published as-is.
pip install "nativegate[clang,build]"— the tool installs from PyPI today.- specfun-py: netlib specfun wrapped end to end from the unchanged Fortran, values pinned in CI.
- The blog: what the tool does, what it refuses, and the defects the third-party contacts found — with the fix version listed for each.