v1.9.9 - Sampler Reader and Writer Module Extraction (2025-12-29)#
What Changed?#
This release completes Phase 2 decomposition by extracting reader and writer functionality from the Sampler class into dedicated modules.
The _reader.py module handles all event reading operations, while _writer.py manages CSV writing and high-level acquisition methods.
Combined with previously extracted helpers and iterators, the Sampler is now fully modularized following the Single Responsibility Principle.
What’s New#
Event Reader Module (_reader.py)#
What it does: Extracts all event reading functionality into a dedicated EventReader class. Handles low-level device I/O and event parsing with three core methods for different use cases.
Methods extracted:
read_event() -> RawEvent | None: Read one measurement from device with timestampBlocks until detector sends data
Records exact timestamp (to microsecond precision)
Returns RawEvent or None if line is invalid
Handles corrupted data gracefully
stream_events(iterator) -> Iterator[RawEvent]: Generator yielding events one-at-a-timeMemory-efficient for large datasets
Automatically skips invalid lines with warnings
Yields only valid RawEvent objects
Pauses until caller asks for next event
collect_events(iterator) -> list[RawEvent]: Collect all events into a listReads all measurements first, returns complete list
Uses stream_events internally
Simple interface for all-at-once processing
Loads everything into memory at once
Event Writer Module (_writer.py)#
What it does: Extracts all event writing functionality into a dedicated EventWriter class. Handles CSV file creation with both streaming and buffered modes, plus high-level acquisition methods.
Methods extracted:
save_events(file_path, source): Write measurements to CSV fileAccepts Iterator or list[RawEvent] as source
Supports streaming mode (write as you go)
Supports buffered mode (collect first, then write)
Proper CSV formatting with timestamps and sensor values
acquire_by_count(file_path, event_count): Collect and save fixed number of eventsReads exactly N measurements from detector
Shows progress bar (if enabled)
High-level API for fixed-count acquisition
Recommended for most use cases
acquire_by_time(file_path, duration, sleep_interval): Collect and save for fixed durationCollects measurements for exactly N seconds
Configurable polling interval
Shows progress bar (if enabled)
Used extensively in threshold scanning
Code examples:
from haniwers.v1.daq.sampler._reader import EventReader
from haniwers.v1.daq.sampler._writer import EventWriter
from haniwers.v1.daq.sampler._iterators import count_based_iterator
from pathlib import Path
# Create reader and writer
reader = EventReader(device, logger)
writer = EventWriter(reader, Path("./data"), stream_mode=True)
# High-level API (recommended)
writer.acquire_by_count(Path("./data/run.csv"), 1000)
writer.acquire_by_time(Path("./data/scan.csv"), duration=10.0, sleep_interval=0.1)
# Low-level API
event = reader.read_event()
iterator = count_based_iterator(100)
for event in reader.stream_events(iterator):
process(event)
Installation#
Quick Start#
# Get the release
git checkout v1.9.9
# Setup
task env:setup
# Run CLI
haniwers-v1 --help
What’s Different from the Last Version?#
✅ Added#
New
src/haniwers/v1/daq/sampler/_reader.pymodule with EventReader class (read_event, stream_events, collect_events)New
src/haniwers/v1/daq/sampler/_writer.pymodule with EventWriter class (save_events, acquire_by_count, acquire_by_time)Comprehensive docstrings with usage examples for both new modules
Clean separation of reading and writing concerns following Single Responsibility Principle
🔧 Changed#
Reorganized
src/haniwers/v1/daq/sampler/_base.pyto coordinate with new reader/writer modulesNo changes to public API or CLI functionality
🐛 Fixed#
No bug fixes in this release
Is It Safe to Upgrade?#
Backward Compatible: ✅ Yes
All existing imports and APIs continue to work without modification. The extraction of reader and writer functionality into dedicated modules is a pure refactoring with zero breaking changes. Sampler API remains identical.
Tests Passed#
✅ Builds without errors
✅ All 133 unit tests passing (DAQ module)
✅ Sampler functionality tests passing (10 tests covering count/time/run modes)
✅ MAC address tests passing (3 essential tests)
✅ Reader module functionality verified
✅ Writer module functionality verified
✅ Pre-commit hooks passing (ruff format, YAML, etc.)
Release Details#
Date: 2025-12-29
Version: v1.9.9
Files Changed: 2 (created
_reader.py,_writer.py)Commits:
03779ce: refactor(daq/sampler): extract event reading methods to _reader.py
9345343: refactor(daq/sampler): extract event writing methods to _writer.py
bd9440b: bump: version 1.9.8 → 1.9.9
Next Steps#
Phase 2 Complete! Sampler Fully Modularized#
src/haniwers/v1/daq/sampler/
├── __init__.py # Re-exports all classes
├── _base.py # Sampler orchestration layer (1,591 lines → pending simplification)
├── _reader.py # EventReader (read_event, stream_events, collect_events) ✓
├── _writer.py # EventWriter (save_events, acquire_by_count, acquire_by_time) ✓
├── _iterators.py # Iterators (count_based_iterator, time_based_iterator) ✓
└── _helpers.py # Static helpers (sanitize, tqdm_wrapper, mock_sample) ✓
Phase 2 Summary:
v1.9.7: Package structure created
v1.9.8: Helpers and iterators extracted
v1.9.9: Reader and writer extracted ✓
Phase 3 (v1.10.0 planned): Simplify Orchestration Layer#
The final step is simplifying _base.py from 1,591 lines to ~200-300 lines as a pure orchestration layer:
Remove all reading logic (now in _reader.py)
Remove all writing logic (now in _writer.py)
Keep only: initialization, configuration management, public API coordination
Delegate to specialized modules via composition
Phase 4 (v1.10.0+ planned): Technical Debt Resolution#
Complete removal of deprecated DaqConfig and ScanConfig models
Removal of TODO comments with implementations
Integration test stabilization
Performance optimization if needed
v1 → v2 Transition#
This refactoring prepares v1 for stable maintenance mode while providing:
Clean, modular codebase for v2 migration
Reusable reader/writer/helper modules for v2
Clear architectural patterns to follow
Foundation for subsystem integration in v2
Architecture Achievements#
This modularization follows the Single Responsibility Principle (SRP):
Module |
Lines |
Responsibility |
Status |
|---|---|---|---|
_helpers.py |
217 |
Type conversion, progress display |
✓ |
_iterators.py |
162 |
Count/time-based iteration |
✓ |
_reader.py |
269 |
Event reading from device |
✓ |
_writer.py |
370 |
Event writing to CSV |
✓ |
_base.py |
1,591 |
Orchestration layer |
Phase 3 pending |
Target: Reduce _base.py to ~250 lines by delegating to specialized modules.
See project constitution for more details on architectural principles.