Wrapping Modules¶
A Fortran module becomes a child Python module (namespace) inside the generated extension.
Basic Usage¶
After building module_state.f90:
import sys
import numpy as np
sys.path.insert(0, "build/first-module")
import module_state
mod = module_state.module_state # child namespace
See First Wrapped Module for the complete source, build command, and usage examples.
Procedures¶
Module functions and subroutines become attributes of the child module:
print(mod.summarize()) # 15
print(mod.scaled_counter()) # 4.5
Standalone procedures (outside any module) remain at the extension root.
When compiling multiple source files, each Fortran module becomes its own child namespace, while standalone procedures stay on the extension root. The first source file usually determines the extension name (you can override with --out).
Public Variables and Constants¶
Supported public scalar variables are exposed as direct Python attributes:
mod.counter = np.int32(9)
print(mod.counter) # 9
print(mod.summarize()) # 21
print(mod.nmax) # 12 (read-only parameter)
parameterdeclarations become read-only constants in the generated contract.- Assigning to a constant in Python only creates a local shadow — it does not mutate the native value.
Module Arrays & Saved State¶
- Allocatable module arrays use the
Allocatable[T[...]]API. - Allocation, lifetime, NumPy views, and mutation rules are covered in the storage and objects section.
saveattributes (including procedure-localsavevariables) persist across calls.- Multiple Python imports of the same extension share the same native module state.
Shape the Module API With the Contract¶
Small contract edits can set initial values or hide names from Python:
from prik.contracts import Final, Float64, Int32, private
nmax: Final[Int32] = 12
counter: Int32 = 9
scale: Float64 = 2.0
saved_counter: private[Int32]
@private
def scaled_counter() -> Float64: ...
counterandscaleare set in the Fortran module when the extension is imported. They remain writable.private[T]hides a module variable;@privatehides a procedure. Both still exist in Fortran.Final[T] = valueis only for a true constant, such as a Fortranparameter. It does not turn a writable Fortran variable into a read-only view.
Deleting a declaration removes that name from the generated Python API. These edits do not create or rename native variables and procedures; those still need to exist in the compiled module.
For the complete rules, see Remove or Hide a Declaration and Set Module Values at Import.
Flatten Module Namespaces¶
The package entry __init__.pyi controls the Python import layout. Suppose an
extension named library contains two Fortran modules. prik generates:
# __init__.pyi
from . import module1
from . import module2
The modules remain child namespaces:
from library.module1 import func1
from library.module2 import func2
To place every public name directly on library, replace those imports with
wildcard imports:
# __init__.pyi
from .module1 import *
from .module2 import *
Python then uses the flattened API:
import library
library.func1()
library.func2()
# This is also valid:
from library import func1, func2
Public functions, variables, constants, and generated classes are exported at
the extension root. If the original module imports were replaced,
library.module1 and library.module2 are no longer exported. The native
Fortran modules and their storage do not move; only the Python API changes.
Wildcard imports never use import order to resolve a collision. If both modules export the same name, the wrapper build fails and asks for an explicit choice. Export aliases instead:
from .module1 import update as update_module1
from .module2 import update as update_module2
This produces library.update_module1 and library.update_module2. You can
also import only selected names instead of flattening every public declaration.
Build the edited entry using the
editable-contract workflow.
For all supported imports, aliases, and namespace layouts, see Choose the Package Shape.
Important Rules¶
- Private declarations are hidden from the Python API.
- Common blocks are not exposed as Python variables (only indirectly through procedures that access them).
- Module state is shared native storage — changes made through one reference are visible to all others.
- The extension name is derived from the source filename unless overridden.
Next¶
- Continue with Optional Arguments.
- Read Memory Management before keeping live views of module storage.