From df92f8c5c7ea506d7420754c31e974e0a72acfff Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Wed, 7 Oct 2026 19:31:53 +0000 Subject: [PATCH 1/2] Port 'Add TryGetChapters to ScriptureRangeParser' from machine PR #497 Co-authored-by: Eli C. Lowry <83078660+Enkidu93@users.noreply.github.com> Co-Authored-By: Claude Sonnet 5.5 --- machine/scripture/__init__.py | 3 ++- machine/scripture/parse.py | 11 ++++++++++- tests/scripture/test_parse.py | 16 +++++++++++++++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/machine/scripture/__init__.py b/machine/scripture/__init__.py index 40863708..bb0080a4 100644 --- a/machine/scripture/__init__.py +++ b/machine/scripture/__init__.py @@ -20,7 +20,7 @@ SEPTUAGINT_VERSIFICATION, VULGATE_VERSIFICATION, ) -from .parse import get_books, get_chapters +from .parse import get_books, get_chapters, try_get_chapters from .verse_ref import ( NULL_VERSIFICATION, VERSE_RANGE_SEPARATOR, @@ -44,6 +44,7 @@ "get_bbbcccvvv", "get_books", "get_chapters", + "try_get_chapters", "is_book_id_valid", "is_canonical", "is_nt", diff --git a/machine/scripture/parse.py b/machine/scripture/parse.py index c5878302..c7f5173e 100644 --- a/machine/scripture/parse.py +++ b/machine/scripture/parse.py @@ -1,5 +1,5 @@ import re -from typing import Dict, List, Set, Union +from typing import Dict, List, Optional, Set, Tuple, Union from .canon import book_id_to_number, book_number_to_id from .constants import ORIGINAL_VERSIFICATION @@ -179,3 +179,12 @@ def get_chapters( chapters[book] = selection_chapters[book] return chapters + + +def try_get_chapters( + selections: Union[str, List[str]], versification: Versification = ORIGINAL_VERSIFICATION +) -> Tuple[bool, Optional[Dict[int, List[int]]]]: + try: + return True, get_chapters(selections, versification) + except ValueError: + return False, None diff --git a/tests/scripture/test_parse.py b/tests/scripture/test_parse.py index b56c3451..2c45abb3 100644 --- a/tests/scripture/test_parse.py +++ b/tests/scripture/test_parse.py @@ -1,6 +1,6 @@ from pytest import raises -from machine.scripture import get_books, get_chapters +from machine.scripture import get_books, get_chapters, try_get_chapters def test_get_books() -> None: @@ -177,3 +177,17 @@ def test_get_chapters() -> None: get_chapters("OT,MAT1") with raises(ValueError): get_chapters("OT,MAT-LUK") + + +def test_try_get_chapters() -> None: + assert try_get_chapters("MAT1-4,12,9") == (True, {40: [1, 2, 3, 4, 9, 12]}) + assert try_get_chapters("") == (True, {}) + assert try_get_chapters(["MAT", "-MAT 1"]) == (True, {40: list(range(2, 29))}) + + # invalid syntax, book, chapter, and subtraction + assert try_get_chapters("OT,MAT1") == (False, None) + assert try_get_chapters("ABC") == (False, None) + assert try_get_chapters("MAT 500") == (False, None) + assert try_get_chapters("MAT3-1") == (False, None) + assert try_get_chapters("-MRK") == (False, None) + assert try_get_chapters("MRK 2-5;-MRK 6") == (False, None) From 039e2690ba72995ae6cc1a1293629ad0cf2a7264 Mon Sep 17 00:00:00 2001 From: "claude[bot]" <41898282+claude[bot]@users.noreply.github.com> Date: Mon, 12 Oct 2026 23:50:18 +0000 Subject: [PATCH 2/2] Reject empty segments and return Optional from try_get_chapters Co-Authored-By: Claude Sonnet 5.5 Co-authored-by: Eli C. Lowry <83078660+Enkidu93@users.noreply.github.com> --- machine/scripture/parse.py | 10 ++++++---- tests/scripture/test_parse.py | 31 ++++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/machine/scripture/parse.py b/machine/scripture/parse.py index c7f5173e..5fa52b77 100644 --- a/machine/scripture/parse.py +++ b/machine/scripture/parse.py @@ -1,5 +1,5 @@ import re -from typing import Dict, List, Optional, Set, Tuple, Union +from typing import Dict, List, Optional, Set, Union from .canon import book_id_to_number, book_number_to_id from .constants import ORIGINAL_VERSIFICATION @@ -57,6 +57,8 @@ def _update_selection(book_set: Set[int], book_nums: Set[int], subtraction: bool def parse_selection(selection: str, versification: Versification) -> Dict[int, List[int]]: selection = selection.strip() + if len(selection) == 0: + raise ValueError("Empty book range.") chapters = {} if selection[-1].isdigit() and len(selection) > 3: # Specific chapters from one book @@ -183,8 +185,8 @@ def get_chapters( def try_get_chapters( selections: Union[str, List[str]], versification: Versification = ORIGINAL_VERSIFICATION -) -> Tuple[bool, Optional[Dict[int, List[int]]]]: +) -> Optional[Dict[int, List[int]]]: try: - return True, get_chapters(selections, versification) + return get_chapters(selections, versification) except ValueError: - return False, None + return None diff --git a/tests/scripture/test_parse.py b/tests/scripture/test_parse.py index 2c45abb3..caaae7f2 100644 --- a/tests/scripture/test_parse.py +++ b/tests/scripture/test_parse.py @@ -178,16 +178,29 @@ def test_get_chapters() -> None: with raises(ValueError): get_chapters("OT,MAT-LUK") + # empty segments + with raises(ValueError): + get_chapters("MAT;;MRK") + with raises(ValueError): + get_chapters(" ; ") + with raises(ValueError): + get_chapters(";") + def test_try_get_chapters() -> None: - assert try_get_chapters("MAT1-4,12,9") == (True, {40: [1, 2, 3, 4, 9, 12]}) - assert try_get_chapters("") == (True, {}) - assert try_get_chapters(["MAT", "-MAT 1"]) == (True, {40: list(range(2, 29))}) + assert try_get_chapters("MAT1-4,12,9") == {40: [1, 2, 3, 4, 9, 12]} + assert try_get_chapters("") == {} + assert try_get_chapters(["MAT", "-MAT 1"]) == {40: list(range(2, 29))} # invalid syntax, book, chapter, and subtraction - assert try_get_chapters("OT,MAT1") == (False, None) - assert try_get_chapters("ABC") == (False, None) - assert try_get_chapters("MAT 500") == (False, None) - assert try_get_chapters("MAT3-1") == (False, None) - assert try_get_chapters("-MRK") == (False, None) - assert try_get_chapters("MRK 2-5;-MRK 6") == (False, None) + assert try_get_chapters("OT,MAT1") is None + assert try_get_chapters("ABC") is None + assert try_get_chapters("MAT 500") is None + assert try_get_chapters("MAT3-1") is None + assert try_get_chapters("-MRK") is None + assert try_get_chapters("MRK 2-5;-MRK 6") is None + + # empty segments + assert try_get_chapters("MAT;;MRK") is None + assert try_get_chapters(" ; ") is None + assert try_get_chapters(";") is None