This library powers CalibreQuarry (CLI/TUI), Hermitage (GTK4 gallery), Carrel-calibre-web (web reader), and bindery-cli (EPUB repair & audit). By centralizing the search grammar parser and metadata access, cquarry evaluates virtual library definitions and search queries consistently across frontends.
Features
- Direct SQLite access. No
calibredbbinary required, no Calibre Python initialization overhead. - Coherence on demand. Caches are lazy and cheap, and
CalibreDB.refresh()clears them all in one call, so a connection held open across an external Calibre write never contradicts itself. One boundary: when the connection rides a locked-database snapshot copy, the snapshot is not retaken; reopen for truly current data. - Lock-safe snapshots. Automatically detects if Calibre holds an exclusive write lock on
metadata.dband routes queries through a temporary consistent snapshot (sqlite's backup API;backup_to()gives the same consistency to consumer backups).external_changes_detected()tells a long-lived holder when its caches have gone stale. - Full search grammar parity. A recursive-descent parser implementing Calibre's native search capabilities: boolean logic, field prefixes, date math (hyphen and slash separators), hierarchical tags with
./..component modifiers on every text field, custom columns, identifiers, saved-search interpolation (search:"Name"), multi-valued count operators (tags:#>3), language canonicalization (languages:English→eng), and nested virtual library cross-references. Adversarial grammar-valid queries surface asParseException, never a rawRecursionError; an empty query after any location matches nothing; invalid boolean keywords raise; two-letter language codes canonicalize (languages:jamatchesjpn); super-quotes"""..."""shield quote- and paren-heavy queries from the lexer; and the vocabulary is upstream-strict (dates and numerics take exactlytrue/falseas presence words), all verified against upstream. - Native page counts. The
pages:location reads Calibre's ownbooks_pages_linktable first (maintained by upstream's CountPages integration) and falls back to an int custom column labelledpages; counts also ride along in every book row. - Entity secondary columns & display config. Book rows carry
author_sorts/author_linksparallel toauthors;get_entities(kind)exposes{id, name, sort, link, count}for authors/series/publishers/tags/languages; custom columns reporteditable,normalizedand their decodeddisplayJSON (enum_values,enum_colors, …); and a typed preferences accessor covers everything else (get_preference,get_field_metadata,get_user_categories,get_tag_browser_state). - Metadata portability. Read e-reader annotations, per-device reading progress, third-party plugin data, and conversion profiles; sanitize comments HTML for display.
- Book-content search. Calibre's
full-text-search.dbsidecar is a sanctioned read:get_book_text()returns a format's extracted plain text,search_book_text()runs a folded content search over it, andget_text_extractions()feeds integrity audits (find_failed_text_extractionreports the formats whose extraction failed: scans, DRM, corrupt files). No FTS5 machinery required. - Opt-in write path.
cquarry.write.WritableCalibreDBoffers trigger-safe mutations in a separate module the read-only API can never touch:- Field setters. Title, authors (with
author_sortrecomputation), series (+index), publisher, rating (UNIQUE-deduped, withclear_rating), languages (canonicalized to ISO codes), tags (add/remove/clear_tagsfor all at once), identifiers, comments, andhas_cover. - Custom columns. Generic writes with storage layout auto-detected, enumerations validated against
display.enum_values, non-editable columns refused, plusadd_custom_column_valuesappend semantics for multi-valued columns; schema management (create_custom_column/delete_custom_column, flag-only delete like upstream). - Books and formats. The creation path (
add_book: trigger-filled sort/uuid,Author/Title (id)layout, atomic format/cover placement with truthfuldatarows, a dry-run plan, copy-only), format registration/replacement/removal, full book removal with orphan pruning, and undo-able format repair (save_original_format/restore_original_format). - Whole-library verbs. Entity-wide renames (
rename_entityfixes a misspelled author everywhere, merging case variants), verbatim sort corrections, cover replacement (set_cover/remove_cover), and trash management (list_trash/empty_trash/expire_trash). - Calibre stays truthful. Every row-level mutation queues OPF resync in
metadata_dirtied; renames re-lay the on-disk layout (the book directory and format stems move with the rows); format writes queue FTS re-extraction and pages rescans in the sidecar.
- Field setters. Title, authors (with
- Context manager.
CalibreDBsupportswithstatements for automatic cleanup of snapshot files. - Zero dependencies. Pure Python 3.14+ stdlib (
sqlite3,re,json,unicodedata).
Usage
from cquarry.db import CalibreDB
# Open a library (creates a snapshot if Calibre has the lock)
with CalibreDB("~/Calibre Library/metadata.db") as db:
# Fetch all books with pre-joined metadata
books = db.get_all_books()
# Search using Calibre's native grammar
sci_fi = db.search("tags:Fic.SciFi and rating:>=4")
print(f"Found {len(sci_fi)} highly rated Sci-Fi books.")
# Resolve a virtual library to a set of book IDs
wing = db.resolve_vl("To Read")
# Interpolate a saved search straight from Calibre's preferences
award_winners = db.search('search:"Award Winners"')
# Inspect custom columns (#label, bare label, or display name all work)
cols = db.get_custom_columns()
status = db.load_custom_column("#reading_status")
# Single-entity helpers (no whole-library scan)
book = db.get_book(42)
epub = db.get_format_path(42, "EPUB")
# Metadata portability
highlights = db.get_annotations(42)
progress = db.get_last_read_positions(42)
wordcounts = db.get_plugin_data(name="wordcount")
The composed deep fetch combines metadata, formats, custom columns, and annotations:
dossier = db.get_book_dossier(42, include_comments=True)
print(dossier["formats"], dossier["custom_columns"])
print(dossier["comments"]["plain"]) # comments HTML, already stripped
Writes live behind an explicit opt-in import:
from cquarry.write import WritableCalibreDB
with WritableCalibreDB("~/Calibre Library/metadata.db") as wdb:
wdb.add_tag(42, "Audited")
wdb.set_identifier(42, "isbn", "9780123456789")
wdb.clear_identifier(42, "mobi-asin") # honest no-op when absent
# A multi-book curation pass commits exactly once; any failure (a Ctrl-C
# included) rolls the whole pass back, directories created inside it too:
with WritableCalibreDB("~/Calibre Library/metadata.db") as wdb:
with wdb.batch():
wdb.set_pubdate(42, "1991-10-01")
wdb.add_tag(43, "Audited")
# Every mutation queues an OPF regeneration; check what Calibre will resync:
with CalibreDB("~/Calibre Library/metadata.db") as db:
print(db.get_dirtied_books()) # e.g. [42, 43]
Installation
pip install cquarry
API at a glance
The full per-method reference lives in API.md. One line per module:
| Module | What it is |
|---|---|
cquarry.db |
The read-only database layer (CalibreDB): hydrated rows, single-entity fetches, format/cover resolution, custom columns, preferences, annotations and progress extractors, VL/saved-search resolution, the restricted tag browser (get_categories), and the composed get_book_dossier() deep fetch. |
cquarry.search |
The lexer/parser/evaluator porting Calibre's search grammar; usable standalone behind the MetadataProvider protocol. |
cquarry.helpers |
Domain utilities: rating conversion, comment sanitization, author display (including the unpiped-author and identifier-link helpers), series gaps, image dimension sniffing, the ISBN family (isbn_normalize, isbn_check_digit_is_valid, to_isbn13), and tag_rollup. |
cquarry.integrity |
The shared library-integrity predicates: untagged, unrated, authorless, formatless, coverless, missing cover files, deprecated formats, low-res covers, duplicates, failed text extractions, series gaps, plus the metadata-quality trio (invalid UUIDs, sentinel pubdates, bad language codes) and the extra-side disk walk (check_library_disk). |
cquarry.analytics |
Shared derivations: addition timeline, per-author stats, rating distribution, genre distribution (hierarchical tag shares), virtual library (wing) overlap. |
cquarry.write |
The opt-in mutation path (WritableCalibreDB): trigger-safe setters, batch() transactions, remove_book and the restore-from-trash family, the add_book creation path, typed preference writes (saved searches, virtual libraries, user categories), and the FTS queue/maintenance verbs. Every row mutation queues OPF resync. |
cquarry.config |
Saved database-path configuration (~/.config/cquarry/config.json). |
Development
python -m pytest tests/ # full suite
python -m pytest tests/ -v # verbose
Run with PYTHONPATH=src to exercise this checkout rather than any installed copy.
Nine test modules: test_db.py (CalibreDB against fixture databases), test_helpers.py (utility functions), test_search.py (parser, matcher, and integration tests), test_write.py (opt-in write module with trigger-hazard fixtures), test_integrity.py (library integrity predicates), test_analytics.py (analytics derivations), test_config.py, test_coverage_fills.py, and test_version_sync.py (the eight-carrier version guard).
See spec.md for the full contract and roadmap.md for planned work.
Acknowledgements
Carrel-calibre-web is a fork of calibre-web that uses cquarry as its search and virtual-library engine. Features proven there flow back into cquarry (the data-layer extraction closed with the fork on 2026-09-04); calibre-web's original authors deserve the credit for the web experience that fork builds on.
Support
If cquarry is useful to you and you'd like to chip in:
- liberapay · liberapay.com/bdkl
- bitcoin
bc1qkge6zr45tzqfwfmvma2ylumt6mg7wlwmhr05yv
License
MIT. See LICENSE.
Metadata
Release files for cquarry 1.26.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| cquarry-1.26.0.tar.gz | 335.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cquarry-1.26.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 470.8 kB
Release files / cquarry-1.26.0.tar.gz
| Download URL | cquarry-1.26.0.tar.gz |
|---|---|
| Size | 335.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4a1191075933d860644173536b4b8bc13f0a62c6b3ea97f3c9e9df44ff9111fe
|
|
BLAKE2b-256 checksum How to use checksums |
7654387916f3a3c2072dd7b3af183b9e5febb5f6f28e24111ce907de2674dd11
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.
Transparency logRelease files / cquarry-1.26.0-py3-none-any.whl
| Download URL | cquarry-1.26.0-py3-none-any.whl |
|---|---|
| Size | 134.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
97e8c9947ba306dcafbc0c87c3aa2ff60d3d7466e8ab9c5673efe7bcd1dd93aa
|
|
BLAKE2b-256 checksum How to use checksums |
f4ba253d0fec981c4bd8520a19074777a67aed41383371b8d041a694d192f6a6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 2, 2026.
Transparency log