ugarit-texts 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
ugarit/__init__.py
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Browse the locally supplied Ugaritic Baal Cycle extraction."""
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
DEFAULT_SOURCE = Path.home() / "Documents" / "Ugaritic Baal Cycle Volumes I and II.md"
|
|
7
|
+
_HEADING = re.compile(r"^#{1,3} .+$", re.MULTILINE)
|
|
8
|
+
_COLUMN = re.compile(r"^## (?:(?:CAT|KTU) )?(1\.[1-4]) (I{1,3}|IV|V|VI{0,3})(?: and CAT 1\.8)?$", re.MULTILINE)
|
|
9
|
+
_PAGE = re.compile(r"^### PDF page (\d+)\s*\n\s*```text\s*\n(.*?)\n```", re.MULTILINE | re.DOTALL)
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class Page:
|
|
13
|
+
volume: int
|
|
14
|
+
number: int
|
|
15
|
+
text: str
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class Section:
|
|
19
|
+
tablet: str
|
|
20
|
+
column: str
|
|
21
|
+
volume: int
|
|
22
|
+
text: str
|
|
23
|
+
|
|
24
|
+
class Corpus:
|
|
25
|
+
def __init__(self, source=DEFAULT_SOURCE):
|
|
26
|
+
self.source = Path(source)
|
|
27
|
+
self._text = self.source.read_text(encoding="utf-8-sig")
|
|
28
|
+
marker = self._text.find("\n# Volume II\n")
|
|
29
|
+
if marker < 0:
|
|
30
|
+
raise ValueError("Source is missing the Volume II heading")
|
|
31
|
+
self._volumes = {1: self._text[:marker], 2: self._text[marker:]}
|
|
32
|
+
|
|
33
|
+
def tablets(self):
|
|
34
|
+
"""Return tablet identifiers represented by column sections."""
|
|
35
|
+
return tuple(dict.fromkeys(s.tablet for s in self.sections()))
|
|
36
|
+
|
|
37
|
+
def sections(self, tablet=None):
|
|
38
|
+
"""Return sections in source order; each includes commentary and page markers."""
|
|
39
|
+
result = []
|
|
40
|
+
for volume, text in self._volumes.items():
|
|
41
|
+
matches = list(_COLUMN.finditer(text))
|
|
42
|
+
for match in matches:
|
|
43
|
+
end = _HEADING.search(text, match.end())
|
|
44
|
+
# Page headings occur within sections; stop only at the next level-2 heading.
|
|
45
|
+
next_section = re.search(r"^## ", text[match.end():], re.MULTILINE)
|
|
46
|
+
stop = match.end() + next_section.start() if next_section else len(text)
|
|
47
|
+
item = Section(match.group(1), match.group(2), volume, text[match.end():stop].strip())
|
|
48
|
+
if tablet is None or item.tablet == str(tablet).removeprefix("KTU ").removeprefix("CAT "):
|
|
49
|
+
result.append(item)
|
|
50
|
+
return tuple(result)
|
|
51
|
+
|
|
52
|
+
def get(self, tablet, column):
|
|
53
|
+
"""Get a tablet column, e.g. get('1.3', 'IV'); raises KeyError if absent."""
|
|
54
|
+
for section in self.sections(tablet):
|
|
55
|
+
if section.column == str(column).upper():
|
|
56
|
+
return section
|
|
57
|
+
raise KeyError(f"No section for {tablet} column {column}")
|
|
58
|
+
|
|
59
|
+
def pages(self, volume=None):
|
|
60
|
+
"""Return extracted PDF pages, optionally restricted to volume 1 or 2."""
|
|
61
|
+
if volume is not None and volume not in self._volumes:
|
|
62
|
+
raise ValueError("volume must be 1 or 2")
|
|
63
|
+
volumes = self._volumes.items() if volume is None else [(volume, self._volumes[volume])]
|
|
64
|
+
return tuple(Page(v, int(m.group(1)), m.group(2)) for v, text in volumes for m in _PAGE.finditer(text))
|
|
65
|
+
|
|
66
|
+
def page(self, volume, number):
|
|
67
|
+
for page in self.pages(volume):
|
|
68
|
+
if page.number == number:
|
|
69
|
+
return page
|
|
70
|
+
raise KeyError(f"No PDF page {number} in volume {volume}")
|
|
71
|
+
|
|
72
|
+
def search(self, query, volume=None):
|
|
73
|
+
"""Return pages containing a case-insensitive literal query."""
|
|
74
|
+
if not query:
|
|
75
|
+
raise ValueError("query must not be empty")
|
|
76
|
+
needle = query.casefold()
|
|
77
|
+
return tuple(page for page in self.pages(volume) if needle in page.text.casefold())
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ugarit-texts
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Browse a local Markdown extraction of the Ugaritic Baal Cycle
|
|
5
|
+
License: MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2026 ugarit-texts contributors
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
|
|
27
|
+
Keywords: ugaritic,baal cycle,ancient texts
|
|
28
|
+
Classifier: Programming Language :: Python :: 3
|
|
29
|
+
Classifier: Topic :: Text Processing
|
|
30
|
+
Classifier: Topic :: Religion
|
|
31
|
+
Requires-Python: >=3.9
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
License-File: LICENSE
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# ugarit-texts
|
|
37
|
+
|
|
38
|
+
Browse a locally supplied Markdown extraction of *The Ugaritic Baal Cycle*, Volumes I and II, using Python. Look up tablet columns, retrieve PDF pages, and search page text. Python 3.9 or later is required; there are no runtime dependencies.
|
|
39
|
+
|
|
40
|
+
## Install
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
python -m pip install ugarit-texts
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The package contains code only. It does **not** include the books or a text corpus. You must supply the matching Markdown extraction yourself. By default, `Corpus()` looks for `Ugaritic Baal Cycle Volumes I and II.md` in your home directory's `Documents` folder. Alternatively, pass a path:
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
from ugarit import Corpus
|
|
50
|
+
|
|
51
|
+
corpus = Corpus(r"C:\path\to\Ugaritic Baal Cycle Volumes I and II.md")
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The input must have the volume, section, PDF-page and fenced text headings used by that extraction; this is not a general parser for arbitrary Ugaritic editions. A missing file raises `FileNotFoundError`.
|
|
55
|
+
|
|
56
|
+
## Browse tablet columns
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from ugarit import Corpus
|
|
60
|
+
|
|
61
|
+
corpus = Corpus()
|
|
62
|
+
print(corpus.tablets()) # ('1.1', '1.2', '1.3', '1.4')
|
|
63
|
+
section = corpus.get('KTU 1.3', 'IV') # CAT prefix also accepted
|
|
64
|
+
print(section.tablet, section.column, section.volume)
|
|
65
|
+
print(section.text)
|
|
66
|
+
|
|
67
|
+
for section in corpus.sections('1.2'):
|
|
68
|
+
print(section.column, section.volume)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`sections()` returns all indexed sections in source order; pass a tablet identifier to filter them. `get(tablet, column)` returns a `Section` with `tablet`, `column`, `volume`, and `text` attributes, or raises `KeyError`. Roman column numbers are case-insensitive.
|
|
72
|
+
|
|
73
|
+
Sections are taken from the extraction's headings. They include commentary, notes and page markers, and may include material outside the named column. They are not clean verse-level transcriptions or isolated translations.
|
|
74
|
+
|
|
75
|
+
## Retrieve and search pages
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
page = corpus.page(1, 2) # volume 1, PDF page 2
|
|
79
|
+
print(page.text)
|
|
80
|
+
|
|
81
|
+
for page in corpus.search('Baal', volume=2):
|
|
82
|
+
print(page.volume, page.number)
|
|
83
|
+
|
|
84
|
+
all_pages = corpus.pages() # or corpus.pages(volume=1)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Page numbers refer to PDF pages, not printed book pages. Search performs a case-insensitive literal substring match and returns matching `Page` objects with `volume`, `number`, and `text` attributes. An empty query or invalid volume raises `ValueError`; a missing page raises `KeyError`.
|
|
88
|
+
|
|
89
|
+
## Scope and source
|
|
90
|
+
|
|
91
|
+
The source describes Mark S. Smith, *The Ugaritic Baal Cycle*, Volume I (Brill, 1994), covering KTU 1.1–1.2; and Smith and Wayne T. Pitard, Volume II (Brill, 2009), covering KTU/CAT 1.3–1.4. KTU 1.5–1.6 and the wider Ugaritic corpus are not included. The Markdown is a mechanical PDF extraction with OCR errors, disrupted columns, missing images, and section headings placed at page boundaries. Consult the original editions when accuracy matters.
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
The library code is available under the [MIT License](LICENSE). The ancient Ugaritic works are distinct from modern editions, translations and commentaries. The supplied Brill edition is not licensed here and is not distributed with this package; no CC BY license is claimed for it. Obtain and use your source copy under its applicable rights.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
ugarit/__init__.py,sha256=QCVWX7BxzfPQTxL-LJjDedEeHXcxwEC0QCVSAJxhqtc,3402
|
|
2
|
+
ugarit_texts-0.1.0.dist-info/licenses/LICENSE,sha256=wTHwXVpF7vJfXi8wQC49SyoHad8nCtgJtMj1y7lwHLU,1086
|
|
3
|
+
ugarit_texts-0.1.0.dist-info/METADATA,sha256=LKetWl0gjDDXX9nJwAGRCvY-JycbAlZTPerwQGd3ry4,4936
|
|
4
|
+
ugarit_texts-0.1.0.dist-info/WHEEL,sha256=YLJXdYXQ2FQ0Uqn2J-6iEIC-3iOey8lH3xCtvFLkd8Q,91
|
|
5
|
+
ugarit_texts-0.1.0.dist-info/top_level.txt,sha256=J4FRNHuD_wP4Sht_Byw4sJreoTQio8aVU1ASPCxW7gM,7
|
|
6
|
+
ugarit_texts-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ugarit-texts contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ugarit
|