Skip to content

Repos

repos

Read-access repositories for the kotobase database

Each repository wraps a single session and turns queries into data transfer objects. Headword lookups use the normalized form tables, meaning searches use the gloss_fts full text index, and Japanese substring searches over sentences use LIKE containment, which is reliable for kanji and kana text

Session Management
  • Repositories never open their own session
  • The Unit Of Work owns one session and hands the same one to every repository it exposes
Error Handling
  • Every repository inherits from KotobaseRepo, whose __init_subclass__ wraps each public method so an unexpected SQLAlchemy failure surfaces as a DatabaseError

  • Kotobase errors, such as a missing audio pack, pass through unchanged

AudioRepo

Bases: KotobaseRepo

Repository for pronunciation audio metadata and clip bytes

The audio table lives in the optional audio pack, attached to the connection only when installed. When it is absent the underlying query raises an OperationalError, which both methods translate into an AudioDatabaseNotFoundError after rolling back the session

for_key(key, *, kind=None)

Fetch audio clip metadata for a lookup key

Selects Audio rows whose key equals key exactly, optionally narrowed to a single kind, and validates each into an AudioDTO, which carries provenance and format metadata but not the raw data bytes. When the audio pack is not attached the query raises OperationalError, which is caught, the session is rolled back, and an AudioDatabaseNotFoundError is raised instead

Parameters:

Name Type Description Default
key str

The lookup key, such as a kanji or word

required
kind str | None

Restrict to a clip kind when given

None

Returns:

Type Description
list[AudioDTO]

The matching audio clips as metadata DTOs without the raw bytes, or [] when none match

Raises:

Type Description
AudioDatabaseNotFoundError

If the optional audio pack is not installed

payloads(key, *, reading=None, kind=None)

Fetch the file name and raw bytes of each matching audio clip

Selects Audio rows whose key equals key exactly, optionally narrowed by exact reading and/or kind. Rows whose data is None (metadata-only entries, such as remote clips) are skipped. For the rest, a download file name is built as <reading or key>.<fmt or "mp3"> and paired with the raw bytes. When the audio pack is not attached the query raises OperationalError, which is caught, the session is rolled back, and an AudioDatabaseNotFoundError is raised instead

Parameters:

Name Type Description Default
key str

The lookup key, such as a kanji or word

required
reading str | None

Restrict to a single clip reading when given

None
kind str | None

Restrict to a clip kind when given

None

Returns:

Type Description
list[tuple[str, bytes]]

A (file name, bytes) pair for every matching clip that has bundled audio, or [] when none match or none carry bytes

Raises:

Type Description
AudioDatabaseNotFoundError

If the optional audio pack is not installed

FuriganaRepo

Bases: KotobaseRepo

Repository for JmdictFurigana spelling-to-reading segmentation

for_text(text_value, reading=None)

Fetch furigana segmentations for a written form

Selects Furigana rows whose text equals text_value exactly. When reading is given it is added as a second exact-match filter on Furigana.reading, narrowing to the one spelling/reading pair (which is unique). Each row is validated into a FuriganaDTO

Parameters:

Name Type Description Default
text_value str

The written spelling to look up

required
reading str | None

A specific reading to narrow the match

None

Returns:

Type Description
list[FuriganaDTO]

The matching furigana segmentations as DTOs, or [] when none match

JLPTRepo

Bases: KotobaseRepo

Repository for the Tanos JLPT vocabulary, kanji and grammar lists

Resolves a word or kanji to its JLPT level, searches grammar points by substring, and returns full per-level study lists. Each method validates rows into the matching JLPT DTO

grammar_like(query, *, limit=20)

Find JLPT grammar points whose text contains the query

Selects up to limit JlptGrammar rows whose grammar contains query as a substring (LIKE '%query%'), ordered by descending level (so N1 grammar comes before N5), and validates each into a JLPTGrammarDTO

Parameters:

Name Type Description Default
query str

The substring to search for in grammar points

required
limit int | None

Maximum number of grammar points to return

20

Returns:

Type Description
list[JLPTGrammarDTO]

The matching grammar points as DTOs ordered by descending level, or [] when none match

kanji_by_literal(literal)

Fetch the JLPT kanji entry for a literal

Selects the first JlptKanji row whose kanji equals literal exactly (LIMIT 1) and validates it into a JLPTKanjiDTO

Parameters:

Name Type Description Default
literal str

The kanji character

required

Returns:

Type Description
JLPTKanjiDTO | None

The JLPT kanji entry as a DTO, or None when it is not listed

kanji_levels(literals)

Map each given kanji to its Tanos JLPT level

De-duplicates literals, then selects (kanji, level) from JlptKanji where kanji is IN that set. An empty input returns {} without a query

Parameters:

Name Type Description Default
literals Sequence[str]

The kanji to look up

required

Returns:

Type Description
dict[str, int]

A mapping of each listed kanji to its JLPT level, omitting kanji not in the Tanos list

list_grammar(level)

Return the full grammar study list for a level

Selects every JlptGrammar row whose level equals level, ordered by ascending id (insertion order), then validates each row into a JLPTGrammarDTO

Parameters:

Name Type Description Default
level int

The JLPT level from 1 to 5

required

Returns:

Type Description
list[JLPTGrammarDTO]

Every grammar point at the level as DTOs, or [] when the level is empty

list_kanji(level)

Return the full kanji study list for a level

Selects every JlptKanji row whose level equals level, ordered by ascending id (insertion order), and validates each into a JLPTKanjiDTO

Parameters:

Name Type Description Default
level int

The JLPT level from 1 to 5

required

Returns:

Type Description
list[JLPTKanjiDTO]

Every kanji item at the level as DTOs, or [] when the level is empty

list_vocab(level)

Return the full vocabulary study list for a level

Selects every JlptVocab row whose level equals level, ordered by ascending id (insertion order), and validates each into a JLPTVocabDTO

Parameters:

Name Type Description Default
level int

The JLPT level from 1 to 5

required

Returns:

Type Description
list[JLPTVocabDTO]

Every vocabulary item at the level as DTOs, or [] when the level is empty

vocab_by_word(word)

Find the JLPT vocabulary entry for a word or reading

Selects the first JlptVocab row whose word or reading equals word exactly (LIMIT 1), and validates it into a JLPTVocabDTO

Parameters:

Name Type Description Default
word str

The headword or reading to look up

required

Returns:

Type Description
JLPTVocabDTO | None

The vocabulary entry as a DTO, or None when the word is not listed

JMDictRepo

Bases: KotobaseRepo

Repository that abstracts queries over the JMdict tables

Looks up entries by sequence number, by written or reading form against the normalized jmdict_kanji / jmdict_kana tables, and by English meaning through the gloss_fts full-text index. Every method eagerly loads kanji forms, kana forms and senses with their glosses (_JMDICT_LOAD) and returns JMDictEntryDTO objects built with model_validate

by_id(entry_id)

Fetch one entry by its JMdict sequence number

Loads the row through Session.get with _JMDICT_LOAD eager loading of its kanji forms, kana forms and senses with glosses, then validates it into a JMDictEntryDTO

Parameters:

Name Type Description Default
entry_id int

The JMdict sequence number (primary key)

required

Returns:

Type Description
JMDictEntryDTO | None

The entry as a DTO, or None when no row has that id

resolve_reference(ref)

Resolve a cross-reference or antonym code to its entries

JMdict xref and antonym codes are -separated into a leading form and an optional disambiguating reading and sense number. This takes the part before the first , strips it, and when it is non-empty looks it up exactly through search_form with no limit (the reading and sense number are ignored). An empty leading form returns [] without querying

Parameters:

Name Type Description Default
ref str

The cross-reference or antonym code to resolve

required

Returns:

Type Description
list[JMDictEntryDTO]

The entries the leading form points to, or [] when the form is empty or nothing matches

search_form(form, *, wildcard=False, limit=50)

Search entries by a written or reading form

Matches form against JMDictKanji.text or JMDictKana.text through a non-correlated id IN (SELECT entry_id ... UNION SELECT entry_id ...) subquery, which SQLite drives off the ix_jmdict_kanji_text / ix_jmdict_kana_text indexes instead of scanning every entry. By default the comparison is exact. When wildcard is True, * is translated to % and the form is matched as a SQL LIKE pattern. Results are eager-loaded with _JMDICT_LOAD, ordered by _JMDICT_ORDER, and capped at limit

Parameters:

Name Type Description Default
form str

The query form, where * and % act as wildcards when wildcard is True

required
wildcard bool

When True, treat the query as a LIKE pattern, otherwise match it exactly

False
limit int | None

Maximum entries to return, or None for no limit

50

Returns:

Type Description
list[JMDictEntryDTO]

The matching entries as DTOs, or [] when none match

search_gloss(query, *, limit=50)

Search entries by English meaning through the gloss_fts index

Runs a raw FTS5 query that matches query against the gloss_fts virtual table (gloss_fts MATCH :query), joined to jmdict_sense to recover each matching sense's entry_id, capped at limit rows. The entry ids are de-duplicated while preserving first-seen order, then re-fetched and ordered through _by_ids, so the final ordering is the canonical commonness/frequency order rather than relevance

Parameters:

Name Type Description Default
query str

The FTS5 match expression to run against glosses

required
limit int | None

Maximum number of gloss_fts rows to scan

50

Returns:

Type Description
list[JMDictEntryDTO]

The matching entries as DTOs, or [] when none match

JMNeDictRepo

Bases: KotobaseRepo

Repository that abstracts queries over the JMnedict proper-name tables

Looks up names by sequence number, by written or reading form against the normalized jmnedict_kanji / jmnedict_kana tables, and by name type. Every method eagerly loads kanji forms, kana forms and translation blocks with their glosses (_JMNEDICT_LOAD) and returns JMNeDictEntryDTO objects built with model_validate

browse_by_type(name_type, *, limit=50)

Browse names that carry a given name type

The name_type codes of a block are stored as a JSON list in JMnedictTranslation.name_type, so this matches the quoted code as a substring (LIKE '%"<name_type>"%') to find translation blocks of that type, collecting up to limit distinct owning entry_id values. Those entries are then re-fetched eager-loaded with _JMNEDICT_LOAD and ordered by ascending id. When no block matches, returns [] without the second query

Parameters:

Name Type Description Default
name_type str

The name type code such as place or surname

required
limit int | None

Maximum number of distinct entry ids to collect

50

Returns:

Type Description
list[JMNeDictEntryDTO]

The matching names as DTOs ordered by id, or [] when none match

by_id(entry_id)

Fetch one name entry by its JMnedict sequence number

Loads the row through Session.get with _JMNEDICT_LOAD eager loading of its kanji forms, kana forms and translation blocks with glosses, then validates it into a JMNeDictEntryDTO

Parameters:

Name Type Description Default
entry_id int

The JMnedict sequence number (primary key)

required

Returns:

Type Description
JMNeDictEntryDTO | None

The name entry as a DTO, or None when no row has that id

search(form, *, wildcard=False, limit=50)

Search names by a written or reading form

Matches form against JMnedictKanji.text or JMnedictKana.text through a non-correlated id IN (SELECT entry_id ... UNION SELECT entry_id ...) subquery, which SQLite drives off the ix_jmnedict_kanji_text / ix_jmnedict_kana_text indexes instead of scanning every entry. By default the comparison is exact. When wildcard is True, * is translated to % and the form is matched as a SQL LIKE pattern. Results are eager-loaded with _JMNEDICT_LOAD, ordered by ascending JMnedictEntry.id and capped at limit

Parameters:

Name Type Description Default
form str

The query form, where * and % act as wildcards when wildcard is True

required
wildcard bool

When True, treat the query as a LIKE pattern, otherwise match it exactly

False
limit int | None

Maximum entries to return, or None for no limit

50

Returns:

Type Description
list[JMNeDictEntryDTO]

The matching names as DTOs ordered by id, or [] when none match

KanjiRepo

Bases: KotobaseRepo

Repository that abstracts KanjiDic2 lookups enriched with extras

Reads kanji from the kanji table, eager-loading every per-character relationship, and joins in two pieces that are not kanji relationships, the KRADFILE radical components and the Tanos JLPT level, which are injected through _kanji_payload when validating each KanjiDTO. Also serves stroke-order SVG and lookups by SKIP code or scalar attribute

bulk_fetch(literals)

Fetch several kanji at once, preserving first-seen input order

De-duplicates literals while keeping order, then selects the matching Kanji rows (literal IN ...) eager-loaded with _KANJI_LOAD, and gathers their radical components and Tanos JLPT levels through _radicals and _jlpt_levels. Each found kanji is built into a KanjiDTO via _kanji_payload, which injects that kanji's radicals and JLPT level, and the results are emitted in input order. Requested literals with no kanji row are skipped, so the result may be shorter than the input. An empty input returns [] without a query

Parameters:

Name Type Description Default
literals Sequence[str]

The kanji characters to fetch

required

Returns:

Type Description
list[KanjiDTO]

The matching kanji as DTOs in input order, or [] when none match

by_literal(literal)

Fetch one kanji with its full profile

Thin wrapper that delegates to bulk_fetch with a single literal and unwraps the result

Parameters:

Name Type Description Default
literal str

The kanji character

required

Returns:

Type Description
KanjiDTO | None

The kanji as a KanjiDTO, or None when the character is not in the kanji table

by_skip(code, *, limit=100)

Find kanji with a given SKIP query code

Selects up to limit literal values from KanjiQueryCode where type is skip and value equals code exactly, then re-fetches their full profiles through bulk_fetch

Parameters:

Name Type Description Default
code str

The SKIP code such as 1-4-3

required
limit int | None

Maximum number of matching literals to collect

100

Returns:

Type Description
list[KanjiDTO]

The matching kanji as DTOs, or [] when none carry the code

search(*, stroke_count=None, grade=None, freq_max=None, jlpt=None, limit=100)

Search kanji by scalar attributes

Builds a select over Kanji.literal, adding a filter for each non-None argument. The literals are ordered with kanji that have a freq ahead of those without, then by ascending freq, then by character, capped at limit, and re-fetched as full profiles through bulk_fetch

Filters
  • stroke_count and grade match exactly
  • freq_max keeps only kanji whose freq is set and <= freq_max
  • jlpt restricts to literals present in JlptKanji at that level (subquery)

  • Omitted arguments add no filter

Parameters:

Name Type Description Default
stroke_count int | None

Required exact stroke count

None
grade int | None

Required exact school grade

None
freq_max int | None

Maximum newspaper frequency rank

None
jlpt int | None

Required Tanos JLPT level

None
limit int | None

Maximum number of matching literals to collect

100

Returns:

Type Description
list[KanjiDTO]

The matching kanji as DTOs, ordered by frequency then character, or [] when none match

stroke_svg(literal, *, raw=False)

Fetch a kanji's KanjiVG stroke order as SVG

Loads the KanjiStrokes row by its literal primary key. By default the stored KanjiVG <kanji> markup is wrapped through _svg_document into a self-contained, browser renderable <svg> document. Pass raw to get that stored <kanji> fragment verbatim instead, which has no <svg> root or styling

Parameters:

Name Type Description Default
literal str

The kanji character

required
raw bool

When True, return the raw KanjiVG fragment unwrapped

False

Returns:

Type Description
str | None

The stroke order SVG, or None when the kanji has no stroke row

KotobaseRepo

Base class for a repository that runs queries through one shared session

Error Handling
  • Subclassing also installs the central error wrapping

  • Every public method a subclass defines is replaced with a version that converts a raised SQLAlchemyError into a DatabaseError, so individual methods only document their own non-SQLAlchemy raises

Attributes:

Name Type Description
session Session

The session, owned by the unit of work, that every query on this repository runs through

__init__(session)

Store the session this repository runs all of its queries through

The session is not opened or owned here, it is supplied by the unit of work and shared with the other repositories

Parameters:

Name Type Description Default
session Session

The active session to query through

required

__init_subclass__(**kwargs)

Wrap each public method the subclass defines with the SQLAlchemy error handler

Iterates the subclass's own vars, and for every callable, non-_ attribute, replaces it with the result of _wrap_sqlalchemy_error, so a raised SQLAlchemyError surfaces as a DatabaseError while a KotobaseError passes through

Parameters:

Name Type Description Default
**kwargs Any

Extra arguments forwarded to the base hook

{}

RadicalRepo

Bases: KotobaseRepo

Repository for the RADKFILE / KRADFILE radical decomposition data

Lists the RADKFILE search radicals, reads a kanji's radical components, and performs reverse radical search, finding the kanji that contain a chosen set of radicals

kanji_by_radicals(radicals, *, match='all')

Find kanji that contain the given radicals (reverse radical search)

De-duplicates radicals, then groups KanjiRadical rows whose radical is IN that set by literal. With match="all" a HAVING clause keeps only kanji whose distinct matched-radical count equals the number of requested radicals, so every radical is required (intersection). Any other match value (such as "any") drops the HAVING clause, so a kanji that contains at least one of the radicals matches (union). An empty radicals argument returns [] without a query

Parameters:

Name Type Description Default
radicals Sequence[str]

The radical components to match against

required
match str

all to require every radical (intersection), or any to match kanji that contain at least one (union)

'all'

Returns:

Type Description
list[str]

The matching kanji literals, or [] when none match

list_radicals()

List every RADKFILE search radical with its stroke count

Selects all Radical rows ordered by ascending stroke_count then radical character, and validates each into a RadicalDTO

Returns:

Type Description
list[RadicalDTO]

Every search radical as a DTO, ordered by stroke count then character

radicals_of(literal)

List the KRADFILE radical components of one kanji

Selects the radical column from KanjiRadical for rows whose literal equals the kanji exactly, in primary-key order

Parameters:

Name Type Description Default
literal str

The kanji character

required

Returns:

Type Description
list[str]

The radical components contained in the kanji, or [] when the kanji has no decomposition

SentenceRepo

Bases: KotobaseRepo

Repository for Tatoeba example sentences and their translations

search_containing(query, *, limit=20, wildcard=False)

Find Japanese sentences containing the query text, with translations

Selects up to limit Sentence rows where lang is jpn and text matches a SQL LIKE pattern, ordered by ascending id. By default the query is wrapped as %query% substring containment. When wildcard is True, * is translated to % and the query is used as the LIKE pattern directly. For the matched sentences it then resolves translations by joining SentenceLink (whose source_id is the Japanese sentence) to the target Sentence.text, grouping the translation texts per source id. Each sentence is validated into a SentenceDTO with its translations injected through the validation context

Parameters:

Name Type Description Default
query str

The text to search for, where * and % act as wildcards when wildcard is True

required
limit int | None

Maximum number of sentences to return

20
wildcard bool

When True, treat the query as a LIKE pattern, otherwise match it as a %query% substring

False

Returns:

Type Description
list[SentenceDTO]

The matching sentences as DTOs with their aligned translations, or [] when none match

TagRepo

Bases: KotobaseRepo

Repository for the tag dictionary that expands codes to descriptions

labels(codes)

Map tag codes to their human readable descriptions

De-duplicates codes, then selects (code, description) from Tag where code is IN that set. Because the lookup is by code alone (not by category), a code shared across tag families collapses to a single description. An empty input returns {} without a query

Parameters:

Name Type Description Default
codes Sequence[str]

The tag codes to expand

required

Returns:

Type Description
dict[str, str]

A mapping of code to description for the codes that are known, omitting any that are not in the tag table