From 3b55bb8be0a2aa406a90fdafeb89aec51aeee1e3 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Nov 2025 01:04:27 +0000 Subject: [PATCH 1/2] feat: improve test coverage, fix bugs, add closed property, and create issue analysis This commit improves the stream2py codebase with bug fixes, new features, enhanced test coverage, and comprehensive issue analysis. ## Bug Fixes - fix: Python 3.11+ compatibility in test_util.py - handle TypeError for context manager protocol - fix: add missing open_instance attribute to QuickSourceReader - fix: SimpleSourceReader now returns None instead of raising StopIteration - docs: fix syntax error in __init__.py docstring (missing colon) ## Features - feat: add closed property to SourceReader (#6) - Added closed property similar to io.IOBase.closed - Updated __enter__ and __exit__ to manage _closed flag - Updated QuickSourceReader to properly set closed state - Updated docstring with usage example ## Tests - test: add comprehensive tests for BufferReader blocking parameter - Tests for blocking=True/False behavior - Tests for blocking read when buffer stops - test: add comprehensive tests for QuickSourceReader - Tests for basic functionality, context manager, info, key, iteration - Tests for custom is_valid_data filtering ## Documentation - docs: create comprehensive ISSUE_ANALYSIS.md - Analyzed all 17 open issues - Categorized by status, effort, and dependencies - Identified already-resolved issues (#20, partial #8) - Provided resolution recommendations for each issue ## Test Results - Before: 11 tests (1 failing) - After: 19 tests (all passing) - 8 new tests added - All existing tests fixed and passing ## Issues Addressed - Resolves #6 (closed property for SourceReader) - Documents that #20 is already resolved (blocking parameter exists) - Documents that #8 is partially resolved (blocking exists, timeout could be added) --- ISSUE_ANALYSIS.md | 313 ++++++++++++++++++ stream2py/__init__.py | 2 +- stream2py/source_reader.py | 25 ++ .../tests/test_buffer_reader_blocking.py | 76 +++++ stream2py/tests/test_quick_source_reader.py | 111 +++++++ stream2py/tests/test_util.py | 8 +- stream2py/tests/utils_for_testing.py | 9 +- 7 files changed, 538 insertions(+), 6 deletions(-) create mode 100644 ISSUE_ANALYSIS.md create mode 100644 stream2py/tests/test_buffer_reader_blocking.py create mode 100644 stream2py/tests/test_quick_source_reader.py diff --git a/ISSUE_ANALYSIS.md b/ISSUE_ANALYSIS.md new file mode 100644 index 0000000..d890fa3 --- /dev/null +++ b/ISSUE_ANALYSIS.md @@ -0,0 +1,313 @@ +# Issue Analysis and Resolution Plan + +## Summary + +This document analyzes all 17 open issues in the stream2py repository, categorizes them, identifies dependencies, and provides a resolution strategy. + +## Issue Status Summary + +- **Already Resolved**: 2 issues (#20, partial #8) +- **Simple Fixes**: 4 issues (#6, #10, #18, #9) +- **Medium Effort**: 4 issues (#17, #14, #15, #13) +- **Complex/Ongoing**: 4 issues (#1, #11, #5, #2, #3) +- **External/Needs Investigation**: 2 issues (#4, #16) + +--- + +## Detailed Issue Analysis + +### ✅ Already Resolved + +#### Issue #20: [Add option to make BufferReader blocking to wait for future data](https://github.com/i2mint/stream2py/issues/20) +- **Status**: RESOLVED ✓ +- **Category**: Feature +- **Resolution**: The `blocking` parameter already exists in `BufferReader.read()` (stream2py/buffer_reader.py:329) +- **Recommendation**: Close this issue with a comment explaining that the feature was implemented +- **Code Reference**: `buffer_reader.py:322-401` + +#### Issue #8: [Add parameters (block=False, timeout=None) in BufferReader methods](https://github.com/i2mint/stream2py/issues/8) +- **Status**: PARTIALLY RESOLVED +- **Category**: Feature +- **Resolution**: The `blocking` parameter exists; `timeout` parameter is not implemented +- **Effort**: Simple (add timeout support) +- **Recommendation**: Can close or update to focus only on timeout if desired + +--- + +### ✅ Resolved in This PR + +#### Issue #6: [a `closed` parameter for SourceReader](https://github.com/i2mint/stream2py/issues/6) +- **Status**: ✓ RESOLVED in this PR +- **Category**: Feature +- **Effort**: Simple +- **Resolution**: Added `closed` property to `SourceReader` class, similar to `io.IOBase.closed` +- **Changes Made**: + - Added `_closed` attribute to track open/close state + - Added `closed` property that returns `self._closed` + - Updated `__enter__` and `__exit__` to manage `_closed` flag + - Updated `QuickSourceReader.open()` and `close()` to set flag + - Updated docstring example to demonstrate usage +- **Code Reference**: `source_reader.py:76,129-137,165-172,244-252` + +--- + +### 🔧 Simple Fixes (Recommended for Next Steps) + +#### Issue #10: [Make BufferReader.__next__() more compatible with builtin next()](https://github.com/i2mint/stream2py/issues/10) +- **Status**: Needs review +- **Category**: Enhancement/Bug +- **Effort**: Simple +- **Description**: The `next()` function should raise `StopIteration` or yield the given default value +- **Current Behavior**: `BufferReader.__next__()` returns None while next value is unavailable +- **Expected Behavior**: Should raise `StopIteration` when iterator is exhausted, or return default if provided +- **Recommendation**: Review current implementation and adjust to match Python's iterator protocol + +#### Issue #18: [What should happen if we start to read without starting the reader?](https://github.com/i2mint/stream2py/issues/18) +- **Status**: Design question +- **Category**: Enhancement/Design +- **Effort**: Simple +- **Description**: Should raise an exception if user tries to read before entering reader context +- **Recommendation**: Add a check in `BufferReader.read()` to raise a clear exception if the buffer hasn't been started +- **Related**: See wiki on [Forwarding context management](https://github.com/i2mint/stream2py/wiki/Forwarding-context-management) + +#### Issue #9: [Not skipping tests and linting](https://github.com/i2mint/stream2py/issues/9) +- **Status**: Unclear what the specific issue is +- **Category**: CI/CD +- **Effort**: Simple +- **Current State**: CI configuration (.github/workflows/ci.yml) runs tests on line 43 +- **Recommendation**: Need clarification on what specifically should not be skipped. Current CI runs pytest and pylint. + +--- + +### 🛠️ Medium Effort + +#### Issue #17: [Revise BufferReader](https://github.com/i2mint/stream2py/issues/17) +- **Status**: Relevant +- **Category**: Enhancement +- **Effort**: Medium +- **TODOs Identified**: + 1. `read` must get its defaults from init ✓ (already done via `_read_kwargs`) + 2. `range` must work similarly to `read` (needs review) + 3. Which init args should be keyword-only? (design decision) + 4. Consider making `read_chk_step` and `read_chk_size` (instead of just `peek`) +- **TODOs in Code**: + - `buffer_reader.py:95` - "should `ignore_no_item_found` default be True to align with iter?" + - `stream_buffer.py:125` - "option to auto restart source on read exception" +- **Recommendation**: Address each TODO item systematically + +#### Issue #14: [Add support for slicing joinable data items](https://github.com/i2mint/stream2py/issues/14) +- **Status**: Relevant +- **Category**: Feature +- **Effort**: Medium +- **Description**: For data like waveform chunks that can be joined, `BufferReader.range()` should optionally join and trim to get exact query results +- **Recommendation**: Add optional abstract methods to `SourceReader` for joining and slicing read data items +- **Benefit**: More precise range queries for chunked data + +#### Issue #15: [Review exception objects raised and figure out custom ones](https://github.com/i2mint/stream2py/issues/15) +- **Status**: Relevant +- **Category**: Enhancement +- **Effort**: Medium +- **Recommendation**: Audit all exception raising in the codebase and create custom exception classes where appropriate (e.g., `StreamNotStartedError`, `BufferOverflowError`, etc.) + +#### Issue #13: [keyboard_and_audio not working in notebook](https://github.com/i2mint/stream2py/issues/13) +- **Status**: Relevant +- **Category**: Bug +- **Effort**: Medium +- **Description**: Terminal-based keyboard input (using termios) doesn't work in notebooks +- **Error**: `termios.error: (25, 'Inappropriate ioctl for device')` +- **Recommendation**: Add conditional imports and provide alternative input method for notebook environments, or document limitation + +--- + +### 📦 Complex/Ongoing (Large Scope) + +#### Issue #1: [Create usage examples and helper classes](https://github.com/i2mint/stream2py/issues/1) +- **Status**: Ongoing +- **Category**: Documentation/Examples +- **Effort**: Complex +- **Current Progress**: + - ✓ webcam (moved to videostream2py) + - ✓ keyboard input (moved to keyboardstream2py) + - ✓ audio (moved to audiostream2py) + - ⏳ url stream + - ⏳ General saving to files + - ⏳ Event based actions + - ⏳ Data visualization + - ⏳ BufferConsumer class +- **Recommendation**: Continue incremental progress; many items moved to separate plugin packages + +#### Issue #11: [Various sources](https://github.com/i2mint/stream2py/issues/11) +- **Status**: Ongoing +- **Category**: Feature +- **Effort**: Complex +- **Scope**: Create SourceReaders for various sources (webcam video/audio, web-audio, keyboard, mouse, wifi packets, bluetooth, CPU/RAM usage, etc.) +- **Current Progress**: Several moved to separate packages (audiostream2py, videostream2py, keyboardstream2py, etc.) +- **Recommendation**: Continue creating SourceReaders in separate plugin packages + +#### Issue #5: [Generalize StreamBuffer and BufferReader consumers](https://github.com/i2mint/stream2py/issues/5) +- **Status**: Relevant +- **Category**: Enhancement/Design +- **Effort**: Complex +- **Description**: Identify common design patterns from example usage and create abstract classes or helpers +- **Patterns to Consider**: + 1. Consumers using single source for single purpose + 2. Consumers using two sources (one watches for events, performs actions with other) + 3. Asynchronous vs synchronous patterns +- **Recommendation**: Work on this after creating more examples to identify patterns + +#### Issue #2: [Tag Sound Events](https://github.com/i2mint/stream2py/issues/2) +- **Status**: Relevant (example application) +- **Category**: Example +- **Effort**: Medium-Complex +- **Description**: Create example app to annotate audio events with key presses +- **Recommendation**: Good example project for demonstrating multi-source event handling + +#### Issue #3: [Two Source Event Trigger: Record as You Type](https://github.com/i2mint/stream2py/issues/3) +- **Status**: Relevant (example application) +- **Category**: Example +- **Effort**: Medium +- **Description**: Save audio recorded while typing, capturing before/after typing +- **Recommendation**: Good example demonstrating event-triggered recording + +--- + +### 🔍 External/Needs Investigation + +#### Issue #4: [New webcam SourceReader](https://github.com/i2mint/stream2py/issues/4) +- **Status**: External +- **Category**: Feature (external) +- **Resolution**: Webcam functionality moved to separate package: [videostream2py](https://github.com/i2mint/videostream2py) +- **Recommendation**: Close with comment directing to videostream2py + +#### Issue #16: [Address the BufferReader(...source_reader_info...) issue](https://github.com/i2mint/stream2py/issues/16) +- **Status**: Needs investigation +- **Category**: Bug/Design +- **Reference**: [Review notes on wiki](https://github.com/i2mint/stream2py/wiki/Review-notes) +- **Effort**: Unknown (needs wiki review) +- **Recommendation**: Review wiki notes to understand the issue, then categorize + +--- + +## Dependency Relationships + +### No Strong Dependencies +Most issues are independent, but some logical groupings exist: + +**Testing & Error Handling Group:** +- #10 (BufferReader.__next__() compatibility) +- #15 (Review exception objects) +- #18 (Read without starting reader) + +**Enhancement Group:** +- #17 (Revise BufferReader) +- #14 (Slicing joinable data) +- #8 (Add timeout parameter) + +**Examples Group:** +- #1 (Usage examples) +- #2 (Tag Sound Events) +- #3 (Record as You Type) +- #5 (Generalize consumers - depends on examples) + +--- + +## Commit Strategy + +### Commits Already Made in This PR: +1. **test: fix Python 3.11+ compatibility in test_util.py** + - Fixed TypeError vs AttributeError for context manager protocol + +2. **docs: fix syntax error in __init__.py docstring** + - Added missing colon in property definition + +3. **fix: add missing open_instance attribute to QuickSourceReader** + - Prevents AttributeError when accessing info property + +4. **fix: SimpleSourceReader should return None instead of raising StopIteration** + - Aligns with SourceReader contract + +5. **test: add comprehensive tests for blocking parameter** + - Tests for BufferReader.read(blocking=True/False) + - Tests for blocking behavior when buffer stops + +6. **test: add comprehensive tests for QuickSourceReader** + - Tests for basic functionality, context manager, info, key, iteration + - Tests for custom is_valid_data filtering + +7. **feat: add closed property to SourceReader (#6)** + - Added closed property similar to io.IOBase.closed + - Updated __enter__ and __exit__ to manage _closed flag + - Updated QuickSourceReader to properly set closed state + - Updated docstring with example usage + +### Recommended Next Steps (Future PRs): + +**Priority 1 - Quick Wins:** +- Fix #10 (BufferReader.__next__() compatibility) +- Address #18 (Read without starting reader check) +- Clarify #9 (Tests and linting) + +**Priority 2 - Medium Effort:** +- Work through #17 TODOs (Revise BufferReader) +- Implement #14 (Slicing joinable data) +- Fix #13 (Notebook compatibility) +- Complete #15 (Custom exceptions) + +**Priority 3 - Ongoing:** +- Continue #1 (Examples and helpers) +- Create #2 and #3 example applications +- Work on #5 after more examples exist + +**Cleanup:** +- Close #20 (already implemented) +- Close #4 (moved to videostream2py) +- Investigate #16 (review wiki) + +--- + +## Testing Strategy + +### Test Coverage Improvements in This PR: +- ✅ Fixed failing test in test_util.py (Python 3.11+ compatibility) +- ✅ Added tests for blocking parameter (2 new tests) +- ✅ Added tests for QuickSourceReader (6 new tests) +- ✅ Fixed QuickSourceReader bug (missing open_instance) +- ✅ Fixed SimpleSourceReader bug (StopIteration handling) + +### Test Results: +- **Before**: 11 tests (1 failing) +- **After**: 19 tests (all passing) +- **New Coverage**: BufferReader blocking behavior, QuickSourceReader functionality + +--- + +## Documentation Improvements in This PR: + +1. Fixed syntax error in `__init__.py` docstring (line 113) +2. Added documentation for `closed` property in SourceReader +3. Updated SourceReader docstring with example of closed property usage +4. Created comprehensive test files with clear docstrings + +--- + +## Recommendations for Issue Comments + +For each issue, I recommend adding a comment with: +- Current status assessment +- Whether it's resolved, in progress, or needs work +- For resolved issues: what code addresses it +- For obsolete issues: why it's no longer relevant +- For actionable issues: estimated effort and approach + +--- + +## Conclusion + +This analysis covers all 17 open issues. The work completed in this PR addresses: +- 2 bugs fixed (QuickSourceReader, SimpleSourceReader) +- 1 feature implemented (closed property, #6) +- 1 test compatibility issue fixed +- 8 new tests added +- 1 documentation error fixed + +The remaining issues are categorized and prioritized for future work. diff --git a/stream2py/__init__.py b/stream2py/__init__.py index 3311f7d..7f9ef28 100644 --- a/stream2py/__init__.py +++ b/stream2py/__init__.py @@ -110,7 +110,7 @@ def key(self, data: EnumeratedReadData): return data[0] @property - def info(self) + def info(self): return dict(file=self.file, mode=self.mode) 2. The StreamBuffer diff --git a/stream2py/source_reader.py b/stream2py/source_reader.py index 75940c3..c0ab62f 100644 --- a/stream2py/source_reader.py +++ b/stream2py/source_reader.py @@ -29,6 +29,7 @@ class SourceReader(Source, metaclass=ABCMeta): ... def open(self): ... self.open_count += 1 ... self.range_iterator = iter(range(self.start, self.stop)) + ... self._closed = False ... ... def read(self): ... value = next(self.range_iterator, None) @@ -40,6 +41,7 @@ class SourceReader(Source, metaclass=ABCMeta): ... def close(self): ... del self.range_iterator ... self.range_iterator = None + ... self._closed = True ... ... @property ... def info(self): @@ -49,12 +51,18 @@ class SourceReader(Source, metaclass=ABCMeta): ... return int(data[1:]) ... >>> source_reader = SimpleCounterString(start=0, stop=10) + >>> source_reader.closed + True >>> source_reader.open() + >>> source_reader.closed + False >>> source_reader.info {'start': 0, 'stop': 10, 'open_count': 1} >>> source_reader.read() 's0' >>> source_reader.close() + >>> source_reader.closed + True >>> with source_reader: ... source_reader.info ... source_reader.read() @@ -67,6 +75,7 @@ class SourceReader(Source, metaclass=ABCMeta): """ _sleep_time_on_iter_none_s = 0.001 + _closed = True # SourceReader starts in closed state def __iter__(self): while True: @@ -119,6 +128,16 @@ def key(self, data: Any) -> ComparableType: "Implement the 'key' method to convert data into a comparable value to sort by" ) + @property + def closed(self) -> bool: + """True if the source reader is closed, False if it's open. + + Similar to io.IOBase.closed property. + + :return: bool indicating whether the source is closed + """ + return self._closed + @property def sleep_time_on_read_none_s(self) -> Optional[Union[int, float]]: """Sets default sleep time for StreamBuffer when it reads None from SourceReader. @@ -147,10 +166,12 @@ def get_timestamp(cls) -> int: def __enter__(self): self.open() + self._closed = False return self def __exit__(self, exc_type, exc_val, exc_tb) -> None: self.close() + self._closed = True enter, exit = __enter__, __exit__ @@ -185,6 +206,7 @@ class QuickSourceReader(SourceReader): read_idx = None open_time = None + open_instance = None def __iter__(self) -> Iterator[StreamItem]: while True: @@ -224,9 +246,12 @@ def when_no_data(self, _next=None): def open(self): self.read_idx = 0 self.open_time = self.get_timestamp() + self.open_instance = self + self._closed = False def close(self): """A dummy close class""" + self._closed = True @property def info(self): diff --git a/stream2py/tests/test_buffer_reader_blocking.py b/stream2py/tests/test_buffer_reader_blocking.py new file mode 100644 index 0000000..a9771de --- /dev/null +++ b/stream2py/tests/test_buffer_reader_blocking.py @@ -0,0 +1,76 @@ +"""Tests for BufferReader blocking parameter""" +import time +import threading +from stream2py import StreamBuffer +from stream2py.tests.utils_for_testing import SimpleSourceReader +from itertools import count + + +def test_buffer_reader_blocking(): + """Test that blocking parameter works correctly for BufferReader.read()""" + # Create a source with enough data + source = SimpleSourceReader(range(100)) + + with StreamBuffer(source, maxlen=100) as buffer: + reader = buffer.mk_reader() + + # Wait a moment for some data to be available + time.sleep(0.2) + + # Non-blocking read should return data immediately when available + result = reader.read(blocking=False, ignore_no_item_found=True) + assert result is not None, "Non-blocking read should return data" + + # Read a few more items with blocking=False + for _ in range(3): + result = reader.read(blocking=False, ignore_no_item_found=True) + assert result is not None + + # Read with blocking=True should also work when data is available + result = reader.read(blocking=True) + assert result is not None, "Blocking read should return data when available" + + # Create a second reader that starts with no data yet read + reader2 = buffer.mk_reader() + + # The second reader should be able to read with blocking=True + result = reader2.read(blocking=True) + assert result is not None, "Blocking read from new reader should get data" + + +def test_buffer_reader_blocking_stops_on_stop_event(): + """Test that blocking read returns None when buffer is stopped""" + source = SimpleSourceReader(range(5)) + + buffer = StreamBuffer(source, maxlen=100) + buffer.start() + reader = buffer.mk_reader() + + # Read all available data + time.sleep(0.1) + while reader.read(blocking=False, ignore_no_item_found=True) is not None: + pass + + results = [] + + def blocking_read_after_stop(): + # This should return None when buffer stops + result = reader.read(blocking=True, ignore_no_item_found=False) + results.append(result) + + # Start blocking read + read_thread = threading.Thread(target=blocking_read_after_stop, daemon=True) + read_thread.start() + + # Give it a moment to start blocking + time.sleep(0.1) + + # Stop the buffer + buffer.stop() + + # Wait for thread to complete + read_thread.join(timeout=2) + + # Should have gotten None because buffer stopped + assert len(results) == 1 + assert results[0] is None, "Blocking read should return None when stopped" diff --git a/stream2py/tests/test_quick_source_reader.py b/stream2py/tests/test_quick_source_reader.py new file mode 100644 index 0000000..2464d42 --- /dev/null +++ b/stream2py/tests/test_quick_source_reader.py @@ -0,0 +1,111 @@ +"""Tests for QuickSourceReader""" +import pytest +from stream2py.source_reader import QuickSourceReader + + +class SimpleQuickReader(QuickSourceReader): + """A simple QuickSourceReader that reads from a list""" + + def __init__(self, data): + self.data = iter(data) + + def read(self): + try: + return next(self.data) + except StopIteration: + return None + + +def test_quick_source_reader_basic(): + """Test basic QuickSourceReader functionality""" + reader = SimpleQuickReader([1, 2, 3, 4, 5]) + + # Check that open sets attributes + assert reader.open_time is None + assert reader.read_idx is None + + reader.open() + assert reader.open_time is not None + assert reader.read_idx == 0 + + # Check that read works + assert reader.read() == 1 + assert reader.read() == 2 + + # Check close + reader.close() # Should do nothing but not raise + + +def test_quick_source_reader_context_manager(): + """Test QuickSourceReader as context manager""" + reader = SimpleQuickReader([1, 2, 3]) + + with reader: + assert reader.open_time is not None + assert reader.read() == 1 + assert reader.read() == 2 + + +def test_quick_source_reader_info(): + """Test QuickSourceReader info property""" + reader = SimpleQuickReader([1, 2, 3]) + reader.open() + + info = reader.info + assert 'open_time' in info + assert 'open_instance' in info + assert info['open_time'] == reader.open_time + + +def test_quick_source_reader_key(): + """Test QuickSourceReader key method (default returns data itself)""" + reader = SimpleQuickReader([1, 2, 3]) + + # Default key should return the data itself + assert reader.key(5) == 5 + assert reader.key('test') == 'test' + + +def test_quick_source_reader_iteration(): + """Test QuickSourceReader iteration behavior""" + reader = SimpleQuickReader([1, 2, 3]) + reader.open() + + # Iteration should yield valid data and skip None + results = [] + for i, item in enumerate(reader): + results.append(item) + if i >= 2: # Get first 3 items + break + + assert results == [1, 2, 3] + + +def test_quick_source_reader_custom_is_valid_data(): + """Test QuickSourceReader with custom is_valid_data""" + + class FilteringQuickReader(QuickSourceReader): + def __init__(self, data): + self.data = iter(data) + + def read(self): + try: + return next(self.data) + except StopIteration: + return None + + def is_valid_data(self, data): + # Only consider even numbers as valid + return data is not None and data % 2 == 0 + + reader = FilteringQuickReader([1, 2, 3, 4, 5, 6]) + reader.open() + + # Iteration should only yield even numbers + results = [] + for i, item in enumerate(reader): + results.append(item) + if i >= 2: # Get first 3 valid items + break + + assert results == [2, 4, 6] diff --git a/stream2py/tests/test_util.py b/stream2py/tests/test_util.py index df75e51..db61df3 100644 --- a/stream2py/tests/test_util.py +++ b/stream2py/tests/test_util.py @@ -88,8 +88,12 @@ def test_reader(reader): it_worked = True except Exception as e: it_worked = False - assert isinstance(e, AttributeError) - assert e.args[0] == '__enter__' # well yeah, reader doesn't have an __enter__! + # Python 3.11+ raises TypeError instead of AttributeError for context manager protocol + assert isinstance(e, (AttributeError, TypeError)) + if isinstance(e, AttributeError): + assert e.args[0] == '__enter__' # well yeah, reader doesn't have an __enter__! + else: # TypeError + assert 'context manager' in str(e) assert not it_worked diff --git a/stream2py/tests/utils_for_testing.py b/stream2py/tests/utils_for_testing.py index fb9d84f..c16a42a 100644 --- a/stream2py/tests/utils_for_testing.py +++ b/stream2py/tests/utils_for_testing.py @@ -139,9 +139,12 @@ def open(self) -> None: # separate indexing concern (could be counter, could be time.time()) def read(self): - output = self._current_index, next(self._data) - self._current_index += 1 - return output + try: + output = self._current_index, next(self._data) + self._current_index += 1 + return output + except StopIteration: + return None def close(self): return None From 2c9b7b04087fa8f3fcc256421a4a569e28d88d18 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 14 Nov 2025 01:06:29 +0000 Subject: [PATCH 2/2] docs: add PR description for pull request --- PR_DESCRIPTION.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 PR_DESCRIPTION.md diff --git a/PR_DESCRIPTION.md b/PR_DESCRIPTION.md new file mode 100644 index 0000000..dd07612 --- /dev/null +++ b/PR_DESCRIPTION.md @@ -0,0 +1,70 @@ +# Pull Request: Improve test coverage, fix bugs, add closed property, and analyze issues + +## Summary +This PR improves the stream2py codebase by enhancing test coverage, fixing several bugs, implementing the `closed` property feature, and providing comprehensive analysis of all open issues. + +## Changes Made + +### Bug Fixes +- **Python 3.11+ compatibility**: Fixed `test_util.py` to handle both `TypeError` and `AttributeError` for context manager protocol violations +- **QuickSourceReader bug**: Added missing `open_instance` attribute that was referenced in `info` property but never set +- **SimpleSourceReader bug**: Fixed to return `None` instead of raising `StopIteration` when data is exhausted +- **Documentation**: Fixed syntax error in `__init__.py` docstring (missing colon in property definition) + +### Features +- **Closed property (#6)**: Implemented `closed` property for `SourceReader` similar to `io.IOBase.closed` + - Added `_closed` attribute to track open/close state + - Updated `__enter__` and `__exit__` to manage the flag + - Updated `QuickSourceReader` to properly set closed state + - Added docstring examples demonstrating usage + +### Tests +- **BufferReader blocking tests**: Added comprehensive tests for the `blocking` parameter + - Tests for `blocking=True` and `blocking=False` behavior + - Tests for blocking read when buffer stops +- **QuickSourceReader tests**: Added 6 new tests covering: + - Basic functionality + - Context manager usage + - Info property + - Key method + - Iteration behavior + - Custom `is_valid_data` filtering + +### Documentation +- **ISSUE_ANALYSIS.md**: Created comprehensive analysis of all 17 open issues + - Categorized by status: already resolved, simple fixes, medium effort, complex/ongoing, external + - Identified dependencies and relationships between issues + - Provided resolution recommendations for each + - Documented that #20 is already resolved and #8 is partially resolved + +## Test Results +- **Before**: 11 tests (1 failing) +- **After**: 19 tests (all passing) +- **Coverage**: +8 new tests added + +## Issues Addressed +- Resolves #6 (closed property for SourceReader) +- Documents #20 as already resolved (blocking parameter exists in BufferReader.read()) +- Documents #8 as partially resolved (blocking exists, timeout could be added) + +## Files Changed +- `stream2py/__init__.py` - Fixed docstring syntax error +- `stream2py/source_reader.py` - Added closed property, fixed QuickSourceReader bug +- `stream2py/tests/test_util.py` - Fixed Python 3.11+ compatibility +- `stream2py/tests/utils_for_testing.py` - Fixed SimpleSourceReader StopIteration handling +- `stream2py/tests/test_buffer_reader_blocking.py` - New test file for blocking parameter +- `stream2py/tests/test_quick_source_reader.py` - New test file for QuickSourceReader +- `ISSUE_ANALYSIS.md` - New comprehensive issue analysis document + +## Breaking Changes +None - all changes are backward compatible. + +## Checklist +- [x] All tests passing (19/19) +- [x] Documentation updated +- [x] Backward compatible +- [x] Issue analysis completed +- [x] Code formatted + +## Next Steps +See `ISSUE_ANALYSIS.md` for recommended next steps and prioritization of remaining issues.