epub-blocks 0.1.0__tar.gz
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.
- epub_blocks-0.1.0/CHANGELOG.md +11 -0
- epub_blocks-0.1.0/CONTRIBUTING.md +36 -0
- epub_blocks-0.1.0/LICENSE +21 -0
- epub_blocks-0.1.0/MANIFEST.in +6 -0
- epub_blocks-0.1.0/PKG-INFO +189 -0
- epub_blocks-0.1.0/README.md +167 -0
- epub_blocks-0.1.0/docs/recipe-format.md +722 -0
- epub_blocks-0.1.0/docs/releasing.md +22 -0
- epub_blocks-0.1.0/pyproject.toml +66 -0
- epub_blocks-0.1.0/schemas/recipe-v1.schema.json +95 -0
- epub_blocks-0.1.0/setup.cfg +4 -0
- epub_blocks-0.1.0/src/epub_blocks/__init__.py +35 -0
- epub_blocks-0.1.0/src/epub_blocks/cli.py +35 -0
- epub_blocks-0.1.0/src/epub_blocks/errors.py +3 -0
- epub_blocks-0.1.0/src/epub_blocks/extract.py +128 -0
- epub_blocks-0.1.0/src/epub_blocks/models.py +74 -0
- epub_blocks-0.1.0/src/epub_blocks/package.py +188 -0
- epub_blocks-0.1.0/src/epub_blocks/py.typed +0 -0
- epub_blocks-0.1.0/src/epub_blocks/recipe.py +423 -0
- epub_blocks-0.1.0/src/epub_blocks/safety.py +132 -0
- epub_blocks-0.1.0/src/epub_blocks/xhtml.py +190 -0
- epub_blocks-0.1.0/src/epub_blocks/xml.py +54 -0
- epub_blocks-0.1.0/src/epub_blocks.egg-info/PKG-INFO +189 -0
- epub_blocks-0.1.0/src/epub_blocks.egg-info/SOURCES.txt +26 -0
- epub_blocks-0.1.0/src/epub_blocks.egg-info/dependency_links.txt +1 -0
- epub_blocks-0.1.0/src/epub_blocks.egg-info/entry_points.txt +2 -0
- epub_blocks-0.1.0/src/epub_blocks.egg-info/top_level.txt +1 -0
- epub_blocks-0.1.0/tests/test_epub_blocks.py +836 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented here. The project uses
|
|
4
|
+
[Semantic Versioning](https://semver.org/).
|
|
5
|
+
|
|
6
|
+
## Unreleased
|
|
7
|
+
|
|
8
|
+
## 0.1.0 - 2026-09-04
|
|
9
|
+
|
|
10
|
+
- Initial public release of EPUB inspection, block discovery, versioned
|
|
11
|
+
extraction recipes, TSV output, safety limits, and validation.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
`epub-blocks` requires Python 3.13 or later and uses
|
|
4
|
+
[uv](https://docs.astral.sh/uv/) for its development environment.
|
|
5
|
+
|
|
6
|
+
## Set up the project
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
git clone https://github.com/jtauber/epub-blocks.git
|
|
10
|
+
cd epub-blocks
|
|
11
|
+
uv sync
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Check a change
|
|
15
|
+
|
|
16
|
+
Run the same checks used by continuous integration:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uv run ruff check .
|
|
20
|
+
uv run pyright
|
|
21
|
+
uv run coverage erase
|
|
22
|
+
uv run coverage run -m unittest discover -s tests
|
|
23
|
+
uv run coverage report
|
|
24
|
+
uv run python -m build
|
|
25
|
+
uv run twine check dist/*
|
|
26
|
+
uv run pyright --verifytypes epub_blocks --ignoreexternal
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
New behavior and bug fixes should include tests. Tests create conforming EPUB
|
|
30
|
+
fixtures in temporary directories; do not commit copyrighted EPUB files.
|
|
31
|
+
|
|
32
|
+
Please open an issue before proposing an incompatible recipe-format or public
|
|
33
|
+
API change. Recipe version 1 readers intentionally reject unknown fields, so
|
|
34
|
+
an incompatible semantic change needs a new recipe version.
|
|
35
|
+
|
|
36
|
+
See the [release checklist](docs/releasing.md) for the packaging process.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 James Tauber
|
|
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,189 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: epub-blocks
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Recipe-driven extraction of structured text blocks from EPUB files
|
|
5
|
+
Author: James Tauber
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Documentation, https://github.com/jtauber/epub-blocks#readme
|
|
8
|
+
Project-URL: Issues, https://github.com/jtauber/epub-blocks/issues
|
|
9
|
+
Project-URL: Repository, https://github.com/jtauber/epub-blocks
|
|
10
|
+
Keywords: epub,text extraction,digital humanities
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Classifier: Topic :: Text Processing
|
|
18
|
+
Requires-Python: >=3.13
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
|
|
23
|
+
# epub-blocks
|
|
24
|
+
|
|
25
|
+
`epub-blocks` is a small, dependency-free Python library for recipe-driven
|
|
26
|
+
extraction of ordered text blocks from EPUB 2 and EPUB 3 containers. It
|
|
27
|
+
also exposes locator-bearing source blocks so that consuming projects can
|
|
28
|
+
prepare and review recipes reproducibly.
|
|
29
|
+
|
|
30
|
+
It requires Python 3.13 or later.
|
|
31
|
+
|
|
32
|
+
This project is at an early 0.x stage. Its public API and recipe format may
|
|
33
|
+
evolve before 1.0.
|
|
34
|
+
|
|
35
|
+
## Installation
|
|
36
|
+
|
|
37
|
+
Install the 0.1.0 release directly from GitHub:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
python -m pip install "git+https://github.com/jtauber/epub-blocks.git@v0.1.0"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
For development, install from a local checkout:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
python -m pip install .
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
The runtime package has no third-party dependencies.
|
|
50
|
+
|
|
51
|
+
## Scope
|
|
52
|
+
|
|
53
|
+
The package handles the reusable EPUB layer:
|
|
54
|
+
|
|
55
|
+
- locating and parsing the EPUB package document;
|
|
56
|
+
- reading identifiers and the XHTML spine;
|
|
57
|
+
- excluding auxiliary `linear="no"` spine items by default;
|
|
58
|
+
- selecting spine documents with case-insensitive glob patterns;
|
|
59
|
+
- extracting paragraph, heading, quotation, list-item, and preformatted text
|
|
60
|
+
blocks with stable source locators;
|
|
61
|
+
- excluding document paths, element locators, CSS classes, page-breaks, and
|
|
62
|
+
note references; and
|
|
63
|
+
- selecting, omitting, slicing, joining, and normalizing XHTML fragments;
|
|
64
|
+
- applying explicit extraction recipes to produce `id`, `type`, and `text`
|
|
65
|
+
records; and
|
|
66
|
+
- writing those records as headerless TSV files.
|
|
67
|
+
|
|
68
|
+
It deliberately leaves the interpretation and downstream use of extracted
|
|
69
|
+
records to consuming projects.
|
|
70
|
+
|
|
71
|
+
## Example
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from pathlib import Path
|
|
75
|
+
|
|
76
|
+
from epub_blocks import extract_blocks
|
|
77
|
+
|
|
78
|
+
blocks = extract_blocks(
|
|
79
|
+
Path("book.epub"),
|
|
80
|
+
include_documents=["*chapter*.xhtml"],
|
|
81
|
+
exclude_classes=["image-caption"],
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
for block in blocks:
|
|
85
|
+
print(block.source_locator, block.tag, block.text)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`source_locator` has the form
|
|
89
|
+
`s008:text/chapter-01.xhtml#1.3.2`. Element-path components are one-based child
|
|
90
|
+
positions within the XHTML `body`.
|
|
91
|
+
|
|
92
|
+
## Extraction recipes
|
|
93
|
+
|
|
94
|
+
An extraction recipe pins the EPUB identity and explicitly maps output blocks
|
|
95
|
+
to XHTML fragments:
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"recipe_version": "1",
|
|
100
|
+
"epub": {
|
|
101
|
+
"identifier": "9780000000000",
|
|
102
|
+
"sha256": "f4f9c2d902a41b80732b2dce7ad01a57f615859c21417a5019e3cd8e4d271282"
|
|
103
|
+
},
|
|
104
|
+
"normalization": {
|
|
105
|
+
"collapse_whitespace": true,
|
|
106
|
+
"strip": true,
|
|
107
|
+
"unicode_normalization": "NFC"
|
|
108
|
+
},
|
|
109
|
+
"omit_epub_types": ["noteref", "pagebreak"],
|
|
110
|
+
"blocks": [
|
|
111
|
+
{
|
|
112
|
+
"id": "01.001",
|
|
113
|
+
"type": "{p}",
|
|
114
|
+
"parts": [
|
|
115
|
+
{
|
|
116
|
+
"document": "text/chapter-01.xhtml",
|
|
117
|
+
"element_path": "1.3.2"
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
The command-line interface writes a three-column, headerless TSV containing
|
|
126
|
+
the identifier, type, and text of each extracted record:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
epub-blocks book.epub recipe.json records.tsv
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Identifiers and types are opaque strings chosen by the recipe. See the
|
|
133
|
+
[complete recipe format](https://github.com/jtauber/epub-blocks/blob/main/docs/recipe-format.md)
|
|
134
|
+
for the full field reference, locator and slicing semantics, normalization
|
|
135
|
+
order, validation rules, and worked examples. A strict
|
|
136
|
+
[JSON Schema](https://github.com/jtauber/epub-blocks/blob/main/schemas/recipe-v1.schema.json)
|
|
137
|
+
is also available for editor and pipeline integration.
|
|
138
|
+
|
|
139
|
+
## Safety and trust model
|
|
140
|
+
|
|
141
|
+
EPUB files are untrusted ZIP and XML input. The default extraction APIs bound
|
|
142
|
+
archive membership, individual and cumulative reads, compression ratios, XML
|
|
143
|
+
document size, element count, and nesting depth. Duplicate or unsafe archive
|
|
144
|
+
paths, encrypted members, external or internal DTDs, and entity declarations
|
|
145
|
+
are rejected. The declaration-only HTML5 `<!DOCTYPE html>` is permitted.
|
|
146
|
+
Custom positive limits can be supplied with `SafetyLimits` when a legitimate
|
|
147
|
+
book is larger than a default.
|
|
148
|
+
|
|
149
|
+
These checks reduce resource-exhaustion and ambiguity risks; they are not a
|
|
150
|
+
sandbox for arbitrary code. The package does not execute EPUB scripts or fetch
|
|
151
|
+
network resources.
|
|
152
|
+
|
|
153
|
+
## Public API
|
|
154
|
+
|
|
155
|
+
The supported import surface is the names exported by `epub_blocks`: the
|
|
156
|
+
result and option data classes, `SafetyLimits`, `inspect_epub`,
|
|
157
|
+
`extract_blocks`, the recipe-loading and extraction functions, `write_tsv`,
|
|
158
|
+
and `EpubBlocksError`. Helpers in submodules are implementation details and
|
|
159
|
+
may change during the pre-1.0 period.
|
|
160
|
+
|
|
161
|
+
## Development
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
uv sync
|
|
165
|
+
uv run coverage run -m unittest discover -s tests
|
|
166
|
+
uv run coverage report
|
|
167
|
+
uv run ruff check .
|
|
168
|
+
uv run pyright
|
|
169
|
+
uv run python -m build
|
|
170
|
+
uv run twine check dist/*
|
|
171
|
+
uv run pyright --verifytypes epub_blocks --ignoreexternal
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Coverage includes branch measurement and enforces a 90% minimum. Run
|
|
175
|
+
`uv run coverage html` after the test command for a browsable report in
|
|
176
|
+
`htmlcov/`.
|
|
177
|
+
|
|
178
|
+
No EPUB files are committed to this repository. Tests construct small synthetic
|
|
179
|
+
EPUB containers in temporary directories.
|
|
180
|
+
|
|
181
|
+
See [CONTRIBUTING.md](https://github.com/jtauber/epub-blocks/blob/main/CONTRIBUTING.md)
|
|
182
|
+
for the complete check sequence and change policy, and
|
|
183
|
+
[CHANGELOG.md](https://github.com/jtauber/epub-blocks/blob/main/CHANGELOG.md)
|
|
184
|
+
for release notes. Bugs and proposals are tracked in the
|
|
185
|
+
[issue tracker](https://github.com/jtauber/epub-blocks/issues).
|
|
186
|
+
|
|
187
|
+
## License
|
|
188
|
+
|
|
189
|
+
`epub-blocks` is available under the [MIT License](LICENSE).
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# epub-blocks
|
|
2
|
+
|
|
3
|
+
`epub-blocks` is a small, dependency-free Python library for recipe-driven
|
|
4
|
+
extraction of ordered text blocks from EPUB 2 and EPUB 3 containers. It
|
|
5
|
+
also exposes locator-bearing source blocks so that consuming projects can
|
|
6
|
+
prepare and review recipes reproducibly.
|
|
7
|
+
|
|
8
|
+
It requires Python 3.13 or later.
|
|
9
|
+
|
|
10
|
+
This project is at an early 0.x stage. Its public API and recipe format may
|
|
11
|
+
evolve before 1.0.
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Install the 0.1.0 release directly from GitHub:
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
python -m pip install "git+https://github.com/jtauber/epub-blocks.git@v0.1.0"
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
For development, install from a local checkout:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
python -m pip install .
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The runtime package has no third-party dependencies.
|
|
28
|
+
|
|
29
|
+
## Scope
|
|
30
|
+
|
|
31
|
+
The package handles the reusable EPUB layer:
|
|
32
|
+
|
|
33
|
+
- locating and parsing the EPUB package document;
|
|
34
|
+
- reading identifiers and the XHTML spine;
|
|
35
|
+
- excluding auxiliary `linear="no"` spine items by default;
|
|
36
|
+
- selecting spine documents with case-insensitive glob patterns;
|
|
37
|
+
- extracting paragraph, heading, quotation, list-item, and preformatted text
|
|
38
|
+
blocks with stable source locators;
|
|
39
|
+
- excluding document paths, element locators, CSS classes, page-breaks, and
|
|
40
|
+
note references; and
|
|
41
|
+
- selecting, omitting, slicing, joining, and normalizing XHTML fragments;
|
|
42
|
+
- applying explicit extraction recipes to produce `id`, `type`, and `text`
|
|
43
|
+
records; and
|
|
44
|
+
- writing those records as headerless TSV files.
|
|
45
|
+
|
|
46
|
+
It deliberately leaves the interpretation and downstream use of extracted
|
|
47
|
+
records to consuming projects.
|
|
48
|
+
|
|
49
|
+
## Example
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from pathlib import Path
|
|
53
|
+
|
|
54
|
+
from epub_blocks import extract_blocks
|
|
55
|
+
|
|
56
|
+
blocks = extract_blocks(
|
|
57
|
+
Path("book.epub"),
|
|
58
|
+
include_documents=["*chapter*.xhtml"],
|
|
59
|
+
exclude_classes=["image-caption"],
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
for block in blocks:
|
|
63
|
+
print(block.source_locator, block.tag, block.text)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
`source_locator` has the form
|
|
67
|
+
`s008:text/chapter-01.xhtml#1.3.2`. Element-path components are one-based child
|
|
68
|
+
positions within the XHTML `body`.
|
|
69
|
+
|
|
70
|
+
## Extraction recipes
|
|
71
|
+
|
|
72
|
+
An extraction recipe pins the EPUB identity and explicitly maps output blocks
|
|
73
|
+
to XHTML fragments:
|
|
74
|
+
|
|
75
|
+
```json
|
|
76
|
+
{
|
|
77
|
+
"recipe_version": "1",
|
|
78
|
+
"epub": {
|
|
79
|
+
"identifier": "9780000000000",
|
|
80
|
+
"sha256": "f4f9c2d902a41b80732b2dce7ad01a57f615859c21417a5019e3cd8e4d271282"
|
|
81
|
+
},
|
|
82
|
+
"normalization": {
|
|
83
|
+
"collapse_whitespace": true,
|
|
84
|
+
"strip": true,
|
|
85
|
+
"unicode_normalization": "NFC"
|
|
86
|
+
},
|
|
87
|
+
"omit_epub_types": ["noteref", "pagebreak"],
|
|
88
|
+
"blocks": [
|
|
89
|
+
{
|
|
90
|
+
"id": "01.001",
|
|
91
|
+
"type": "{p}",
|
|
92
|
+
"parts": [
|
|
93
|
+
{
|
|
94
|
+
"document": "text/chapter-01.xhtml",
|
|
95
|
+
"element_path": "1.3.2"
|
|
96
|
+
}
|
|
97
|
+
]
|
|
98
|
+
}
|
|
99
|
+
]
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
The command-line interface writes a three-column, headerless TSV containing
|
|
104
|
+
the identifier, type, and text of each extracted record:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
epub-blocks book.epub recipe.json records.tsv
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Identifiers and types are opaque strings chosen by the recipe. See the
|
|
111
|
+
[complete recipe format](https://github.com/jtauber/epub-blocks/blob/main/docs/recipe-format.md)
|
|
112
|
+
for the full field reference, locator and slicing semantics, normalization
|
|
113
|
+
order, validation rules, and worked examples. A strict
|
|
114
|
+
[JSON Schema](https://github.com/jtauber/epub-blocks/blob/main/schemas/recipe-v1.schema.json)
|
|
115
|
+
is also available for editor and pipeline integration.
|
|
116
|
+
|
|
117
|
+
## Safety and trust model
|
|
118
|
+
|
|
119
|
+
EPUB files are untrusted ZIP and XML input. The default extraction APIs bound
|
|
120
|
+
archive membership, individual and cumulative reads, compression ratios, XML
|
|
121
|
+
document size, element count, and nesting depth. Duplicate or unsafe archive
|
|
122
|
+
paths, encrypted members, external or internal DTDs, and entity declarations
|
|
123
|
+
are rejected. The declaration-only HTML5 `<!DOCTYPE html>` is permitted.
|
|
124
|
+
Custom positive limits can be supplied with `SafetyLimits` when a legitimate
|
|
125
|
+
book is larger than a default.
|
|
126
|
+
|
|
127
|
+
These checks reduce resource-exhaustion and ambiguity risks; they are not a
|
|
128
|
+
sandbox for arbitrary code. The package does not execute EPUB scripts or fetch
|
|
129
|
+
network resources.
|
|
130
|
+
|
|
131
|
+
## Public API
|
|
132
|
+
|
|
133
|
+
The supported import surface is the names exported by `epub_blocks`: the
|
|
134
|
+
result and option data classes, `SafetyLimits`, `inspect_epub`,
|
|
135
|
+
`extract_blocks`, the recipe-loading and extraction functions, `write_tsv`,
|
|
136
|
+
and `EpubBlocksError`. Helpers in submodules are implementation details and
|
|
137
|
+
may change during the pre-1.0 period.
|
|
138
|
+
|
|
139
|
+
## Development
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
uv sync
|
|
143
|
+
uv run coverage run -m unittest discover -s tests
|
|
144
|
+
uv run coverage report
|
|
145
|
+
uv run ruff check .
|
|
146
|
+
uv run pyright
|
|
147
|
+
uv run python -m build
|
|
148
|
+
uv run twine check dist/*
|
|
149
|
+
uv run pyright --verifytypes epub_blocks --ignoreexternal
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Coverage includes branch measurement and enforces a 90% minimum. Run
|
|
153
|
+
`uv run coverage html` after the test command for a browsable report in
|
|
154
|
+
`htmlcov/`.
|
|
155
|
+
|
|
156
|
+
No EPUB files are committed to this repository. Tests construct small synthetic
|
|
157
|
+
EPUB containers in temporary directories.
|
|
158
|
+
|
|
159
|
+
See [CONTRIBUTING.md](https://github.com/jtauber/epub-blocks/blob/main/CONTRIBUTING.md)
|
|
160
|
+
for the complete check sequence and change policy, and
|
|
161
|
+
[CHANGELOG.md](https://github.com/jtauber/epub-blocks/blob/main/CHANGELOG.md)
|
|
162
|
+
for release notes. Bugs and proposals are tracked in the
|
|
163
|
+
[issue tracker](https://github.com/jtauber/epub-blocks/issues).
|
|
164
|
+
|
|
165
|
+
## License
|
|
166
|
+
|
|
167
|
+
`epub-blocks` is available under the [MIT License](LICENSE).
|