Files
orisys-sdk/docs/api-audit.md
T
ykcheungab 4c1ab5c27e tools: add a static public-API audit and record its first run
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.
2026-09-30 16:44:12 +08:00

7.9 KiB
Raw Blame History

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:

  • LowpassFilter is documented in docs/api.md as orisys.signal_processing.LowpassFilter and 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 / CheckResult are the entire force calibration object model, and calibration.check() / calibration.build() are used directly by the test suite. The orisys.calibration package 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

  1. 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.
  2. 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.
  3. 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.
  4. The omissions are not random. StreamPipeline, capture and unroll are the newest subsystems, and they are the ones with no coverage. The reference drifted toward the parts that existed when it was written.
  5. orisys.hhd is 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.