bibleref 0.16.0
A package for handling references to Bible books, verses and verse-ranges.
Requires Python: >=3.7

# bibleref

## Overview

**bibleref is a Python package for handling references to Bible books, verses and verse-ranges, including string
parsing and conversion.** Its only dependency is the [Lark](https://github.com/lark-parser/lark) parsing toolkit.

`bibleref` defines the following primary classes:
  - `bibleref.ref.BibleBook`:      An Enum of books in the Bible, with extra methods.
  - `bibleref.ref.BibleVerse`:     A reference to a single Bible verse (e.g. Matt 2:3)
  - `bibleref.ref.BibleRange`:     A reference to a continuous range of Bible verses (e.g. Matt 2:3-4:5)
  - `bibleref.ref.BibleRangeList`: A specialised list of `BibleRange` elements, allowing for grouping and
  set-style operations.

(There is no `BibleChapter` class, as chapters are usually best handled as a `BibleRange`.)

For convenience these classes can be directly imported from `bibleref`. They can each convert to and from strings.
`BibleRange` and `BibleRangeList` implement common set operations (such as union, intersection, difference and 
symmetric difference).

## Docs

See [multiscript.app/bibleref](https://multiscript.app/bibleref)

## Examples

```python
>>> import bibleref
>>> from bibleref import *
>>>                             # Parse from string
>>> range_list = BibleRangeList("Mark 2-3:6; 4; 6:1-6, 30-44, 56; Luke 2")
>>> print(range_list)           # Convert back to a string
Mark 2-3:6; 4; 6:1-6, 30-44, 56; Luke 2
>>> len(range_list)
6
>>> range_list[0]               # Individual ranges from a list
BibleRange(Mark 2-3:6)
>>> range_list[1]
BibleRange(Mark 4)
>>> range_list[5].start         # Start and...
BibleVerse(Luke 2:1)
>>> range_list[5].end           # ...end verses of a range.
BibleVerse(Luke 2:52)
>>> range_list[5].end.book      # Verse attributes
<BibleBook.Luke: 'Luke'>
>>> range_list[5].end.chap_num
2
>>> range_list[5].end.verse_num
52
>>> len(range_list.groups)
4
>>> range_list.groups[2]        # Indivdual range groups from a list
GroupView([BibleRange(Mark 6:1-6), BibleRange(Mark 6:30-44), BibleRange(Mark 6:56)])
>>> range_list.groups[2][0]     # Individual ranges within the group
BibleRange(Mark 6:1-6)
>>> range_list.groups[2][1]        
BibleRange(Mark 6:30-44)
>>> range_list.groups[2][2]
BibleRange(Mark 6:56)
>>> BibleVerse('Mark 2:23') + 10 # Verse addition / subtraction
BibleVerse(Mark 3:5)
>>> BibleVerse('Mark 3:5') - BibleVerse('Mark 2:23')
10
>>> BibleRange('1 John').split(by_chap=True, num_verses=15) # Range splits
BibleRangeList("1 John 1, 2:1-15, 16-29, 3:1-15, 16-24, 4:1-15, 16-21, 5:1-15, 16-21")
>>> for verse in BibleRange('Mark 6:1-3'): # Range iteration
...     print(verse)
... 
Mark 6:1
Mark 6:2
Mark 6:3
>>> bibleref.flags = BibleFlag.MULTIBOOK   # Enable multi-book ranges
>>> BibleRange('Matt 10-John 10')
BibleRange(Matthew 10-John 10)
>>> list_1 = BibleRangeList("Matt 2-4; Mark 6-8; Luke 10-12; John 14-16")
>>> list_2 = BibleRangeList("John 1-3; Luke 9; Matt 3-5; Mark 12")
>>> list_1 | list_2                     # Union
BibleRangeList("Matthew 2-5; Mark 6-8; 12; Luke 9-12; John 1-3; 14-16")
>>> list_1 & list_2                     # Intersection
BibleRangeList("Matthew 3-4")
>>> list_1 - list_2                     # Difference
BibleRangeList("Matthew 2; Mark 6-8; Luke 10-12; John 14-16")
>>> list_1 ^ list_2                     # Symmetric difference
BibleRangeList("Matthew 2; 5; Mark 6-8; 12; Luke 9-12; John 1-3; 14-16")
>>> range_list = BibleRangeList("Mark 3:2-4:5; 1 John 1:5-3 John 8;")
>>> range_list.verse_count()            # Count of verses
161
>>> range_list.chap_count()             # Count of chapters (incl partial)
9
>>> range_list.chap_count(whole=True)   # Count of chapters (whole only)
5
>>> range_list.book_count()             # Count of books (incl partial)
4
>>> range_list.book_count(whole=True)   # Count of books (whole only)
1
```

## List Grouping

Bible ranges in a list can be separated by two different characters, known here as the *major list separator*
('`;`' by default), and the *minor list separator* ('`,`' by default). These separators play two roles: distinguishing
between 'bare' chapter and verse numbers (i.e. those not preceded by book names), and controlling how Bible ranges are
grouped within a list.

The major list separator (`;`) indicates any bare number that follows is a chapter number. It also marks the start of
a new group. It is usually used between ranges in different chapters.

The minor list separator (`,`) indicates any bare number that follows is of the same kind as the previous number
(whether a chapter or verse number). It also marks the continuation of the same group. It is usually used between
ranges within the same chapter.

The groups of a `BibleRangeList` are accessed through its `groups` property. Alternative, you can index each
`BibleRange` directly (e.g. `range_list[1]`), ignoring the groupings.

For example:
```python
>>> from bibleref import *
>>> range_list = BibleRangeList("Matt 2:3-4, 5-7, 9-12") # One group of three verse ranges
>>> len(range_list.groups)
1
>>> range_list[0]
BibleRange(Matthew 2:3-4)
>>> range_list[1]
BibleRange(Matthew 2:5-7)
>>> range_list[2]
BibleRange(Matthew 2:9-12)
>>> range_list = BibleRangeList("Matt 2:3-4; 5-7, 9-12") # Two groups: one verse range, two chapter ranges
>>> len(range_list.groups)
2
>>> range_list[0]             # Range access directly
BibleRange(Matthew 2:3-4)
>>> range_list.groups[0][0]   # Same range accessed through its group
BibleRange(Matthew 2:3-4)
>>> range_list.groups[1]      # Next group
GroupView([BibleRange(Matthew 5-7), BibleRange(Matthew 9-12)])
>>> range_list.groups[1][0]
BibleRange(Matthew 5-7)
>>> range_list.groups[1][1]   
BibleRange(Matthew 9-12)
>>> range_list[2]             # Same range as previous line, but accessed directly
BibleRange(Matthew 9-12)
>>> range_list = BibleRangeList("Matt 2:3-4, Matt 5-7, 9-12") # One group: one verse range, two chapter ranges
>>> len(range_list.groups)
1
>>> range_list[1]
BibleRange(Matthew 5-7)
>>> range_list[2]
BibleRange(Matthew 9-12)
```

`bibleref.ref.BibleRangeList.regroup()` removes the existing groups in the list, and places the list items into
their most natural new groupings.

The major and minor list separator characters can be changed through the `bibleref.data.BibleData` singleton returned
by `bible_data()`.

## Attribution

The set operations and linked-list implementation in this package are derived from
[python-ranges](https://github.com/Superbird11/ranges), under the MIT Licence.

Other ideas in this package were developed from [python-scriptures](https://github.com/davisd/python-scriptures),
under the BSD-3-Clause license.

## Installation

   `pip install bibleref`

## Build Instructions

Use these instructions if you’re building from the source. bibleref has been developed on Python 3.10, but should
work on several earlier versions as well.

1. `git clone https://github.com/multiscript/bibleref/`
1. `cd bibleref`
1. `python3 -m venv venv` (Create a virtual environment.)
   - On Windows: `python -m venv venv`
1. `source venv/bin/activate` (Activate the virtual environment.)
   - In Windows cmd.exe: `venv\Scripts\\activate.bat`
   - In Windows powershell: `.\\venv\Scripts\Activate.ps1` You may first need to run `Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser`
1. For development work...
   - `pip install -e .` (Creates an editable local install)
1. ...or to build the package:
   - `pip install build`
   - `python -m build`