Skip to content

Use None, not inf, for "no limit" resample settings - #219

Merged
cboulay merged 1 commit into
devfrom
fix/settings-no-infinite-defaults
Aug 14, 2026
Merged

Use None, not inf, for "no limit" resample settings#219
cboulay merged 1 commit into
devfrom
fix/settings-no-infinite-defaults

Conversation

@cboulay

@cboulay cboulay commented Aug 14, 2026

Copy link
Copy Markdown
Member

Why

float("inf") is not valid JSON. Any tool that serializes a Settings object trips over it — ezmsg-dashboard#4 hits this on our defaults, so it fails before the user has touched anything.

A sweep of the repo turned up exactly one Settings field defaulting to infinity, declared twice:

Location Field Was
resample.py ResampleSettings.max_chunk_delay np.inf
resampleconcat.py ResampleConcatSettings.max_chunk_delay np.inf (forwarded)

Plus one near-miss: reference_reset_after_chunks defaults to a finite 3, but its docstring told users to pass float("inf") to disable reset recovery — straight into the same bug. Two tests did exactly that.

What

  • max_chunk_delay: float = np.inffloat | None = None (no limit — never extrapolate on wall-clock alone).
  • reference_reset_after_chunks: float = 3int | None = 3. Default behaviour unchanged; only the "disabled" spelling moves from inf to None.
  • Both mirrored on ResampleConcatSettings; its now-unused numpy import is dropped.
  • New _as_limit() helper folds non-finite values onto None, so pipelines already passing inf keep working rather than breaking on upgrade.
  • The publisher's wait loop drops np.isfinite(timeout) for a plain is not None branch.

None also matches the convention already used across this repo for optional numerics (resample_rate, downsample.target_rate, align.gain, kaiser.ripple), and gives a dashboard an unambiguous "unset" to render.

Considered and rejected: a large finite sentinel

float32 max (~3.4e38) was the other candidate. These are float64 scalars in wall-clock-seconds arithmetic, so it's an arbitrary magic number that still renders as garbage in a UI field — and it would silently flip a code path, sending the publisher into asyncio.wait_for with an absurd deadline instead of the plain event wait.

Not changed

ResampleState.last_write_time keeps -np.inf. It's state, never serialized into a settings payload. The remaining inf hits in the repo are a np.nextafter direction argument, a benchmark min-tracker, and two comments — all correct as-is.

Testing

test_resample_reset_disabled_can_stall is parametrized over [None, float("inf")] so the back-compat path stays covered. The integration test and its docstring move to None.

  • ruff check clean
  • 4050 unit tests pass (6 skipped)
  • 7 resample integration tests pass
  • Verified both Settings classes now serialize under json.dumps(..., allow_nan=False), which previously raised ValueError: Out of range float values are not JSON compliant

Note for the dashboard side

This fixes the defaults. A user can still type inf into a float field at runtime, and this repo will accept it (normalizing internally) — so if ezmsg-dashboard serializes settings before handing them to the processor, it still needs its own guard.

float("inf") is not valid JSON, so any tool that serializes a Settings
object -- e.g. ezmsg-dashboard, which hit this in ezmsg-org/ezmsg-dashboard#4
-- fails on the defaults rather than on anything the user did.

Switch the two affected fields to an Optional sentinel, which serializes
cleanly and matches the convention already used by resample_rate,
target_rate, and friends:

  ResampleSettings.max_chunk_delay            inf -> None
  ResampleSettings.reference_reset_after_chunks  float -> int | None

reference_reset_after_chunks keeps its finite default of 3; only its
"disabled" spelling changes, since its docstring used to tell users to
pass float("inf") and walk straight into the bug.

Non-finite input is normalized to None rather than rejected, so existing
pipelines that pass inf keep working. The publisher's wait loop drops its
np.isfinite check for a plain is-not-None branch.

ResampleState.last_write_time keeps -inf: it is state, never serialized
into a settings payload.
@cboulay
cboulay merged commit 8784ed8 into dev Aug 14, 2026
14 checks passed
@cboulay
cboulay deleted the fix/settings-no-infinite-defaults branch August 14, 2026 14:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant