Skip to content

Make multi-chunk logging queue-based and surface auxiliary data errors clearly - #412

Open
animmosmith wants to merge 7 commits into
399-migrate-docs-build-to-uvfrom
331-350-safer-multiprocess-logging-and-auxdata-errors
Open

Make multi-chunk logging queue-based and surface auxiliary data errors clearly#412
animmosmith wants to merge 7 commits into
399-migrate-docs-build-to-uvfrom
331-350-safer-multiprocess-logging-and-auxdata-errors

Conversation

@animmosmith

Copy link
Copy Markdown
Collaborator

Summary

This PR bundles two related bug fixes, both in pyopia.cli's logging/error handling. They're flagged separately below since they're distinct fixes for distinct issues, just sharing the same code area.

Fixes #331: logging is not multiprocess-safe

pyopia process --num-chunks N previously had each chunk's process open its own FileHandler onto the same log file, which isn't guaranteed safe against concurrent writes (per the Python logging cookbook linked from the issue). Every process (including the main one) now routes logging through a multiprocessing.Queue drained by a single QueueListener, which is the sole writer to the real log file/console. Worker processes are named chunk-{c} so %(processName)s in the merged log identifies which chunk each line came from.

Fixes #350: unhelpful error + wasted retries on a malformed auxiliary data file

A malformed auxiliary data file previously wasn't actually caught by AuxillaryData's own except RuntimeError clause (real parsing failures raise KeyError/ValueError/etc., not RuntimeError), so it surfaced as a bare, unhelpful message (e.g. just 'time') with no indication of the cause - and because the failed AuxillaryData instance was never cached, pyopia process retried the identical failure on every remaining image in the chunk.

  • Added AuxillaryDataError, raised with the file path and underlying cause.
  • process_file_list now recognises this error specifically, logs one clear message, and stops the rest of that chunk instead of repeating the same failure per image.
  • Promoted the per-image exception traceback from DEBUG to ERROR so it's visible without enabling verbose logging.

Version bumped to 2.16.21, ready to merge once #394/#404/#406/#408/#410/#411 are in.

Test plan

  • flake8 pyopia clean.
  • Fast test subset (pytest -m "not slow and not training"): 36 passed.
  • Slow/real-integration subset (pytest -m "slow and not training"): 19 passed.
  • Manually verified Logging handling is not multiprocess safe #331: ran process --num-chunks 2 against 4 real duplicated images with log_file set - single merged log file, chunk-0/chunk-1/MainProcess correctly tagging every line, no corruption, all 4 real STATS files produced.
  • Manually verified Using incorrectly formatted auxilliary data file makes pyopia skip image with non-useful log message #350: ran process against a deliberately malformed auxiliary CSV (header timestamp instead of time, reproducing the original KeyError('time')) - error now names the file and cause, and appears exactly once (processing stopped after the first image instead of repeating for all of them).

Closes #331
Closes #350

🤖 Generated with Claude Code

@animmosmith
animmosmith requested a review from emlynjdavies July 31, 2026 16:55
@animmosmith
animmosmith changed the base branch from main to 399-migrate-docs-build-to-uv July 31, 2026 17:00
@animmosmith animmosmith self-assigned this Jul 31, 2026
@animmosmith animmosmith added bug Something isn't working patch / enhancement improved functionality or patch indented for changes that require bumping only the PATCH number priority-medium labels Jul 31, 2026
…s clearly

Fixes #331 and #350.

- process/process_realtime/merge_mfdata/process_file_list now all route logging
  through a multiprocessing.Queue drained by a single QueueListener, rather than
  each process/chunk opening its own FileHandler onto the same log file. Worker
  processes are named chunk-{c} so the merged log identifies which chunk each
  line came from.
- AuxillaryData.load_auxillary_data failures are now wrapped in a new
  AuxillaryDataError naming the file and underlying cause, instead of being
  silently swallowed (or, for errors load_auxillary_data could actually raise,
  not caught at all). process_file_list stops the rest of that chunk on this
  error instead of retrying every remaining image against the same broken file.
- Promoted the per-image exception traceback from DEBUG to ERROR so it's visible
  by default rather than only with verbose logging enabled.
…rrors

process()/process_realtime() created the log queue listener before validating
the config (missing output step, etc.), but only stopped it at the very end -
so a validation error left the listener's background thread (and the
multiprocessing.Queue feeding it) running forever. Wrap the listener's whole
lifetime in try/finally so it's always stopped, regardless of where an
exception occurs.
@animmosmith
animmosmith force-pushed the 331-350-safer-multiprocess-logging-and-auxdata-errors branch from 22d0eef to a19e6c4 Compare August 5, 2026 10:08
…to 331-350-safer-multiprocess-logging-and-auxdata-errors
…to 331-350-safer-multiprocess-logging-and-auxdata-errors
…to 331-350-safer-multiprocess-logging-and-auxdata-errors
Real CI evidence: Windows CI runs on this stack were completing all tests
successfully but then hanging for ~48 minutes before being killed by the
60-minute job timeout, with no further output after the test summary.

Stopping the QueueListener alone isn't sufficient cleanup - the underlying
multiprocessing.Queue keeps its own background feeder thread alive until
explicitly closed, which per the multiprocessing docs can prevent a process
from exiting cleanly. This wasn't visible in local verification (Linux/macOS)
or in Ubuntu/macOS CI, but Windows' stricter process-exit/thread-cleanup
semantics surfaced it as a full job timeout.

Adds a shared stop_queue_logging() helper (listener.stop() + queue.close() +
queue.join_thread()) used at all three call sites: process(),
process_realtime(), and merge_mfdata().
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working patch / enhancement improved functionality or patch indented for changes that require bumping only the PATCH number priority-medium

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Using incorrectly formatted auxilliary data file makes pyopia skip image with non-useful log message Logging handling is not multiprocess safe

1 participant