Development¶
Quick start¶
git clone https://github.com/prajwalmahajan101/BookReader
cd BookReader
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
pre-commit install
The full local gate (CI runs the same thing)¶
All three must be green before pushing.
Project layout¶
src/bookreader/
core/ # config, paths, logging, exceptions (no I/O)
epub/ # parsing + rendering (no UI, no DB)
state/ # JSON position + bookmark fallback
library/ # SQLite library: db, repo, service, migrations
ui/ # Textual app, screens, widgets, themes
tests/
unit/ # parsers, renderer, config
integration/ # service + screen pilot tests
docs/
adr/ # architecture decision records
site/ # mkdocs-material source for this site
screenshots/ # README/PyPI screenshots
scripts/
capture_screenshots.py # regenerates docs/screenshots/*.svg
UI never imports a repository directly — it goes through library/service.
Architectural conventions¶
core/does no I/O.epub/does no UI or DB.library/repositorydoes no business logic.- Errors subclass
BookReaderError; the central handler inui/app.pyroutes them. Don't blanket-catchException. - Module-level logger via
bookreader.core.logging.get_logger(__name__). - Type hints everywhere; mypy strict is the gate.
from __future__ import annotationsat the top of every module.- PEP 257 + Google docstrings.
Branch + commit policy¶
- Never commit to
main— cut afeature/<topic>/fix/<topic>/docs/<topic>branch and fast-forward merge. - Conventional commits:
feat|fix|refactor|docs|test|chore(scope): …. Subject ≤ 72 chars, imperative, no trailing period. - Atomic commits: one logical change each.
- Never
--no-verify. Never amend pushed commits. - No AI attribution footer.
See CONTRIBUTING.md for the full policy.
Regenerating screenshots¶
Writes docs/screenshots/{library,reader,reader-paged}.svg. Re-run
whenever the UI changes meaningfully.
Running the docs site locally¶
Releasing¶
Maintainer only. Tag-driven, fully automated via
.github/workflows/release.yml:
# 1. Bump version in two places
$EDITOR src/bookreader/__init__.py # __version__ = "X.Y.Z"
$EDITOR pyproject.toml # version = "X.Y.Z"
# 2. Add a [X.Y.Z] section to CHANGELOG.md
# 3. Commit, tag, push
git commit -am "chore(release): bump to X.Y.Z"
git tag -a vX.Y.Z -m "Release X.Y.Z"
git push origin main vX.Y.Z
The workflow builds sdist + wheel, verifies the tag matches
pyproject.toml, publishes to PyPI via trusted publishing, and creates a
GitHub Release.