From cd21843f3bc7df52ff10423bdbebea109a8331e6 Mon Sep 17 00:00:00 2001 From: qinpeili <2522922661@qq.com> Date: Sat, 26 Sep 2026 03:20:35 +0800 Subject: [PATCH] chatformat: reject a non-positive split limit instead of hanging split() with limit<=0 cut at 0 (or -1), appended text[:0] forever and never shortened text, growing a list until the process died. A two-line guard raises ValueError at the door; regression tests cover limit=0, limit=-5 and the limit=1 boundary. --- taskuary/chatformat.py | 5 +++++ tests/test_chatformat.py | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/taskuary/chatformat.py b/taskuary/chatformat.py index 9fffbe2c..02e80616 100644 --- a/taskuary/chatformat.py +++ b/taskuary/chatformat.py @@ -171,6 +171,11 @@ def split(text: str, limit: int = HARD) -> list: """One section that will not fit, broken where a reader would break it: a blank line, then a line end, and only as a last resort a space. The old splitter took max() of those three positions, so the space always won and every break landed mid-sentence.""" + if limit < 1: + # A non-positive limit makes every cut 0 (or -1): text[:0] is appended, text never gets + # shorter, and the loop grows a list for ever (issue #60). Callers pass sensible limits, + # so this is a bug in the caller - say so instead of hanging the process. + raise ValueError(f'limit must be at least 1, got {limit}') out = [] while len(text) > limit: window = text[:limit] diff --git a/tests/test_chatformat.py b/tests/test_chatformat.py index cc20e443..3ae56c3a 100644 --- a/tests/test_chatformat.py +++ b/tests/test_chatformat.py @@ -170,6 +170,21 @@ def test_blocks_leave_the_spelling_to_the_door(self): self.assertIn('## Top', got[0]) +class SplitLimitTests(unittest.TestCase): + """split() is pure and its callers pass sensible limits, so a limit <= 0 was latent - but the + arithmetic made it hang: every cut was 0 (or -1), text[:0] was appended and text never got + shorter, so the loop grew a list for ever. Reject it at the door.""" + + def test_a_non_positive_limit_raises_instead_of_hanging(self): + with self.assertRaises(ValueError): + cf.split('abc', 0) + with self.assertRaises(ValueError): + cf.split('abc', -5) + + def test_limit_one_still_splits_one_character_at_a_time(self): + self.assertEqual(cf.split('ab', 1), ['a', 'b']) + + if __name__ == '__main__': unittest.main()