Adds tools/api_audit.py, a read-only static audit of the public API surface, and docs/api-audit.md, its first run against this revision. The audit answers three questions the reference cannot answer about itself: 1. Does every declared export exist, have a docstring, and appear in docs/api.md? It reads the root __all__ plus each submodule's __all__. 2. How complete is each public definition? Docstring presence, detected docstring convention, parameter and return annotations. 3. Which public names do examples and tests actually use but the reference never explains? That set is the real documentation gap. It parses with ast and never imports the package, so it costs no numpy/cv2 import and cannot be affected by the environment. Findings on this revision (full detail in docs/api-audit.md): - 393 public classes/methods/functions; 65% have a docstring, 32% have a fully annotated signature, 38 document their return value. - 243 docstrings are unstructured prose, 137 are absent, 13 use Google or NumPy sections. There is effectively one convention: none. - 9 of the 18 root exports are absent from docs/api.md, including StreamPipeline, StreamRecorder, mosaic_videos and FFCFrameProcessor - the pipelined multi-stream and offline-recording paths. - Six sub-surfaces have zero coverage in the reference: unroll (18 exports), capture (17), hhd + solvers (7), pipeline (3), export (2). - 74 names are used by examples or tests but absent from the reference. - 21 public classes are exported nowhere; LowpassFilter is documented in docs/api.md and importable, but declared in no __all__. No behaviour change: a new tool and a new document, no source file touched.
7.9 KiB
Public API surface audit (2026-09-30)
A snapshot of what the SDK declares as public, what is documented, and where the gaps are.
Produced by tools/api_audit.py, which is static and read-only — it
never imports the package, so it costs no numpy/cv2 import and cannot be affected by the
environment. Re-run it after any API change:
cd D:\orisys_ws\orisys-sdk
python tools/api_audit.py # text report
python tools/api_audit.py --json # machine-readable
python tools/api_audit.py --quiet # headline numbers
1. Headline
| Measure | Value |
|---|---|
| Public classes / methods / functions | 393 (53 / 214 / 126) |
| With a docstring | 256 (65 %) |
| Fully annotated signature (params + return) | 125 (32 %) |
| Docstring documents its return value | 38 |
Docstring conventions actually in use:
| Convention | Count |
|---|---|
| plain prose (no structured sections) | 243 |
| none | 137 |
Google (Args: / Returns:) |
9 |
NumPy (Parameters + ---) |
4 |
Reading: the dominant convention is not a convention. Structured sections are absent from almost every docstring, and two thirds of signatures are missing type information. The hand-written reference compensates in the places it covers; a generated reference would not.
2. The declared public surface
The root __all__ (src/orisys/__init__.py) declares 18 names. Sub-surfaces declare a further 88.
2.1 Root exports — 9 of 18 are absent from docs/api.md
| Documented | Not mentioned anywhere in docs/api.md |
|---|---|
Sensor, util, finger, signal_processing, capture, list_config, export_config, export_sensor_bundle, export_sensor_bundles |
StreamPipeline, FrameSnapshot, PipelineStats, StreamRecorder, mosaic_videos, FFCFrameProcessor, CompoundEyeSensor, CameraSensor, calibration |
StreamPipeline is the engine behind the published "5 × 30 FPS on one desktop, 4.2× headroom"
claim and is used by examples/multi_stream.py. StreamRecorder and mosaic_videos are the
offline-recording path. None of them has a Markdown reference anywhere in the repository.
2.2 Sub-surfaces with no coverage in api.md at all
| Module | Exports | Documented | Note |
|---|---|---|---|
orisys.unroll |
18 | 0 | UV domain, remap, geometry — the finger pipeline's core maths |
orisys.capture |
17 | 0 | capture sources, workers, reconnect policy, camera discovery |
orisys.hhd + hhd.solvers |
7 | 0 | no docstrings either — see §3 |
orisys.pipeline |
3 | 0 | StreamPipeline etc. |
orisys.export |
2 | 0 | StreamRecorder, mosaic_videos |
2.3 Exports with no docstring — 20 of 88
Includes CaptureWorker, pick_default_camera, available_blend_backends,
compute_final_crop_rect, coverage_ratio, largest_fully_covered_rect,
match_bgr_brightness, barycentric_grid, face_uvs_from_loop_data, all four orisys.hhd
exports, and named constants (DEFAULT_LAPTOP_WEBCAM_PATTERNS, MAX_SIMULTANEOUS_CAPTURES,
DEFAULT_FINGER_EXPORT_DIR, DEFAULT_FINGER_MAP_CACHE, BLEND_MODES).
3. Coverage by module
The extreme cases, which are where effort pays off:
| Module | Symbols | Docstrings | Annotated | Comment |
|---|---|---|---|---|
orisys.finger.runtime |
31 | 35 % | 3 % | FingerRuntime is the documented entry point for finger users, yet its implementation module is the thinnest in the package |
orisys.hhd.timing |
4 | 0 % | 0 % | |
orisys.stitch.feature |
10 | 20 % | 0 % | |
orisys.stitch.tiles |
5 | 20 % | 0 % | |
orisys.stitch.api |
10 | 30 % | 10 % | |
orisys.frame_processors |
21 | 48 % | 0 % | |
orisys.capture.source |
32 | 50 % | 91 % | annotations good, prose thin |
orisys.sensor |
21 | 90 % | 38 % | the most-documented module, still missing annotations |
orisys.configs |
13 | 100 % | 69 % | |
orisys.unroll.precompute, domain |
6 / 6 | 100 % | 0 % | documented but untyped |
Annotated and documented do not move together: capture.source is 91 % annotated but 50 %
documented; hhd.domain is 100 % documented but 0 % annotated.
4. Used by examples and tests, absent from the reference — 74 names
These are APIs users demonstrably reach for and the reference never explains. The highest-traffic group is the runtime/typing surface:
StreamPipeline, FrameSnapshot, PipelineStats, StreamRecorder, mosaic_videos,
FFCFrameProcessor, UVDomain, UVRemap, UVSurfaceMap, UVSurfaceMesh, UVMeshViewer,
Tracker, Contact3D, CalSample, CalibrationError, StitchStatus, CameraInfo,
CameraFilter, CaptureSource, CaptureConfig, CaptureHealth, ReconnectPolicy,
PoissonSolver, nHHD
Plus the finger/geometry helpers (build_uv_surface_mesh, compute_uv_surface_frames,
lift_uv_flow_to_3d, flow_magnitude_vertex_colors, scalar_map_vertex_colors,
sample_field_at_mesh_vertices, mesh_uv_to_world_scale, resolve_export_paths,
resolve_finger_geometry, load_finger_export_bundle, init_uv_mesh_viewer,
update_uv_mesh_viewer), the capture helpers (open_capture, normalize_source,
list_cameras, pick_default_camera), the calibration helpers (check, build, parse,
load, calibrate_normal_force, calibrate_shear_force, calibrate_shear_components),
and the config resolvers (load_config, list_sensor_bundles, resolve_sensor_config_path,
resolve_sensor_resource, resolve_sensor_asset_dir, pipeline_from_sensor_json).
5. Public classes exported nowhere — 21
Defined, carrying no name-mangling underscore, but in no __all__ and not reachable as a declared
API: BlendBackend, CalSample, Calibration, CalibrationError, CausalMedianFilter,
ChannelCurve, CheckResult, CompoundEyeFrameProcessor, Contact3D,
ForceChannelCalibration, FrameUnwarper, LowpassFilter, MaskedDomain,
MaskedPoissonConfig, NumbaBlendBackend, NumpyBlendBackend, SampleRateEstimator,
SensorBundle, SingleCameraFrameProcessor, Tracker, VideoCaptureLike.
Two of these are the clearest signals of a boundary that was never decided:
LowpassFilteris documented indocs/api.mdasorisys.signal_processing.LowpassFilterand is imported by examples — but it appears in neither the submodule's__all__nor the root's. It is public by documentation and private by declaration.Calibration/CalibrationError/CalSample/CheckResultare the entire force calibration object model, andcalibration.check()/calibration.build()are used directly by the test suite. Theorisys.calibrationpackage is exported from the root, but none of its public names are declared or documented.
Each of the 21 needs one decision: promote into the relevant __all__, or rename with a leading
underscore to mark it internal.
6. What this implies
- Fix the declaration boundary first. Nine root exports have no reference and 21 classes sit in the gap between documented and declared. Publishing a reference on top of an undecided public surface multiplies the confusion rather than resolving it.
- Pick one docstring convention before generating anything. With 243 prose docstrings, 137 absent, and 13 structured, a generator's output would be thin and inconsistent — the tool renders what is there.
- Annotations are the larger gap. 32 % complete signatures means generated parameter tables would be mostly bare. This is mechanical work and it improves IDE help, not only docs.
- The omissions are not random.
StreamPipeline,captureandunrollare the newest subsystems, and they are the ones with no coverage. The reference drifted toward the parts that existed when it was written. orisys.hhdis documented nowhere and has no docstrings, yet is exported from the root package. Either document it or stop exporting it — leaving it in__all__promises support that does not exist.