v1.9.8 - Sampler Utility and Iterator Extraction (2025-12-29)#

What Changed?#

This release continues Phase 2 decomposition by extracting utility functions and iterators from the Sampler class into dedicated modules. The _helpers.py module provides type conversion and progress display utilities, while _iterators.py contains count-based and time-based iteration logic. All functionality is preserved through the existing modular package structure with zero breaking changes.


What’s New#

Utility Functions Module (_helpers.py)#

What it does: Extracts three static utility functions from the Sampler class into a dedicated helpers module. These functions handle type conversion, progress display, and test data generation.

Functions extracted:

  1. sanitize(event: list) -> list: Converts raw CSV string values to proper Python types

    • Empty strings → None

    • Numbers → int (if whole) or float (if decimal)

    • Non-numbers → remain as strings

  2. tqdm_wrapper(iterable, desc=None, show=True): Conditionally wraps iterables with progress bars

    • show=True: Displays tqdm progress bar

    • show=False: Returns iterable unchanged

    • Useful for interactive vs batch processing

  3. mock_sample(*args, **kwargs): Generates hardcoded test data ["mock_event"]

    • Used for unit testing without hardware

    • Accepts any arguments (ignored)

Code examples:

# Type conversion
from haniwers.v1.daq.sampler._helpers import sanitize

raw_csv_row = ["100", "200.5", "", "invalid"]
cleaned = sanitize(raw_csv_row)
# Returns: [100, 200.5, None, "invalid"]

# Progress bar display
from haniwers.v1.daq.sampler._helpers import tqdm_wrapper

# Show progress
for item in tqdm_wrapper(range(1000), desc="Processing", show=True):
    process(item)

# No progress (batch mode)
for item in tqdm_wrapper(range(1000), desc="Processing", show=False):
    process(item)

Iterator Functions Module (_iterators.py)#

What it does: Extracts iteration logic into generator functions for count-based and time-based acquisition loops. These generators encapsulate the control flow for both sampling modes.

Functions extracted:

  1. count_based_iterator(count: int): Generator that yields count times

    • Used for acquire_by_count() mode

    • Yields exactly N times before stopping

  2. time_based_iterator(duration: float, sleep_interval: float): Generator for fixed-duration loops

    • Used for acquire_by_time() mode

    • Yields until duration elapsed

    • Respects sleep_interval between yields

Code examples:

from haniwers.v1.daq.sampler._iterators import count_based_iterator

# Collect exactly 1000 events
for _ in count_based_iterator(1000):
    event = device.read_event()
    csv_writer.write_row(event)

# Time-based acquisition (30 seconds, 0.1s sleep)
for _ in time_based_iterator(30.0, 0.1):
    event = device.read_event()
    csv_writer.write_row(event)

Installation#

Quick Start#

# Get the release
git checkout v1.9.8

# Setup
task env:setup

# Run CLI
haniwers-v1 --help

What’s Different from the Last Version?#

✅ Added#

  • New src/haniwers/v1/daq/sampler/_helpers.py module with utility functions (sanitize, tqdm_wrapper, mock_sample)

  • New src/haniwers/v1/daq/sampler/_iterators.py module with generator functions (count_based_iterator, time_based_iterator)

  • Comprehensive docstrings with usage examples for all new modules

🔧 Changed#

  • Reorganized src/haniwers/v1/daq/sampler/_base.py to import helpers and iterators from new modules

  • No 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 utility functions and iterators into dedicated modules is a pure refactoring with no breaking changes.


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)

  • ✅ Helper function tests verified

  • ✅ Iterator function tests verified

  • ✅ Pre-commit hooks passing (ruff format, YAML, etc.)


Release Details#

  • Date: 2025-12-29

  • Version: v1.9.8

  • Files Changed: 3 (created _helpers.py, _iterators.py; updated _base.py imports)

  • Commits:

    • 6292a99: refactor(daq/sampler): extract static helper functions to _helpers.py

    • b0ce175: refactor(daq/sampler): extract iterator functions to _iterators.py

    • 81b0d45: bump: version 1.9.7 → 1.9.8


Next Steps#

Phase 2.2 (v1.9.9 planned): Reader and Writer Module Decomposition#

src/haniwers/v1/daq/sampler/
├── __init__.py           # Re-exports all classes
├── _base.py              # Sampler base class (initialization, coordination)
├── _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) ✓

Benefits of Phase 2.2:

  • Single Responsibility Principle (SRP) fully applied

  • Each module ~300-400 lines instead of 1,591 lines

  • Improved testability and code organization

  • Clearer relationships between reader, writer, and orchestration layers

Phase 3 (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 codebase for v2 migration

  • Reusable helper modules for v2

  • Clear architectural patterns to follow


Architecture Decision#

This modularization follows the Single Responsibility Principle (SRP):

  • Each module has one reason to change

  • Dependencies flow downward (high-level → utilities)

  • Public API remains unchanged

This pattern will be applied to other large modules in future releases:

  • config/model.py (1,184 lines) → Phase 2.2

  • cli/options.py (770 lines) → Phase 2.3

See project constitution for more details on architectural principles.