Editing .pyi Contracts¶
prik's generated .pyi files are editable wrapper contracts. They look like
Python stubs, but they also describe native calls, storage, and results. Edit
them to change the Python API without changing the native implementation.
This section explains supported edits and their effect. The complete grammar
will be covered by the Semantic .pyi Format reference.
Workflow¶
Generate a starter contract:
python3 -m prik generate --pyi native/solver.f90 --out contracts/solver
Edit contracts/solver/__init__.pyi and its leaf .pyi files, then build from
the entry contract:
python3 -m prik contracts/solver/__init__.pyi \
--native-fortran-sources native/solver.f90 \
--out-dir build/solver
You can provide compiled objects or libraries instead of source. In either
case, the .pyi files define the Python API and the native files provide its
implementation. prik does not reread the native source to restore declarations
you removed from the contract.
Keep an unchanged generated copy while experimenting. It makes each edit easy to compare and undo.
What Do You Want to Change?¶
Names, Visibility, and Modules¶
- How do I rename or alias a function, variable, or class?
- How do I reorganize a module's Python namespace?
- How do I flatten modules or choose what appears at the package root?
- How do I rename a function without changing its native target?
- How do I hide or remove a function, variable, class, or class member?
- How do I add a procedure that already exists in the native implementation?
- How do I set a module variable when the extension is imported?
- How do I declare a true read-only constant?
Functions and Classes¶
- How do I turn a module procedure into a method?
- How do I add or remove a function overload?
- How do I replace or remove a class constructor?
- How do I add or edit a type-bound or magic method?
Arguments, Calls, and Results¶
- How do I expose every native argument directly in its native order?
- How do I reorder or hide arguments, or turn outputs into Python results?
- How do I pass values, addresses, lengths, presence flags, or temporary work storage?
- How do I change a NumPy dtype, shape, layout, or optional argument?
- How do I add a default for a genuinely optional native argument?
- How do I return a replacement instead of mutating the original Python value?
- How do I pass checked storage or a raw memory address?
- How do I turn a native status into a Python exception?
- How do I release Python's Global Interpreter Lock (GIL) for a native call?
- How do I describe a callback signature in the contract?
Most edits change the Python surface: names, visibility, grouping, or how native arguments appear as Python parameters and results.
Some facts must continue to match the supplied implementation:
- native module and symbol names;
- procedure kind and native argument order;
- datatype, kind, rank, and storage category;
- callback signature; and
- required native imports.
prik checks that the contract is internally consistent. It cannot prove that an arbitrary object or shared library has the binary interface described by the contract. A contract that gives false native facts may fail while building, importing, or calling the extension.
Safety Checklist¶
Before rebuilding:
- Start from a contract generated for the same native implementation.
- Make one kind of edit at a time.
- Keep native types, ranks, argument order, and symbol names accurate.
- Do not invent optionality, ownership, or a release method.
- Rebuild and call the edited path once before making the next change.
When prik rejects an incomplete or unsafe rule, fix the contract instead of removing metadata until the build happens to pass.
Understanding Errors¶
- While loading the
.pyi: check Python syntax, imports, decorators, annotations, and import cycles. - While checking the contract: check duplicate exports, missing links, invalid projections, and public declarations that expose private types.
- While planning the wrapper: check ownership, lifetime, mutation, allocation, conversion, and release rules.
- While building or calling: check that the supplied implementation matches the declared native symbol and binary interface.
Errors include the contract path and declaration when that information is
available. Use --verbose to see the build commands; use --debug when a full
Python traceback is needed.
Next¶
Start with Exports and Modules for the most common API edits.