Exports and Modules¶
The entry __init__.pyi controls the extension's Python namespace. Leaf
.pyi files describe native modules and their declarations.
Choose the Package Shape¶
Generated entry contract:
from . import module1
from . import module2
Python then uses package.module1 and package.module2. To place both
modules' public names directly in package, edit the entry contract:
from .module1 import *
from .module2 import *
Selective imports and aliases also work:
from .module1 import solve
from .module2 import reset as clear
Changing entry imports changes only the Python namespace. It does not rename a native module or select a native object file.
Only declarations reachable from __init__.pyi are public. Missing files,
import cycles, and two different exports using the same Python name are
errors. Explicit aliases share the same native target. A module-variable alias
reads and writes the declaring variable's live native storage; it does not
create another variable.
Remove or Hide a Declaration¶
Delete a declaration to remove it from the Python API:
from prik.contracts import Int32
counter: Int32
def summarize() -> Int32: ...
The removed declaration is not regenerated during this build. This works for functions, variables, classes, methods, fields, constructors, and individual overload candidates.
Use @private for a function or class that another contract declaration still
needs:
from prik.contracts import Float64, private
@private
def scaled_counter() -> Float64: ...
Use private[...] for a variable or argument:
from prik.contracts import Float64, private
scale: private[Float64]
Both forms keep the declaration in the contract while hiding it from Python.
Add or Rename a Native Procedure¶
You may add a declaration when the procedure already exists in the supplied native implementation:
from prik.contracts import Float64
def norm2(values: Float64[:]) -> Float64: ...
In a module leaf, the filename identifies the native module and the function
name selects the native procedure. Use @bind(...) when the Python name
differs:
from prik.contracts import Float64, Int32, bind
@bind("solver_step")
def step(values: Float64[:]) -> Int32: ...
For a standalone native symbol, also use @standalone:
from prik.contracts import Float64, bind, standalone
@standalone
@bind("vendor_norm2")
def norm2(values: Float64[:]) -> Float64: ...
When an original Fortran procedure was declared bind(C), preserve that ABI
fact separately with @native_abi("c"). An optional @bind(...) then names
its linkable label; neither decorator changes the procedure's module or
standalone placement:
from prik.contracts import Float64, bind, native_abi
@native_abi("c")
@bind("vendor_norm2")
def norm2(values: Float64[:]) -> Float64: ...
This marker does not guarantee direct routing. Policy still validates the complete operation before choosing the C ABI entrypoint or a generated Fortran adapter.
The declaration must include the correct native arguments, types, ranks, and call shape. Adding Python syntax cannot create a native procedure that is not present in the linked implementation.
Set Module Values at Import¶
A writable scalar module variable may have a literal initial value:
from prik.contracts import Int32
counter: Int32 = 41
PRIK sets the module variable when the extension is imported. It remains writable. This works only when PRIK can write that native variable, and the initializer must be a literal rather than a call, name, or expression.
Use Final[...] only for a true read-only constant:
from prik.contracts import Final, Int32
nmax: Final[Int32] = 12
See Wrapping Modules for the resulting Python usage.
Re-export Module State¶
Import a variable into another leaf and include it in that leaf's __all__:
# state.pyi
from prik.contracts import Int32
counter: Int32
__all__ = ["counter"]
# facade.pyi
from .state import counter
__all__ = ["counter"]
Both package.state.counter and package.facade.counter access the same
native variable. Assignment, array mutation, allocation, pointer association,
and derived-object changes made through either namespace are immediately
visible through the other. PRIK completes the variable's access and ownership
policy once; the second namespace changes publication only.
A re-exported Final[...] parameter behaves differently, because a Fortran
parameter has no native storage to share and no setter. Each namespace
receives the same native constant value as an ordinary Python attribute:
- every publication starts at the value the Fortran
parameterdeclares; - assigning to one, such as
facade.limit, rebinds that Python name and does not modify the Fortran parameter; - assigning to one does not rebind the others, so the namespaces can disagree afterwards.
Assignment is not refused. Nothing enforces the constant at runtime, so treat a published parameter as a value each namespace holds rather than a shared read-only view of native state.
Next¶
Use Functions and Classes to add methods, overloads, or a custom constructor.