| title | Development Workflow |
|---|---|
| audience | developers, contributors |
| prerequisites | repository checkout, Python 3.10 or newer |
| related | index.md, quality-assurance.md |
| status | maintained |
| publication | draft |
This guide is for changing prik. It maps user-visible behavior to its owning implementation and tests, then gives focused change and verification workflows.
Install the project and QA dependencies:
python3 -m pip install -e ".[qa]"Run the smallest relevant test while iterating, then run the full suite:
PYTHONPATH=. python3 -m pytest -q tests/fortran/command_line_interface/pipeline/
PYTHONPATH=. python3 -m pytest -qBefore changing a public behavior, trace it through these layers:
For example, a new CLI stage option normally requires:
- A focused contract test in
tests/fortran/command_line_interface/pipeline/. - Dispatch or output routing in
prik/cli.py. - Preprocessing tests if the option changes source loading.
- A copy-paste command in the relevant user guide or checked example.
- A tutorial update only when the main user workflow changes.
Documentation must describe implemented behavior, not intended behavior. Treat a support claim as established only when it is traceable to current implementation plus one of these forms of evidence:
- a focused test that proves the contract;
- a maintained fixture test that proves generated output;
- a repository command that has been run against a checked fixture;
- an explicit parser or semantic reference inventory backed by tests.
Use these documentation roles consistently:
| Document | Role |
|---|---|
| Getting Started | Main supported user workflow and boundaries |
| Examples Gallery | Checked commands and Python API recipes |
| Fortran wrapper reference | Implemented Fortran runtime contract, mechanism, ownership, and build modes |
| Fortran parser reference | Developer inventory for the Fortran frontend |
| Semantic IR reference | Accepted semantic IR and datatype contract |
| Semantic .pyi format | User-visible semantic .pyi syntax and roadmap |
When adding a user example:
- Prefer a checked repository fixture or a short inline source string.
- Run the command or snippet from the repository root.
- Add or identify the focused test that owns the behavior.
- State limitations next to the example when metadata is preserved but not
executed, such as
@native_callprojection metadata.
tests/shared/docs/test_examples.py executes explicitly marked
bash CLI examples and python API snippets from README.md and Markdown
files under docs/. Bash examples must be python3 -m prik commands; the test replaces python3
with the active test interpreter and runs them without a shell. It rejects
shell operators, output-writing options, and options that select custom
executables or preprocessing command templates. Python snippets run with the
active test interpreter.
Wrapper examples that need native compilation should use
build_fortran_extension with TemporaryDirectory so verification does not
leave build artifacts in the checkout.
Mark a command that only needs to exit successfully:
<!-- prik-doc-test: run -->
```bash
python3 -m prik semantics tests/fortran/source_parsing/parsing/fixtures/general/basic_subroutine.f90
```Mark a command whose stdout must match the documentation exactly:
<!-- prik-doc-test: exact -->
```bash
python3 -m prik parse tests/fortran/source_parsing/parsing/fixtures/general/basic_subroutine.f90
```
<!-- prik-doc-test-output -->
```text
File: tests/fortran/source_parsing/parsing/fixtures/general/basic_subroutine.f90
...
```Use exact checks for stable human-readable output. Use run checks for large
JSON or semantic payloads whose detailed contract is already covered by
focused tests. The same markers can precede a python fenced block. Do not
mark placeholder commands, snippets that modify the checkout,
environment-dependent compiler recipes, or intentionally failing diagnostic
examples.
When a command reads a checked fixture, include its source input in the user documentation and verify the displayed source against the fixture:
<!-- prik-doc-source: tests/fortran/source_parsing/parsing/fixtures/general/basic_subroutine.f90 -->
```fortran
module m1
...
end module m1
```Append a target profile to an exact marker only for compiler-generated output that is intentionally architecture-specific:
<!-- prik-doc-test: exact linux-x86_64 -->Off-target checks are skipped. The matching profile must still run the command and compare its complete output.
Run the documentation checks directly:
PYTHONPATH=. python3 -m pytest -q tests/shared/docs/test_examples.py- Getting Started: supported end-to-end user workflow and current boundaries.
- Examples Gallery: checked CLI and Python API recipes.
- Fortran parser reference: Fortran frontend scope, recursive parser organization, API/CLI behavior, diagnostics, fixture workflow, semantic handoff, and tests.
- Semantic
.pyiformat: user-visible.pyiloader/printer contract and roadmap. - Quality assurance: active QA commands, tool benefits, known defects found by each tool, and scheduled triage process.
The tutorial, examples cookbook, .pyi format, and semantic reference describe
CLI stages, .pyi syntax, datatype names, and wrapper-plan diagnostics. The developer
task is to keep those user-visible contracts stable, tested, and traceable to
implementation files.
| User-visible area | Main implementation files | Main tests |
|---|---|---|
| Fortran parse output | prik/parsers/fortran/parser.py, prik/parsers/fortran/models.py, prik/parsers/fortran/lexer.py |
tests/fortran/source_parsing/parsing/, tests/fortran/source_parsing/parsing/test_fortran_fixture_suite.py, tests/fortran/source_parsing/parsing/test_error_handling.py |
| CLI stage selection and output | prik/cli.py, prik/parsers/fortran/cli.py |
tests/fortran/command_line_interface/pipeline/ |
| Fortran target type probing and cache | prik/probes/fortran_types.py |
tests/fortran/data_types/probes/test_fortran_type_probes.py |
| Generated target datatype mapping examples | prik/probes/report.py |
tests/shared/types/test_mapping_report.py, tests/shared/docs/test_examples.py |
| Fortran to semantic IR | prik/semantics/fortran2ir.py, prik/semantics/models.py |
tests/fortran/semantic_ir/semantics/ |
.pyi printing |
prik/wrapper_codegen/printers/pyi_printer.py |
tests/fortran/semantic_pyi_format/pipeline/, tests/fortran/semantic_pyi_format/pipeline/test_modern_example.py |
.pyi parsing/loading/editing |
prik/parsers/pyi/parser.py, prik/pipeline/pyi.py, prik/semantics/pyi2ir.py |
tests/fortran/semantic_pyi_format/ |
| Semantic policy completion | prik/semantics/policy_completion.py, prik/semantics/ownership.py |
tests/fortran/infrastructure/policy/ and feature-local policy/ directories |
| Fortran wrapper orchestration | prik/pipeline/build.py |
tests/fortran/building_shared_library/end_to_end/test_source_build_modes.py, tests/fortran/building_shared_library/end_to_end/test_multi_source_builds.py |
| Wrapper planning, owner-local errors, and direct lowering | prik/wrapper_codegen/plan.py, prik/wrapper_codegen/planner.py, prik/wrapper_codegen/generator.py |
tests/fortran/infrastructure/wrapper_codegen/, feature-local wrapper_codegen/ stages |
| Native compilation and binding support | prik/compiling/, prik/binding_support/ |
tests/fortran/building_shared_library/end_to_end/test_runtime_compatibility.py, tests/fortran/building_shared_library/end_to_end/test_source_build_modes.py |
| Executable Markdown examples | README.md, docs/*.md |
tests/shared/docs/test_examples.py |
Organize generators and printers using FortranParser in
prik/parsers/fortran/parser.py as the structural reference. A developer
should be able to read each class from top to bottom in the same order that
data moves through it:
- The class docstring states the class's responsibility and lists its method sections.
- Construction and public entrypoints come first.
- Dispatched model handlers follow, grouped by feature and pipeline order.
Their names use the class's configured visitor prefix, for example
_visit_<ModelType>,_print_<ModelType>, or_parse_<ModelType>. - Helpers immediately follow the visitor group that owns them, or appear in a final low-level helper section when several visitor groups share them.
- Every method has a short contract docstring. The docstring explains the method's purpose or invariant; it does not restate its name.
Use the same visible section banners as FortranParser, for example
Public entrypoints, Module visitors, Function visitors, and Shared helpers. Keep related visitors adjacent instead of sorting methods merely by
name.
All model-type dispatch goes through prik.utilities.visitor.ClassVisitor._visit and a
matching <prefix>_<ClassName> handler. Parser-model converters, semantic
lowering, .pyi AST visitors, bridges, bindings, and printers share that one
implementation; do not duplicate its MRO lookup in an individual class.
An explicit table is allowed only for a genuine second dispatch dimension,
such as a completed policy action or primitive ABI datatype mapping. Such a
table must not replace model-class visitation. Do not add a second independent
visitor family, visit_<ClassName>, or scattered isinstance dispatch
schemes.
A method that performs ordinary work but is not a dispatch target must have a
descriptive helper name rather than a visitor-shaped name.
Keep functionality on the class that owns its state and policy. A module-level function is justified only when it is a deliberate public functional API or a genuinely stateless utility shared by unrelated classes. Do not retain a module-level function only to preserve an old internal call path.
User-visible .pyi syntax is first parsed to Python AST by
prik/parsers/pyi/parser.py, loaded from text/files by
prik/pipeline/pyi.py, converted to semantic IR by
prik/semantics/pyi2ir.py, and printed by
prik/wrapper_codegen/printers/pyi_printer.py. The converter and printer operate on
prik/semantics/models.py.
Important implementation rules:
Addr(T)andAddr(T)are storage contracts, not just pretty syntax.- Array subscriptions such as
Float64[n]are semantic array contracts. Annotated[..., ORDER_F]andORDER_ANYare non-default array storage metadata. Plain multidimensional Fortran.pyiarrays useORDER_F; do not print or retain that default marker in a generated contract.Allocatable[T[...]]andPointer[T[...]]are descriptor-handle wrappers around the array storage contract. Output and writeback behavior is represented by writable storage plusReturns["name", T]when a Python result is projected.
Final[T]is the public constant spelling. Do not reintroduceConstantas user-facing.pyisyntax.@native_callis projection metadata. Use it only when the Python-visible signature intentionally differs from the native signature.- Generated stubs should preserve behavior-changing native contracts while
staying compact; exact source intent that does not change execution can stay
in semantic IR instead of the printed
.pyi. - Use
SourceName("...")only when a source identifier cannot be used as the Python target. Do not infer source identifiers from normalized Python names. - Binding locals derived from a Python-visible argument must use the reserved
bound_namespace. Generated binding sources include Python, standard-library, optional descriptor, NumPy, and runtime headers, so their imported identifier sets are not a stable public-name vocabulary. - Omit
Polymorphiconly for the passed-object dummy of a type-bound procedure, where the binding itself restores that native fact. Ordinaryclass(T)arguments must retain it.
When changing .pyi syntax:
- Add or update parser tests in
tests/fortran/semantic_pyi_format/parsing/. - Add or update printer tests in
tests/fortran/semantic_pyi_format/pipeline/. - Update fixture tests only if the public generated contract changes.
- Update the relevant User Guide or checked example if users need to write or read the new syntax.
- Update Semantic .pyi format for the full user-facing reference.
- Update Semantic IR reference if the underlying semantic IR contract changes.
User-visible datatype names are semantic names, not raw parser spellings. Mapping happens during parser-to-IR conversion:
- Fortran intrinsic/kind mapping and compiler storage-fact application live in
prik/semantics/fortran2ir.py. - The shared dtype names and storage contracts live in
prik/semantics/models.py. - Compiler-measured mapping snapshots are generated by
prik/probes/report.py.
When changing datatype mapping:
- Add focused Fortran conversion tests in
tests/fortran/semantic_ir/semantics/. - Add
.pyiprinter/loader coverage if the emitted syntax changes. - Update semantic fixtures only when serialized semantic IR intentionally changes.
- Update Semantic IR reference, plus the relevant User Guide or checked example when the visible user workflow or examples change.
- Regenerate and update the exact target mapping snapshots in Semantic IR reference. The executable documentation test must match the complete output of:
python3 -m prik probe --language fortran --compiler gfortran --format markdownFor Fortran, keep both modern and legacy spellings in the generated report.
Legacy numeric type*N forms carry fixed total storage; compiler-dependent
default, kind, DOUBLE PRECISION, and DOUBLE COMPLEX forms use probe facts.
Diagnostics belong to the earliest stage that has enough facts to explain the failure. Parsers report source syntax and preprocessing faults. Semantic conversion reports facts that cannot form a valid contract. Policy completion records every lowering decision; the wrapper planner reports an unsupported completed policy with its owner path. Add focused tests to that owning stage, and update the relevant user guide when a user can correct the input or contract.
Do not move wrapper policy into parsers. Parsers can preserve:
- source locations;
- declaration and signature facts;
- type, pointer, array, callback, and aggregate facts;
- preprocessor provenance and diagnostics;
- unresolved references.
Post-IR policy completion and wrapper planning decide:
- ownership and lifetime;
- callback registration/unregistration policy;
- output-buffer projection;
- hidden pointer/size projection;
- ABI shim requirements;
- Python-visible signature adaptation.
The user-facing stages all start in prik/cli.py, but each stage owns a
different layer of the pipeline.
prik/cli.py is the shared command-line entrypoint. It is responsible for:
- rejecting ambiguous directories and unknown suffixes without
--language; - building
PreprocessingConfig; - dispatching
parse,semantics,generate, andprobe; - defaulting recognizable Fortran sources to a wrapper build when no subcommand is selected;
- routing the default build and
generate --sources|--makefilethroughprik/pipeline/build.py; - routing text, JSON, and
--outoutput.
The package-specific prik/parsers/fortran/cli.py remains for the Fortran parser
package entrypoint. New cross-language user behavior normally belongs in
prik/cli.py.
prik/pipeline/preprocessing.py owns compiler-backed preprocessing and provenance. The
main value object is PreprocessingConfig; the main execution path is
run_compiler_preprocessor_with_recipe(...).
Important contracts:
- The preprocessing recipe is part of the parser payload when preprocessing happened. It records compiler, adapter, argv, include directories, defines, undefs, standard, extra compiler args, included files, source mappings, and diagnostics.
Keep source loading, parser models, and semantic conversion separate. Semantic converters accept parsed models; they must not hide compiler preprocessing or source loading inside conversion helpers.
Fortran direct Python API, no CPP/FPP macros:
from prik import parse_fortran_file
from semantics.fortran2ir import fortran_module_to_semantic_module
parsed = parse_fortran_file(source, filename="visibility_mod.f90")
semantic = fortran_module_to_semantic_module(parsed.modules[0])parse_fortran_file(...) runs the parser's internal line preparation:
source-form detection, comment stripping, and continuation folding. It does
not expand #define, #ifdef, or other CPP/FPP directives. Raw CPP/FPP
directives are rejected with PARSE_PREPROCESSING_REQUIRED.
Fortran with macros or textual configuration must be compiler-preprocessed before parsing:
from pathlib import Path
from prik import parse_fortran_file
from semantics.fortran2ir import fortran_file_to_semantic_modules
from prik.pipeline.preprocessing import PreprocessingConfig, preprocess_source
path = Path("configured.F90")
preprocessed = preprocess_source(
path,
language="fortran",
config=PreprocessingConfig(
mode="compiler",
compiler="gfortran",
defines=["USE_MPI", "N=32"],
include_dirs=["include"],
),
)
parsed = parse_fortran_file(preprocessed.source, filename=str(path))
modules = fortran_file_to_semantic_modules(parsed)Choose the Fortran semantic helper from the parser model shape:
fortran_module_to_semantic_module(parsed.modules[0])for one selected module.[fortran_module_to_semantic_module(m) for m in parsed.modules]when a file contains multiple modules and no top-level standalone procedures matter.fortran_file_to_semantic_modules(parsed, standalone_module_name=...)when top-level procedures should become a synthetic semantic module too.fortran_project_to_semantic_modules(project)when project-level module and derived-type context matters.
Fortran parameter values and kind expressions are not CPP macros. If the
parser leaves a Fortran compile-time expression symbolic, collect missing
values with collect_semantic_compile_time_requirements(parsed), evaluate
them with the target compiler or a reusable type report, and pass
compile_time_values=... to the semantic converter. The shared CLI semantic
stage performs this target probing when a Fortran compiler or report is
configured; direct API callers must do it explicitly.
Input shapes are part of the contract:
parse_fortran_file(source_or_path, filename=...)accepts inline source text. It reads from disk only whensource_or_pathnames an existing file andfilenameis omitted. Passfilenamewith inline text for diagnostic provenance.preprocess_source(path, language=..., config=...)is path-based because it shells out to a compiler. Feedpreprocessed.sourceto the parser afterward.parse_pyi_text(...)accepts inline.pyisource text and returns Python AST.convert_pyi_to_ir(...)converts that parsed AST to semantic IR.pyi_text_to_semantic_module(...),pyi_file_to_semantic_module(...), andpyi_paths_to_semantic_modules(...)combine parsing and conversion for inline text, one file, or a file set.- The CLI accepts source,
.pyi, and directory paths. It does not accept inline source text on the command line.
CLI source stages:
CLI .pyi wrapper build:
.pyi path(s) or directory
-> prik/parsers/pyi/parser.py
-> prik/pipeline/pyi.py pyi_paths_to_semantic_modules(...)
-> prik/semantics/pyi2ir.py
-> SemanticModule list
-> prik/semantics/policy_completion.py
-> complete_semantic_policies(...)
-> WrapperPlanner.build(...)
Generating .pyi from source is semantic conversion plus printing. In Python
API code, keep those calls visible:
from prik import emit_module_stubs, parse_fortran_file
from semantics.fortran2ir import fortran_file_to_semantic_modules
parsed = parse_fortran_file(source, filename="api.f90")
modules = fortran_file_to_semantic_modules(parsed)
stubs = emit_module_stubs(modules)Loading or editing .pyi is the opposite direction:
from prik import pyi_paths_to_semantic_modules
modules = pyi_paths_to_semantic_modules("interfaces")Use the .pyi helpers by input shape:
parse_pyi_text(source, filename=...)fromprik.parsers.pyifor parser-only AST parsing.convert_pyi_to_ir(tree, module_name=..., source=...)frompyi2ir.pyfor AST-to-IR conversion.pyi_text_to_semantic_module(source, module_name=..., filename=...)frompyi_pipeline.pyfor inline text.pyi_file_to_semantic_module(path, module_name=...)for one file.pyi_paths_to_semantic_modules(paths_or_directory)for a set of interfaces that may reference each other.
The .pyi pipeline uses a per-operation in-memory conversion cache. Wrapper
entry-contract discovery reuses the same converted modules when it later builds
the reconciled contract bundle, so an imported file is not parsed and converted
twice in one build. Do not make this cache process-global: semantic modules are
mutated by reconciliation, export selection, and policy completion.
Compiler preprocessing flags all flow through PreprocessingConfig:
| CLI flag | PreprocessingConfig field |
Notes |
|---|---|---|
--compiler |
compiler |
Exact executable for direct preprocessing and automatic type probes. |
--preprocessor-adapter |
adapter |
Adapter family, including command-template. |
--preprocess-template |
command_template |
Custom command; requires --preprocessor-adapter command-template. |
-I / --include-dir |
include_dirs |
Passed to compiler preprocessing and native Fortran include expansion. |
-D / --define |
defines |
Macro definitions for compiler preprocessing. |
-U / --undef |
undefs |
Macro undefinitions for compiler preprocessing. |
--std |
std |
Passed as -std=.... |
--compiler-arg |
compiler_args |
Raw target/sysroot/compiler options. |
--public-include, --private-include, --include-exposure |
include exposure fields | Controls provenance exposure, not parser grammar. |
Fortran target datatype mapping and compile-time path:
Fortran source
-> parse_fortran_file(...)
-> collect_semantic_compile_time_requirements(...)
-> evaluate_fortran_type_requirements(...)
-> collect_fortran_type_storage_requirements(...)
-> evaluate_fortran_type_facts(...)
-> fortran_module_to_semantic_module(..., compile_time_values=..., type_facts=...)
prik/pipeline/build.py::build_fortran_extension(...) and
prik/pipeline/build.py::build_pyi_extension(...) are the public orchestration
boundaries for wrapper builds. Keep their stages explicit:
ordered source paths
-> preprocess_source(..., language="fortran")
-> parse_fortran_project(...)
-> compile-time expression and storage probes
-> fortran_project_to_semantic_modules(...)
-> merge public semantic modules
-> WrapperPlanner and WrapperCodeGenerator
-> create_shared_library(...)
-> WrapperBuildResult
The main ownership boundaries are:
prik/pipeline/build.py: source order, preprocessing/probing, semantic merge,.pyientry-contract loading, native build plan assembly, output placement, direct-versus-Makefile mode, and artifact reporting;prik/wrapper_codegen/planner.py: projection from completed semantic policy into validated typed plans;prik/wrapper_codegen/generator.py: direct bridge, binding, and source artifact generation;prik/compiling/: compiler commands and shared-library linking; andprik/binding_support/: native binding support copied into each build.
Do not move semantic ownership or projection policy into printers. Do not infer
source dependencies: multi-source source builds compile in caller order, and
the first semantic module names the merged extension. .pyi builds use exactly
one semantic entry contract plus a separate extension-level
NativeBuildPlan; they must not recover Python API facts by reparsing native
implementation sources. --makefile records the compiler/linker plan
without executing it; for .pyi builds, prik-build.json is written first and
Makefile.prik is projected from that manifest.
Runtime verification belongs under the relevant
tests/fortran/<feature>/end_to_end/ owner. The
tests/fortran index and permanent
contract ledger map generated
behavior to compiled/imported tests. Build-mode changes should at least cover
tests/fortran/building_shared_library/end_to_end/test_source_build_modes.py,
tests/fortran/building_shared_library/end_to_end/test_multi_source_builds.py,
and the affected runtime subject test.
Parser models are source facts. They should answer "what did the source say?" rather than "what Python wrapper should be generated?"
Fortran:
prik/parsers/fortran/parser.pyslices the file into grammar units, then parses each unit's specification region.prik/parsers/fortran/models.pystoresFortranFile, modules, procedures, variables, derived types, interfaces, programs, submodules, and diagnostics.- Execution bodies are intentionally skipped after the parser has enough signature/source facts.
Adding parser fields is a schema decision. Add fields only when downstream semantic conversion, fixtures, diagnostics, or user-visible behavior need a new fact.
The semantic layer normalizes Fortran facts into language-neutral models from
prik/semantics/models.py.
prik/semantics/fortran2ir.pymaps Fortran procedures, derived types, module variables, kinds, shapes, storage contracts, visibility, imported references, and compile-time values.prik/wrapper_codegen/printers/pyi_printer.pyemits editable user contracts.prik/parsers/pyi/parser.pyparses edited contracts to Python AST.prik/pipeline/pyi.pyconverts edited contract text, files, and path sets.prik/semantics/pyi2ir.pyconverts parsed.pyiAST back into semantic IR.prik/semantics/native_contract.pyvalidates immutable native scope, ABI, placement, type, callback, and projection facts before source-free codegen.- Named data bindings keep role-specific semantic types:
SemanticVariablefor module variables and constants,SemanticArgumentfor callable parameters, andSemanticFieldfor Fortran derived-type components. prik/semantics/policy_completion.pycompletes semantic policies after Fortran or.pyiconversion and before wrapper planning or lowering.
Keep semantic IR stable where possible. If a parser change does not affect the semantic contract, avoid changing semantic fixtures.
@native_call is stored as projection metadata on SemanticFunction. The
loader and printer currently support Arg, Return, ABI-typed literal calls
such as Int32(1), Len, IsPresent, Work, Pass, and .shape[...]
value references. Generated Fortran contracts use it when outputs make the
Python-visible argument order differ from native order. Pass() preserves the
hidden passed object when a type-bound method also needs such a projection. They do not currently
implement future wrapper projection helpers such as Addr(Arg(...)), As[...],
status-return policy, ownership conversion, or coercion execution.
The test ownership is:
- loader syntax and error behavior:
tests/fortran/semantic_pyi_format/parsing/; - printer round-trip shape:
tests/fortran/semantic_pyi_format/pipeline/; - policy-completion decisions:
tests/fortran/infrastructure/policy/and feature-localpolicy/directories; - wrapper-plan diagnostics:
tests/fortran/infrastructure/wrapper_codegen/.
When adding projection syntax, first add loader tests that prove the accepted syntax and rejected syntax. Then add policy or wrapper-plan tests only if the new metadata affects those layers.
Use the smallest test layer that proves the behavior, then add broader coverage only when the public contract changes.
| Layer | Purpose | Typical files |
|---|---|---|
| Focused parser tests | One construct, diagnostic, or model field | tests/fortran/source_parsing/parsing/test_*.py |
| Parser fixture goldens | Serialized Fortran parser contracts | tests/fortran/source_parsing/parsing/test_fortran_fixture_suite.py |
| Semantic tests | Fortran parser facts converted to wrapper-neutral IR | tests/fortran/semantic_ir/semantics/ |
| Policy tests | Completed policy decisions | tests/fortran/infrastructure/policy/ and feature-local policy/ directories |
| Wrapper-plan tests | Unsupported plan diagnostics and generated plan shape | tests/fortran/infrastructure/wrapper_codegen/ |
.pyi tests |
Editable contract loader/printer behavior | tests/fortran/semantic_pyi_format/, tests/fortran/semantic_pyi_format/pipeline/ |
| CLI tests | User commands, output routing, diagnostics | tests/fortran/command_line_interface/pipeline/, tests/fortran/source_preprocessing/preprocessing/ |
| Wrapper build tests | Artifact placement, direct/Makefile modes, multi-source ordering | tests/fortran/building_shared_library/end_to_end/test_source_build_modes.py, tests/fortran/building_shared_library/end_to_end/test_multi_source_builds.py |
| Wrapper runtime tests | Imported extension behavior, ownership, lifetime, and failures | Feature-local tests/fortran/*/end_to_end/ suites indexed by tests/fortran/README.md |
| Property/fuzz tests | Broad parser robustness invariants | tests/fortran/source_parsing/parsing/ and feature-local semantic property tests |
- Parser-only source fact: focused parser test first; fixture golden only if serialized output changes intentionally.
- CLI flag or output change: CLI test first; update README/user docs if the visible command changes.
- New datatype mapping: semantic conversion test plus
.pyiprinter/loader tests if emitted syntax changes. - New
.pyisyntax: loader and printer tests, plus policy or plan tests when it changes a completed decision or lowering. - New unsupported case: a semantic-conversion, policy, or wrapper-plan test at the stage that detects it.
- Preprocessing behavior: preprocessing CLI tests and at least one parser path that consumes the recipe.
- Wrapper orchestration or codegen behavior: the focused feature-local
end_to_end/orwrapper_codegen/owner, including an imported runtime assertion rather than build success alone.
Do not regenerate broad fixture sets to hide uncertainty. First write or run a focused test that explains the intended behavior. Then regenerate only the affected fixture group when the serialized contract really changed.
Useful commands:
When investigating coverage failures, mirror the GitHub Actions coverage flow instead of relying on a plain local run:
COVERAGE_PROCESS_START=pyproject.toml PYTHONPATH=. coverage run -m pytest
python -m coverage combine
python -m coverage reportThe COVERAGE_PROCESS_START environment variable matters because subprocess
CLI tests need the same coverage configuration as CI.
Use these walkthroughs when adding behavior. They are deliberately procedural: change the smallest owned layer first, test that layer, then update downstream contracts only when the public behavior actually changes.
Example target: preserve a new declaration attribute, source fact, or argument metadata item.
-
Add a focused parser test in the file that owns the behavior:
tests/fortran/source_parsing/parsing/,tests/fortran/modules/parsing/test_scope_handling.py, ortests/fortran/source_preprocessing/preprocessing/test_parser_boundaries.py. -
Implement parsing in
prik/parsers/fortran/parser.py. Add model fields inprik/parsers/fortran/models.pyonly if the parser output needs to expose the new fact. -
Add parser diagnostic coverage in
tests/fortran/source_parsing/parsing/test_error_handling.pyif malformed source should now fail differently. -
If project ordering, imports, or compile-time values change, update
tests/fortran/modules/parsing/test_project_scope_models.pyortests/fortran/data_types/probes/test_fortran_type_probes.py. -
If serialized parser JSON changes intentionally, regenerate the selected fixture:
python tests/fortran/source_parsing/parsing/generate_parser_goldens.py tests/fortran/source_parsing/parsing/fixtures/general/basic_subroutine.f90
-
If the new fact affects semantic output, update
prik/semantics/fortran2ir.pyandtests/fortran/semantic_ir/semantics/. -
If generated
.pyichanges, updatetests/fortran/semantic_pyi_format/pipeline/and the relevant fixture tests. -
Update Fortran parser reference, the relevant User Guide, checked example, or Semantic IR reference as needed.
Focused verification:
PYTHONPATH=. pytest -q tests/fortran/source_parsing/parsing/
PYTHONPATH=. pytest -q tests/fortran/source_parsing/parsing/test_fortran_fixture_suite.py
PYTHONPATH=. pytest -q tests/fortran/semantic_ir/semantics/Example target: map a new Fortran kind or compiler-probed storage fact.
- Add conversion coverage in
tests/fortran/semantic_ir/semantics/. - Implement the mapping in
prik/semantics/fortran2ir.py. - Keep the public semantic dtype names in
prik/semantics/models.pystable unless there is a deliberate schema decision. - If the emitted
.pyiannotation changes, updatetests/fortran/semantic_pyi_format/pipeline/andtests/fortran/semantic_pyi_format/parsing/. - Update the datatype tables in Semantic IR reference, and update the relevant User Guide or checked example when a visible example changes.
Focused verification:
PYTHONPATH=. pytest -q tests/fortran/semantic_ir/semantics/
PYTHONPATH=. pytest -q tests/fortran/semantic_pyi_format/pipeline/ tests/fortran/semantic_pyi_format/parsing/Example target: add a new Annotated[...] metadata item or projection helper.
- Add loader tests in
tests/fortran/semantic_pyi_format/parsing/. - Update
prik/semantics/pyi2ir.py. Updateprik/pipeline/pyi.pywhen loading or cross-file reconciliation changes. Updateprik/parsers/pyi/parser.pyonly when the raw Python AST parsing boundary changes. - Add printer tests in
tests/fortran/semantic_pyi_format/pipeline/. - Update
prik/wrapper_codegen/printers/pyi_printer.py. - Update semantic models in
prik/semantics/models.pyonly if the IR needs a new field or constraint. - Update policy completion or wrapper planning if the syntax changes a completed decision.
- Update Semantic IR reference, plus the relevant User Guide or checked example when users need the new syntax in a workflow.
Focused verification:
PYTHONPATH=. pytest -q tests/fortran/semantic_pyi_format/parsing/
PYTHONPATH=. pytest -q tests/fortran/semantic_pyi_format/pipeline/
PYTHONPATH=. pytest -q tests/fortran/semantic_ir/semantics/ tests/fortran/infrastructure/wrapper_codegen/Example target: report a new unsupported Fortran semantic contract clearly.
- Preserve the source fact in the parser if it is not already present.
- Raise a semantic-conversion error when no valid contract can be formed; do not attach a deferred diagnostic payload.
- If the source facts are valid but a selected wrapper behavior is unsafe, express that result in completed policy and let the planner name the owner path and reason.
- Add a focused conversion, policy, or wrapper-plan test at that owning stage.
- Update the relevant user guide and Error Handling
when users can correct the source or edited
.pyicontract.
Focused verification:
PYTHONPATH=. pytest -q tests/fortran/semantic_ir/semantics/
PYTHONPATH=. pytest -q tests/fortran/semantic_ir/semantics/
PYTHONPATH=. pytest -q tests/fortran/infrastructure/wrapper_codegen/Example target: add a stage option, change output routing, or improve diagnostic formatting.
- Add CLI tests in
tests/fortran/command_line_interface/pipeline/first. - Implement shared dispatch and output behavior in
prik/cli.py. - Keep Fortran package-specific CLI behavior in
prik/parsers/fortran/cli.py. - If compiler preprocessing behavior changes, update
prik/pipeline/preprocessing.pyand preprocessing tests. - Update the relevant User Guide or checked example for user-facing commands and this guide for developer command maps.
Focused verification:
PYTHONPATH=. pytest -q tests/fortran/command_line_interface/pipeline/
PYTHONPATH=. pytest -q tests/fortran/source_preprocessing/preprocessing/Use this map when changing one part of the project. Each section shows how to call that part manually, which focused test file to run, and where to look for more executable examples. Run the broader suite before merging.
Run the ordinary suite from the repository root before merging. Full BLAS/LAPACK cases belong to their designated real-library lane:
PYTHONPATH=. pytest -q -m "not real_library" \
tests/architecture tests/c tests/fortran tests/sharedRun the major suites individually while iterating:
PYTHONPATH=. pytest -q tests/c
PYTHONPATH=. pytest -q -m "not real_library" tests/fortran
PYTHONPATH=. pytest -q tests/sharedAs a project policy, do not merge pull requests unless all checks are green.
Refresh all Fortran parser goldens:
python tests/fortran/source_parsing/parsing/generate_parser_goldens.pyRefresh one Fortran fixture:
python tests/fortran/source_parsing/parsing/generate_parser_goldens.py tests/fortran/source_parsing/parsing/fixtures/general/basic_subroutine.f90In-test Fortran parser fixture update mode:
FORTRAN_PARSER_UPDATE_GOLDENS=1 PYTHONPATH=. pytest -q \
tests/fortran/source_parsing/parsing/test_fortran_fixture_suite.pyRefresh semantic and .pyi fixtures:
python tests/fortran/semantic_ir/semantics/generate_semantic_fixtures.py
WRAPPER_UPDATE_PYI_FIXTURES=1 python3 -m pytest -q tests/fortran/semantic_pyi_format/pipeline/test_contract_package_generation.pyWhen parser model output changes, include the regenerated parser goldens and a
short explanation in the PR. For .pyi, semantic IR, policy, or wrapper-planning behavior
changes, update the reviewed contracts under
tests/fortran/semantic_pyi_format/pipeline/fixtures/contracts/ or
the semantic fixtures under tests/fortran/semantic_ir/semantics/fixtures.
Manual call for one Fortran fixture:
python -m prik parse tests/fortran/source_parsing/parsing/fixtures/general/basic_subroutine.f90 --language fortran --jsonManual Python API call:
from prik import parse_fortran_file
parsed = parse_fortran_file(
"tests/fortran/source_parsing/parsing/fixtures/general/basic_subroutine.f90",
)
print([module.name for module in parsed.modules])Focused tests by concern:
- Parser walkthrough:
PYTHONPATH=. pytest -q tests/fortran/source_parsing/parsing/test_developer_tutorial.py - Procedures, declarations, derived types, and interfaces:
PYTHONPATH=. pytest -q tests/fortran/source_parsing/parsing/ - Scope and project behavior:
PYTHONPATH=. pytest -q tests/fortran/modules/parsing/test_scope_handling.py tests/fortran/modules/parsing/test_project_scope_models.py - Preprocessing and execution-boundary behavior:
PYTHONPATH=. pytest -q tests/fortran/source_preprocessing/preprocessing/test_parser_boundaries.py - Parser diagnostics:
PYTHONPATH=. pytest -q tests/fortran/source_parsing/parsing/test_error_handling.py - Fixture goldens:
PYTHONPATH=. pytest -q tests/fortran/source_parsing/parsing/test_fortran_fixture_suite.py - Parser error fixtures:
PYTHONPATH=. pytest -q tests/fortran/source_parsing/parsing/test_error_fixture_suite.py
Regenerate one Fortran fixture:
python tests/fortran/source_parsing/parsing/generate_parser_goldens.py tests/fortran/source_parsing/parsing/fixtures/general/basic_subroutine.f90Executable tutorial: tests/fortran/source_parsing/parsing/test_developer_tutorial.py.
Manual calls:
Focused tests by concern:
- Fortran parser-to-IR conversion:
PYTHONPATH=. pytest -q tests/fortran/semantic_ir/semantics/ - Wrapper-plan support diagnostics:
PYTHONPATH=. pytest -q tests/fortran/infrastructure/wrapper_codegen/ .pyiprinter:PYTHONPATH=. pytest -q tests/fortran/semantic_pyi_format/pipeline/.pyiloader and edited stub behavior:PYTHONPATH=. pytest -q tests/fortran/semantic_pyi_format/parsing/- Semantic and
.pyifixtures:PYTHONPATH=. pytest -q tests/fortran/semantic_pyi_format/pipeline/test_contract_loading.py
Regenerate semantic and .pyi fixtures:
python tests/fortran/semantic_ir/semantics/generate_semantic_fixtures.py
WRAPPER_UPDATE_PYI_FIXTURES=1 python3 -m pytest -q tests/fortran/semantic_pyi_format/pipeline/test_contract_package_generation.pyExecutable examples: tests/fortran/semantic_pyi_format/pipeline/ and
tests/fortran/semantic_pyi_format/parsing/.
Manual calls:
Focused tests:
- Full CLI behavior:
PYTHONPATH=. pytest -q tests/fortran/command_line_interface/pipeline/ - Stage dispatch:
PYTHONPATH=. pytest -q tests/fortran/command_line_interface/pipeline/ -k "parse or semantics or pyi or wrap" - Language and preprocessing selection:
PYTHONPATH=. pytest -q tests/fortran/command_line_interface/pipeline/ -k "language or preprocessing"
Executable reference: tests/fortran/command_line_interface/pipeline/.