SDK guide

Finite camera helper: controlled fixtures only

PymmcorePlusDriver, CameraProfile and CameraFactory provide a bounded single-camera workflow for an explicitly constructed Python fixture. Native hardware and native simulator backends are refused. This API does not establish an ImSwitch installation, MMCore binary identity, device-adapter stop guarantee or owned singleton factory. No waiting registry family or package is automatically enabled by its presence.

A data-only driver subclass supplies a fixed camera label, primary pixel mode, finite exposure/software budgets and optional ROI/binning choices. The fake backend returns a CameraFactory declaration; it must not construct a camera while the package loads. The bridge invokes the factory inside a retained construction job. The factory transfers the dedicated core handle to its CameraLease before loading/initializing any camera, then returns the matching manager. Borrowed/shared cores, foreign device labels, live owners, external trigger and autoshutter configurations are refused.

The selected route is MMCoreDetectorManager.acquireSingleFrame(cancelEvent, timeout, pollInterval). Capture never substitutes snapSync, getLatestFrame, a cached array or a direct-core capture call. The pure fixture is source-shaped and implements the same call/result boundary; it is not proof that the upstream singleton constructor can safely be used. Fixed nested core observations, labelled stop and owned-label unload are visible to the SDK recorder.

cap.sensing.image.v1 is exclusive, bounded and publicly non-cancellable. Public operation cancellation is refused without setting the acquisition event; e-stop and deadline handling still request internal cancellation and attempt stop. One successful capture returns one immutable artifact and actual dimensions/exposure. Width, height and exposure inputs assert fresh settings; they never resize pixels or hide a configuration write. Only lossless PNG is supported.

Supported primary arrays are exact NumPy ndarrays: mono8 (height, width), mono16 (height, width) in either byte order, and RGB8 (height, width, 3). Every logical sample is preserved. Noncontiguous/negative strides are detached into owned bytes. Floating, signed, object, structured, RGB16 and ambiguous packed layouts are refused. RGB refers to the returned logical samples; no packed native alpha/padding or calibration is claimed. NumPy is an optional dependency needed only when capturing; base SDK imports do not import NumPy, Pillow or camera frameworks. Pillow is used by validation to independently decode every sample, not by this encoder.

Enable the endpoint's artifact store before capture. A conservative preflight refuses known size/capacity failures before exposure. It is not a quota reservation: final publication still checks atomically, and a concurrent capacity loss causes failure without an artifact. Existing generic artifact_ref publication remains supported; cameras retain image_ref. Ordinary receipts contain artifact references rather than filesystem paths, thumbnails or base64 image data. Session/endpoint grants, evidence commit, expiry, replay and MCP resource access remain stock SDK behavior.

Exposure, ROI, binning and numeric capture assertions require site operator limits. Configure limits use writable parameter paths; assertion limits belong under cap.sensing.image.v1. The camera profile additionally narrows supported settings and image budgets. These are software/fixture limits, not specimen exposure, illumination or physical safety guarantees. Only one configuration field is changed per command; readback must match. Global core reset, configuration reload, stage/laser commands, live preview and file writers are not exposed.

Reads never construct or capture. Before construction they truthfully report unknown cached values and state_stale=true; settled reads use fixed observations. Cold e-stop cannot confirm an unconstructed native state and remains failed/latched. It does not create a core merely to return success. Consequently a package requiring the generic gate's unconditional cold e-stop/reset success remains blocked until a separately reviewed initialization/safety contract exists.

Construction, ordinary execution, stop and cleanup have distinct retained owners. Timeout, cancellation, delayed worker startup and late native return do not free admission. Each retained stop observation records the ordinary-owner epoch before native dispatch. A stop cannot acknowledge safety if it began before an earlier owner retired, including a late success or error after caller timeout. Joining an old pending stop does not refresh its proof. Stale proof refuses acknowledgement and retains the exclusive claim; after that stop terminates, a new bounded stop/readback can reconcile it. The proof is checked again at safety commit, status and reset. At most one stop is in flight; a terminal failure permits another protective attempt but cannot clear sticky failure debt. Reset observes the same loaded labelled camera and clears only local latches after owners settle. It never captures, starts live preview or calls native reset. Close is bounded, closes admission first and retains incomplete cleanup. It unloads only its owned camera after jobs end, and never reports a failed or hanging cleanup as complete.

Profile/factory identity and values, construction function code/defaults/closure bindings, and fixed receiver method identity/code/defaults are checked through their retained lifetime. This guards the supported fixture declarations, not arbitrary mutable Python globals or native ABI semantics. The stock gate preloads the lazy pixel validator and checks the immutable ordinary-owner policy; legitimate live core leases remain runtime state. A rejected gate process is not restored or sandboxed. The caller still supplies trusted fixture code. No passing fixture test or Python declaration check authorizes a native constructor.

The SDK-contained camera tests cover complete pixels, stock server/MCP artifacts, limits, ownership races, partial constructor failures, public cancellation refusal, stop/reset and cleanup. Native enablement additionally needs exact installed dependencies/binaries, an unchanged source-proven owned factory, callback lifetime, operator configuration and separately authorized runtime evidence. The selected ImSwitch card mismatch and native stop/build/factory blockers remain explicit.