Fortran 90/95 is a different binding problem from F77: modules, INTENT declarations, derived types, KIND parameters. That's mostly good news — the code says more about itself — but each modern feature carries a practical consideration when a Python caller is on the other side. This post walks them.
Modules become scopes — decide what that means
f2py maps module procedures into nested module structures; there
have historically been wrinkles in how generations of f2py
handled that nesting, and working libraries around it is a real
pattern. The practical question for an interface: is the Python
surface module.routine() or a flat list? Decide it
early — downstream imports absorb whatever you choose, but not
whatever you re-decide later.
KIND is code, not annotation
real(dp) where dp is a module
PARAMETER is the standard correct spelling in
Fortran and a hazard in wrapper generation: the parameter has
to make it into every intermediate the wrapper reads, or the
argument degrades to a different real width and the build dies
later in a mismatch that names neither dp nor the argument.
nativegate resolves parameterized kinds to literals on a
generated copy for exactly this reason. If you're generating
by hand, plan for it.
Derived types: the feature f90wrap handles best
Derived types are where F90 gets genuinely object-shaped, and where tool support matters most. f90wrap wraps type-bound procedures across into Python classes; nativegate today takes derived types in a narrower case (all-scalar components, type defined in the same file, module-contained, free-form source) and refuses those outside that envelope with a stated reason. Mass wanted types → wider-than-us: use the tool built for it (f90wrap). Both roads honor the same rule: unsupported isn't guessed; unsupported is refused.
Assumed-shape and array arguments
Explicit-shape arrays compile into workable signatures; assumed-shape ones need an explicit interface available at bind time — a fact that is convenient from Fortran and a wrinkle from tooling that predates the calling convention. This is one of the honest variance points between generators; test the actual call round-trip before trusting a signature.
The F90 checklist, condensed
- Free-form source: fine — parses cleanly everywhere.
- Module structure: decide your Python surface shape once.
- KIND parameters: resolve them into the generated copy.
- Derived types: f90wrap best-in-class; document your envelope.
- INTENT: now written down — use it, and generate can infer.
The general F90 note, then: easier than F77 in every dimension except the ones your code spans. Choose tools per construct, keep the numeric baseline from day one, and the modernization rides on the same rails as the F77 case.