Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.15.5] - 2026-01-24

### Fixed

- [Issue #235](https://github.com/avendesora/pythonbible/issues/235) - Error getting reference to Jonah

## [0.15.4] - 2026-01-01

### Changed
Expand Down Expand Up @@ -234,7 +240,8 @@ The goal of this release was to address [Issue #90], and to make things related

## [0.0.1] - 2020-10-08

[unreleased]: https://github.com/avendesora/pythonbible/compare/v0.15.4...HEAD
[unreleased]: https://github.com/avendesora/pythonbible/compare/v0.15.5...HEAD
[0.15.5]: https://github.com/avendesora/pythonbible/compare/v0.15.4...v0.15.5
[0.15.4]: https://github.com/avendesora/pythonbible/compare/v0.15.3...v0.15.4
[0.15.3]: https://github.com/avendesora/pythonbible/compare/v0.15.2...v0.15.3
[0.15.2]: https://github.com/avendesora/pythonbible/compare/v0.15.1...v0.15.2
Expand Down
2 changes: 1 addition & 1 deletion docs/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pythonbible-docs"
version = "0.15.4"
version = "0.15.5"
description-file = "README.md"
requires-python = ">=3.13"
authors = [
Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
author = "Nathan Patton"

# The full version, including alpha/beta/rc tags
release = "0.15.4"
release = "0.15.5"


# -- General configuration ---------------------------------------------------
Expand Down
4 changes: 2 additions & 2 deletions pythonbible/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ module-name = "pythonbible"

[project]
name = "pythonbible"
version = "0.15.4"
version = "0.15.5"
description-file = "README.md"
requires-python = ">=3.10"
authors = [
Expand Down Expand Up @@ -172,7 +172,7 @@ ignore = [

[tool.ruff.lint.per-file-ignores]
"pythonbible/bible/bible.py" = ["PLR0913", "FBT001", "FBT002"]
"pythonbible/books.py" = ["ARG004", "PYI034"]
"pythonbible/books/__init__.py" = ["ARG004", "PYI034"]
"pythonbible/book_groups.py" = ["ARG004", "PYI034"]
"pythonbible/formatter.py" = ["ANN401", "FBT001", "FBT002", "PLR0911"]
"pythonbible/parser.py" = ["PLR2004"]
Expand Down
2 changes: 1 addition & 1 deletion pythonbible/pythonbible/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from __future__ import annotations

__version__ = "0.15.4"
__version__ = "0.15.5"

from .bible import add_bible
from .bible import get_bible
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@
from typing import Any
from typing import Type

from pythonbible.books.common_constants import FIRST
from pythonbible.books.common_constants import FIRST_BOOK
from pythonbible.books.common_constants import SECOND
from pythonbible.books.common_constants import SECOND_BOOK
from pythonbible.books.common_constants import THIRD
from pythonbible.books.john import JOHN_REGULAR_EXPRESSION


def _build_book_regular_expression(
book: str,
Expand All @@ -24,30 +31,22 @@ def _add_suffix(regex: str, suffix: str | None = None) -> str:
_SAMUEL_REGULAR_EXPRESSION = r"(Samuel|Sam\.*|Sa\.*|Sm\.*)"
_KINGS_REGULAR_EXPRESSION = r"(Kings|Kgs\.*|Kin\.*|Ki\.*)"
_CHRONICLES_REGULAR_EXPRESSION = r"(Chronicles|Chron\.*|Chro\.*|Chr\.*|Ch\.*)"
_JOHN_REGULAR_EXPRESSION = r"(John|Joh\.*|Jhn\.*|Jo\.*(?!shua|b|nah|el)|Jn\.*)"
_CORINTHIANS_REGULAR_EXPRESSION = r"Co\.*(?:r\.*(?:inthians)?)?"
_THESSALONIANS_REGULAR_EXPRESSION = r"Th\.*(?:(s|(es(?:s)?))\.*(?:alonians)?)?"
_TIMOTHY_REGULAR_EXPRESSION = r"Ti\.*(?:m\.*(?:othy)?)?"
_PETER_REGULAR_EXPRESSION = r"(Pe\.*(?:t\.*(?:er)?)?|Pt\.*)"

_MACCABEES_REGULAR_EXPRESSION = r"(Maccabees|Macc\.*|Mac\.*|Ma\.*|M\.*)"

_FIRST = r"1|I\s+|1st\s+|First\s+"
_SECOND = r"2|II|2nd\s+|Second\s+"
_THIRD = r"3|III|3rd\s+|Third\s+"

_FIRST_BOOK = rf"{_FIRST}|(First\s+Book\s+of(?:\s+the)?)"
_SECOND_BOOK = rf"{_SECOND}|(Second\s+Book\s+of(?:\s+the)?)"

_EPISTLE_OF_PAUL_TO = r"Epistle\s+of\s+Paul\s+(?:the\s+Apostle\s+)?to(?:\s+the)?"
_GENERAL_EPISTLE_OF = r"(?:General\s+)?Epistle\s+(?:General\s+)?of"

_FIRST_PAUL_EPISTLE = rf"{_FIRST}|(First\s+{_EPISTLE_OF_PAUL_TO})"
_SECOND_PAUL_EPISTLE = rf"{_SECOND}|(Second\s+{_EPISTLE_OF_PAUL_TO})"
_FIRST_PAUL_EPISTLE = rf"{FIRST}|(First\s+{_EPISTLE_OF_PAUL_TO})"
_SECOND_PAUL_EPISTLE = rf"{SECOND}|(Second\s+{_EPISTLE_OF_PAUL_TO})"

_FIRST_GENERAL_EPISTLE = rf"{_FIRST}|(First\s+{_GENERAL_EPISTLE_OF})"
_SECOND_GENERAL_EPISTLE = rf"{_SECOND}|(Second\s+{_GENERAL_EPISTLE_OF})"
_THIRD_GENERAL_EPISTLE = rf"{_THIRD}|(Third\s+{_GENERAL_EPISTLE_OF})"
_FIRST_GENERAL_EPISTLE = rf"{FIRST}|(First\s+{_GENERAL_EPISTLE_OF})"
_SECOND_GENERAL_EPISTLE = rf"{SECOND}|(Second\s+{_GENERAL_EPISTLE_OF})"
_THIRD_GENERAL_EPISTLE = rf"{THIRD}|(Third\s+{_GENERAL_EPISTLE_OF})"


class Book(Enum):
Expand Down Expand Up @@ -111,7 +110,7 @@ def abbreviations(self: Book) -> tuple[str, ...]:
"1 Samuel",
_build_book_regular_expression(
_SAMUEL_REGULAR_EXPRESSION,
prefix=_FIRST_BOOK,
prefix=FIRST_BOOK,
suffix=r"Otherwise\s+Called\s+The\s+First\s+Book\s+of\s+the\s+Kings",
),
("Sa", "Sam", "Sm"),
Expand All @@ -121,7 +120,7 @@ def abbreviations(self: Book) -> tuple[str, ...]:
"2 Samuel",
_build_book_regular_expression(
_SAMUEL_REGULAR_EXPRESSION,
prefix=_SECOND_BOOK,
prefix=SECOND_BOOK,
suffix=r"Otherwise\s+Called\s+The\s+Second\s+Book\s+of\s+the\s+Kings",
),
("Sa", "Sam", "Sm"),
Expand All @@ -131,7 +130,7 @@ def abbreviations(self: Book) -> tuple[str, ...]:
"1 Kings",
_build_book_regular_expression(
_KINGS_REGULAR_EXPRESSION,
prefix=_FIRST_BOOK,
prefix=FIRST_BOOK,
suffix=r"\,\s+Commonly\s+Called\s+the\s+Third\s+Book\s+of\s+the\s+Kings",
),
("Kgs", "Ki", "Kin"),
Expand All @@ -141,7 +140,7 @@ def abbreviations(self: Book) -> tuple[str, ...]:
"2 Kings",
_build_book_regular_expression(
_KINGS_REGULAR_EXPRESSION,
prefix=_SECOND_BOOK,
prefix=SECOND_BOOK,
suffix=r"\,\s+Commonly\s+Called\s+the\s+Fourth\s+Book\s+of\s+the\s+Kings",
),
("Kgs", "Ki", "Kin"),
Expand All @@ -151,7 +150,7 @@ def abbreviations(self: Book) -> tuple[str, ...]:
"1 Chronicles",
_build_book_regular_expression(
_CHRONICLES_REGULAR_EXPRESSION,
prefix=_FIRST_BOOK,
prefix=FIRST_BOOK,
),
("Ch", "Chr", "Chro", "Chron"),
)
Expand All @@ -160,7 +159,7 @@ def abbreviations(self: Book) -> tuple[str, ...]:
"2 Chronicles",
_build_book_regular_expression(
_CHRONICLES_REGULAR_EXPRESSION,
prefix=_SECOND_BOOK,
prefix=SECOND_BOOK,
),
("Ch", "Chr", "Chro", "Chron"),
)
Expand Down Expand Up @@ -229,7 +228,7 @@ def abbreviations(self: Book) -> tuple[str, ...]:
JOHN = (
43,
"John",
rf"(?<!(?:1|2|3|I)\s)(?<!(?:1|2|3|I)){_JOHN_REGULAR_EXPRESSION}",
rf"(?<!(?:1|2|3|I)\s)(?<!(?:1|2|3|I)){JOHN_REGULAR_EXPRESSION}",
("Jhn", "Jn", "Jo", "Joh"),
)
ACTS = (
Expand Down Expand Up @@ -336,7 +335,7 @@ def abbreviations(self: Book) -> tuple[str, ...]:
62,
"1 John",
_build_book_regular_expression(
_JOHN_REGULAR_EXPRESSION,
JOHN_REGULAR_EXPRESSION,
prefix=_FIRST_GENERAL_EPISTLE,
),
("Jhn", "Jn", "Jo", "Joh"),
Expand All @@ -345,7 +344,7 @@ def abbreviations(self: Book) -> tuple[str, ...]:
63,
"2 John",
_build_book_regular_expression(
_JOHN_REGULAR_EXPRESSION,
JOHN_REGULAR_EXPRESSION,
prefix=_SECOND_GENERAL_EPISTLE,
),
("Jhn", "Jn", "Jo", "Joh"),
Expand All @@ -354,7 +353,7 @@ def abbreviations(self: Book) -> tuple[str, ...]:
64,
"3 John",
_build_book_regular_expression(
_JOHN_REGULAR_EXPRESSION,
JOHN_REGULAR_EXPRESSION,
prefix=_THIRD_GENERAL_EPISTLE,
),
("Jhn", "Jn", "Jo", "Joh"),
Expand All @@ -374,7 +373,7 @@ def abbreviations(self: Book) -> tuple[str, ...]:
"1 Esdras",
_build_book_regular_expression(
r"(Esdras|Esdr\.*|Esd\.*|Es\.*)",
_FIRST,
FIRST,
),
("Es", "Esd", "Esdr"),
)
Expand All @@ -396,7 +395,7 @@ def abbreviations(self: Book) -> tuple[str, ...]:
"1 Maccabees",
_build_book_regular_expression(
_MACCABEES_REGULAR_EXPRESSION,
_FIRST,
FIRST,
),
("M", "Ma", "Mac", "Macc"),
)
Expand All @@ -405,7 +404,7 @@ def abbreviations(self: Book) -> tuple[str, ...]:
"2 Maccabees",
_build_book_regular_expression(
_MACCABEES_REGULAR_EXPRESSION,
_SECOND,
SECOND,
),
("M", "Ma", "Mac", "Macc"),
)
8 changes: 8 additions & 0 deletions pythonbible/pythonbible/books/common_constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"""Common constants used in Book regular expressions."""

FIRST = r"1|I\s+|1st\s+|First\s+"
SECOND = r"2|II|2nd\s+|Second\s+"
THIRD = r"3|III|3rd\s+|Third\s+"

FIRST_BOOK = rf"{FIRST}|(First\s+Book\s+of(?:\s+the)?)"
SECOND_BOOK = rf"{SECOND}|(Second\s+Book\s+of(?:\s+the)?)"
23 changes: 23 additions & 0 deletions pythonbible/pythonbible/books/john.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""John book regular expression definitions."""

import re

_JO_EXCLUDE_SUFFIXES: tuple[str, ...] = (
"shua", # Joshua
"b", # Job
"nah", # Jonah
"n", # Jonah (abbreviation)
"el", # Joel
)

_JO_EXCLUDED_SUFFIXES_PATTERN = "|".join(map(re.escape, _JO_EXCLUDE_SUFFIXES))
_JO_NEGATIVE_LOOKAHEAD = f"(?!{_JO_EXCLUDED_SUFFIXES_PATTERN})"

_ABBREVIATIONS: tuple[str, ...] = (
r"Joh\.*",
r"Jhn\.*",
r"Jo\.*" + _JO_NEGATIVE_LOOKAHEAD,
r"Jn\.*",
)

JOHN_REGULAR_EXPRESSION = rf"(John|{'|'.join(_ABBREVIATIONS)})"
68 changes: 68 additions & 0 deletions pythonbible/tests/parser/parser_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

import pytest

import pythonbible as bible


Expand Down Expand Up @@ -334,3 +336,69 @@ def test_book_abbreviations() -> None:

if book != book.SONG_OF_SONGS:
assert expected == actual_period


@pytest.mark.parametrize(
("input_reference", "expected_output"),
[
(
"micah-jon",
bible.NormalizedReference(
book=bible.Book.MICAH,
start_chapter=None,
start_verse=None,
end_chapter=None,
end_verse=None,
end_book=bible.Book.JONAH,
),
),
(
"jon-jon",
bible.NormalizedReference(
book=bible.Book.JONAH,
start_chapter=None,
start_verse=None,
end_chapter=None,
end_verse=None,
end_book=bible.Book.JONAH,
),
),
(
"obadiah-jon",
bible.NormalizedReference(
book=bible.Book.OBADIAH,
start_chapter=None,
start_verse=None,
end_chapter=None,
end_verse=None,
end_book=bible.Book.JONAH,
),
),
(
"jon-john",
bible.NormalizedReference(
book=bible.Book.JONAH,
start_chapter=None,
start_verse=None,
end_chapter=None,
end_verse=None,
end_book=bible.Book.JOHN,
),
),
],
)
def test_issue_235_jonah_abbreviation(
input_reference: str,
expected_output: bible.NormalizedReference,
) -> None:
"""Test for Issue 235 "Error getting reference to Jonah".

:param input_reference: The input reference string to test.
:param expected_output: The expected normalized reference output.
"""
# Given a text string with an abbreviation for the book of Jonah
# When we parse the references from that text
references: list[bible.NormalizedReference] = bible.get_references(input_reference)

# Then the parser returns the appropriate normalized reference
assert references == [expected_output]
2 changes: 1 addition & 1 deletion pythonbible/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading