diff --git a/src/semchunk/semchunk.py b/src/semchunk/semchunk.py index 6538383..a8cb1e2 100644 --- a/src/semchunk/semchunk.py +++ b/src/semchunk/semchunk.py @@ -227,6 +227,10 @@ def chunk( ilgs_doc = None if is_first_call := not _recursion_depth: + # A non-positive chunk_size fits no token, so the recursion never terminates. + if chunk_size < 1: + raise ValueError(f"chunk_size must be a positive integer, not {chunk_size!r}.") + # Memoize the token counter if desired. if memoize: token_counter = _memoized_token_counters.setdefault(token_counter, lru_cache(cache_maxsize)(token_counter)) diff --git a/tests/test_semchunk.py b/tests/test_semchunk.py index 209de8f..ba6aad6 100644 --- a/tests/test_semchunk.py +++ b/tests/test_semchunk.py @@ -199,5 +199,27 @@ def test_semchunk() -> None: # Test chunking whitespace to ensure no errors are raised. semchunk.chunk('\n\n', 512, lambda *args: 0) +def test_non_positive_chunk_size() -> None: + """Test that a non-positive chunk size raises a clear ``ValueError`` rather than recursing until a ``RecursionError``.""" + token_counter = len + + for bad_size in (0, -1, -100): + # Directly via `chunk`. + try: + semchunk.chunk('hello world', bad_size, token_counter) + except ValueError: + pass + else: + raise AssertionError(f'chunk() with chunk_size={bad_size!r} did not raise ValueError') + + # And via a `chunkerify`-built chunker, which defers to `chunk`. + try: + semchunk.chunkerify(token_counter, bad_size)('hello world') + except ValueError: + pass + else: + raise AssertionError(f'chunkerify() with chunk_size={bad_size!r} did not raise ValueError') + if __name__ == '__main__': - test_semchunk() \ No newline at end of file + test_semchunk() + test_non_positive_chunk_size() \ No newline at end of file