Module ki.types
Types for ki.
Expand source code
#!/usr/bin/env python3
"""Types for ki."""
import json
import sqlite3
import textwrap
import dataclasses
from enum import Enum
from pathlib import Path
from dataclasses import dataclass
import git
import whatthepatch
from anki.decks import DeckTreeNode
from anki.collection import Note, Card
from beartype import beartype
from beartype.typing import List, Dict, Any, Optional, Union
# pylint: disable=too-many-lines, missing-class-docstring, too-many-instance-attributes
NotetypeDict = Dict[str, Any]
MODELS_FILE = "models.json"
HINT = (
"hint: Updates were rejected because the tip of your current branch is behind\n"
+ "hint: the Anki remote collection. Integrate the remote changes (e.g.\n"
+ "hint: 'ki pull ...') before pushing again."
)
ERROR_MESSAGE_WIDTH = 69
DATABASE_LOCKED_MSG = "database is locked"
DeckId = int
# TYPES
class File(type(Path())):
"""UNSAFE: Indicates that file *was* extant when it was resolved."""
class Dir(type(Path())):
"""UNSAFE: Indicates that dir *was* extant when it was resolved."""
class EmptyDir(Dir):
"""UNSAFE: Indicates that dir *was* empty (and extant) when it was resolved."""
class NoPath(type(Path())):
"""UNSAFE: Indicates that path *was not* extant when it was resolved."""
class Singleton(type(Path())):
"""UNSAFE: A path consisting of a single component (e.g. `file`, not `dir/file`)."""
class PseudoFile(type(Path())):
"""
UNSAFE: Indicates that path was extant but weird (e.g. a device or socket)
when it was resolved.
"""
class Link(type(Path())):
"""UNSAFE: Indicates that this path was a symlink when tested."""
class NoFile(NoPath):
"""A nonexistent file in an extant directory."""
@property
def parent(self):
return Dir(super().parent)
# ENUMS
class GitChangeType(Enum):
"""Enum for git file change types."""
ADDED = "A"
DELETED = "D"
RENAMED = "R"
MODIFIED = "M"
TYPECHANGED = "T"
class PushResult(Enum):
"""Enum for `push()` return codes."""
NONTRIVIAL = "NONTRIVIAL"
UP_TO_DATE = "UP_TO_DATE"
# DATACLASSES
@beartype
@dataclass(frozen=True)
class Patch:
"""Relative paths and a Diff object."""
a: Path
b: Path
diff: whatthepatch.patch.diffobj
@beartype
@dataclass(frozen=True)
class DeckNote:
"""Flat (as possible) representation of a note, but with deck."""
title: str
guid: str
deck: str
model: str
tags: List[str]
fields: Dict[str, str]
@beartype
@dataclass(frozen=True)
class NoteMetadata:
"""The nid, mod, and mid of a note."""
nid: int
mod: int
mid: int
@beartype
@dataclass(frozen=True)
class Delta:
"""
The git delta for a single file.
We don't instead store a root and a relative path, because we need the
`File` object to avoid making unnecessary syscalls to check that stuff
exists.
"""
status: GitChangeType
path: File
relpath: Path
@beartype
@dataclass(frozen=True)
class KiRepo:
"""
UNSAFE: A ki repository, including:
- .ki/hashes
- .ki/config
Existence of collection path is guaranteed.
"""
# pylint: disable=invalid-name
repo: git.Repo
root: Dir
ki: Dir
col_file: File
backups_dir: Dir
config_file: File
hashes_file: File
models_file: File
@beartype
@dataclass(frozen=True)
class Field:
"""A typechecked version of `anki.models.FieldDict` for use within ki."""
name: str
ord: Optional[int]
@beartype
@dataclass(frozen=True)
class Template:
"""A typechecked version of `anki.models.TemplateDict` for use within ki."""
name: str
qfmt: str
afmt: str
ord: Optional[int]
@beartype
@dataclass(frozen=True)
class Notetype:
"""A typechecked version of `anki.models.NotetypeDict` for use within ki."""
# pylint: disable=invalid-name
id: int
name: str
type: int
flds: List[Field]
tmpls: List[Template]
sortf: Field
# A copy of the `NotetypeDict` object as it was returned from the Anki
# database. We keep this around to preserve extra keys that may not always
# exist, but the ones above should be required for Anki to function.
dict: Dict[str, Any]
@beartype
@dataclass(frozen=True)
class ColNote:
"""A note that exists in the Anki DB."""
n: Note
new: bool
deck: str
title: str
markdown: bool
notetype: Notetype
sfld: str
@beartype
@dataclass(frozen=True)
class KiRev:
"""
UNSAFE: A repo-commit pair, where `sha` is guaranteed to be an extant
commit hash of `repo`.
"""
kirepo: KiRepo
sha: str
@beartype
@dataclass(frozen=True)
class Rev:
"""
UNSAFE: A repo-commit pair, where `sha` is guaranteed to be an extant
commit hash of `repo`.
"""
repo: git.Repo
sha: str
@beartype
@dataclass(frozen=True)
class CardFile:
"""A card written to disk, either as a link or a file."""
card: Card
file: File
@beartype
@dataclass(frozen=True)
class Deck:
did: DeckId
node: DeckTreeNode
deckd: Dir
mediad: Dir
children: List["Deck"]
fullname: str
@beartype
@dataclass(frozen=True)
class Root:
did: DeckId
node: DeckTreeNode
deckd: None
mediad: None
children: List[Deck]
fullname: str
@beartype
@dataclass(frozen=True)
class PlannedLink:
"""A not-yet-created symlink path and its extant target."""
link: NoFile
tgt: Union[File, Link]
@beartype
@dataclass(frozen=True)
class DotKi:
config: File
backups: EmptyDir
@beartype
@dataclass(frozen=True)
class Submodule:
sm: git.Submodule
sm_repo: git.Repo
rel_root: Path
branch: str
@beartype
@dataclass(frozen=True)
class MediaBytes:
"""A media file, its old bytes (from collection) and new bytes (from file)."""
file: File
old: bytes
new: bytes
@beartype
@dataclass(frozen=True)
class AddedMedia:
"""An added media file and its (possibly changed) filename."""
file: File
new_name: str
@beartype
@dataclass(frozen=True)
class NoteDBRow:
nid: int
guid: str
mid: int
mod: int
usn: int
tags: str
flds: str
sfld: Union[str, int]
csum: int
flags: int
data: str
@beartype
def notetype_json(notetype: Notetype) -> str:
"""Return the JSON for a notetype as a string."""
dictionary: Dict[str, Any] = dataclasses.asdict(notetype)
dictionary.pop("id")
inner = dictionary["dict"]
inner.pop("id")
inner.pop("mod")
dictionary["dict"] = inner
return json.dumps(dictionary, sort_keys=True, indent=4)
@beartype
def nt_str(notetype: Notetype) -> str:
"""Display a notetype and its JSON."""
# pylint: disable=invalid-name
s = notetype_json(notetype)
return f"JSON for '{notetype.id}':\n{s}"
# EXCEPTIONS
@beartype
def errwrap(msg: str) -> str:
"""Wrap an error message to a fixed width."""
out: str = textwrap.fill(textwrap.dedent(msg), width=ERROR_MESSAGE_WIDTH)
out = out.lstrip()
out = out.rstrip()
return out
class MissingFileError(FileNotFoundError):
@beartype
def __init__(self, path: Path, info: str = ""):
header = f"File not found: '{path}'"
msg = f"{info.rstrip()}"
super().__init__(f"{header}\n\n{errwrap(msg)}")
class MissingDirectoryError(RuntimeError):
@beartype
def __init__(self, path: Path, info: str = ""):
msg = f"Directory not found: '{path}'{info.rstrip()}"
super().__init__(errwrap(msg))
class ExpectedFileButGotDirectoryError(FileNotFoundError):
@beartype
def __init__(self, path: Path, info: str = ""):
msg = "A file was expected at this location, but got a directory: "
msg += f"'{path}'{info.rstrip()}"
super().__init__(errwrap(msg))
class ExpectedDirectoryButGotFileError(RuntimeError):
@beartype
def __init__(self, path: Path, info: str = ""):
msg = "A directory was expected at this location, but got a file: "
msg += f"'{path}'{info.rstrip()}"
super().__init__(errwrap(msg))
class ExpectedEmptyDirectoryButGotNonEmptyDirectoryError(RuntimeError):
@beartype
def __init__(self, path: Path, info: str = ""):
msg = "An empty directory was expected at this location, but it is nonempty: "
msg += f"'{path}'{info.rstrip()}"
super().__init__(errwrap(msg))
class StrangeExtantPathError(RuntimeError):
@beartype
def __init__(self, path: Path, info: str = ""):
msg = "A normal file or directory was expected, but got a weird pseudofile "
msg += "(e.g. a socket, or a device): "
msg += f"'{path}'{info.rstrip()}"
super().__init__(errwrap(msg))
class ExpectedNonexistentPathError(FileExistsError):
@beartype
def __init__(self, path: Path, info: str = ""):
top = f"""
Expected this path not to exist, but it does: '{path}'{info.rstrip()}
"""
msg = """
If the path is to the `.ki/` metadata directory, this error may have
been caused by a `.gitignore` file that does not include `.ki/` (this
metadata should not be tracked by git). Check if this pattern is
included in the `.gitignore` file, and if it is not included, try
adding it.
"""
super().__init__(f"{top}\n\n{errwrap(msg)}")
class NotKiRepoError(RuntimeError):
@beartype
def __init__(self):
msg = "fatal: not a ki repository (or any parent up to mount point /)\n"
msg += "Stopping at filesystem boundary."
super().__init__(errwrap(msg))
class UpdatesRejectedError(RuntimeError):
@beartype
def __init__(self, col_file: File):
msg = f"Failed to push some commits to '{col_file}'\n{HINT}"
super().__init__(errwrap(msg))
class TargetExistsError(RuntimeError):
@beartype
def __init__(self, target: Path):
msg = f"fatal: destination path '{target}' already exists and is "
msg += "not an empty directory."
super().__init__(errwrap(msg))
class GitRefNotFoundError(RuntimeError):
@beartype
def __init__(self, repo: git.Repo, sha: str):
msg = f"Repo at '{repo.working_dir}' doesn't contain rev '{sha}'"
super().__init__(errwrap(msg))
class GitHeadRefNotFoundError(RuntimeError):
@beartype
def __init__(self, repo: git.Repo, error: Exception):
msg = f"""
ValueError raised while trying to get rev 'HEAD' from repo at
'{repo.working_dir}': '{error}'. This may have occurred because there
are no commits in the current repository. However, this should never be
the case, because ki repositories must be instantiated with a 'ki clone
<collection>' command, and this command creates an initial commit.
"""
super().__init__(errwrap(msg))
class CollectionChecksumError(RuntimeError):
@beartype
def __init__(self, col_file: File):
msg = f"Checksum mismatch on {col_file}. Was file changed?"
super().__init__(errwrap(msg))
class MissingNotetypeError(RuntimeError):
@beartype
def __init__(self, model: str):
msg = f"""
Notetype '{model}' doesn't exist. Create it in Anki before adding notes
via ki. This may be caused by a corrupted '{MODELS_FILE}' file. The
models file must contain definitions for all models that appear in all
note files.
"""
super().__init__(errwrap(msg))
# TODO: We should also print which field ordinals *are* valid.
class MissingFieldOrdinalError(RuntimeError):
# pylint: disable=redefined-builtin
@beartype
def __init__(self, ord: int, model: str):
msg = f"Field with ordinal {ord} missing from notetype '{model}'."
super().__init__(errwrap(msg))
class MissingNoteIdError(RuntimeError):
@beartype
def __init__(self, nid: int):
msg = f"Failed to locate note with nid '{nid}' in Anki database."
super().__init__(errwrap(msg))
class NotetypeMismatchError(RuntimeError):
@beartype
def __init__(self, decknote: DeckNote, new_notetype: Notetype):
msg = f"Notetype '{decknote.model}' "
msg += f"specified in DeckNote with GUID '{decknote.guid}' "
msg += f"does not match passed notetype '{new_notetype}'. "
msg += "This should NEVER happen, "
msg += "and indicates a bug in the caller to 'update_note()'."
super().__init__(errwrap(msg))
class NotetypeKeyError(RuntimeError):
@beartype
def __init__(self, key: str, name: str):
msg = f"""
Expected key {key} not found in notetype '{name}' parsed from a
'{MODELS_FILE}' file in the current repository (may be contained in a
subdirectory).
"""
super().__init__(errwrap(msg))
class NoteFieldKeyError(RuntimeError):
@beartype
def __init__(self, key: str, nid: int):
msg = f"""
Expected field {key} not found in note '{nid}'. This should *never*
happen, and indicates a serious failure, since we only ever index
`anki.notes.Note` objects on names pulled from their own notetype
dictionary.
"""
super().__init__(errwrap(msg))
class UnnamedNotetypeError(RuntimeError):
@beartype
def __init__(self, nt: NotetypeDict):
msg = f"""
Failed to find 'name' field for a notetype while parsing
a '{MODELS_FILE}' file in the current repository (may be
contained in a subdirectory):
"""
super().__init__(errwrap(msg) + "\n" + str(nt))
class SQLiteLockError(RuntimeError):
@beartype
def __init__(self, col_file: File, err: sqlite3.DatabaseError):
if str(err) == DATABASE_LOCKED_MSG:
header = f"fatal: {DATABASE_LOCKED_MSG} (Anki must not be running)."
super().__init__(header)
return
header = "Unexpected SQLite3 error while attempting to acquire lock on file: "
header += f"'{col_file}':"
msg = f"""
A 'sqlite3.DatabaseError' was raised with error message: '{str(err)}'.
This may indicate that either the database file at the location
specified above is corrupted, or the config file at '.ki/config' is
pointing to the wrong location. (The latter may occur in the unlikely
event that the collection file in the Anki data directory has been
accidentally overwritten.)
"""
super().__init__(f"{header}\n{errwrap(msg)}")
class MissingMediaDirectoryError(RuntimeError):
@beartype
def __init__(self, col_path: str, media_dir: Path):
top = f"Missing or bad Anki collection media directory '{media_dir}' "
top += f"while processing collection '{col_path}':"
msg = """
This should *never* happen, as Anki generates a media directory at the
relevant location whenever a `Collection` object is instantiated. It
is possible that the collection's containing directory was manually
tampered with, or an old version of Anki incompatible with ki is
installed.
"""
super().__init__(f"{top}\n{errwrap(msg)}")
class AnkiAlreadyOpenError(RuntimeError):
@beartype
def __init__(self, msg: str):
super().__init__(f"fatal: {msg}")
class MissingTidyExecutableError(FileNotFoundError):
@beartype
def __init__(self, err: FileNotFoundError):
top = "Command not found: 'tidy' (Is 'html5-tidy' installed?)"
msg = f"Original exception: {err}"
super().__init__(f"{top}\n{errwrap(msg)}")
class AnkiDBNoteMissingFieldsError(RuntimeError):
@beartype
def __init__(self, decknote: DeckNote, nid: int, key: str):
top = f"fatal: Note with GUID '{decknote.guid}' missing DB field '{key}'"
msg = f"""
This is strange, should only happen if the `add_db_note()` call fails
or behaves strangely. This may indicate a bug in ki. Please report this
on GitHub at https://github.com/langfield/ki/issues. Note ID: '{nid}'.
"""
super().__init__(f"{top}\n\n{errwrap(msg)}")
class GitFileModeParseError(RuntimeError):
@beartype
def __init__(self, file: Path, out: str):
top = f"fatal: Failed to parse git file mode for media file '{file}'"
msg = """
A 'git ls-files' call is used to figure out the git file mode for
cloned media files. This is done in order to detect symlinks on
Windows, and follow them manually. This error is raised when we are
unable to parse the output of 'git ls-files' for some reason or
another, which for a symlink called 'filename', should look like this:
"""
example = "120000 a35bd1f49b7b9225a76d052e9a35fb711a8646a6 0 filename"
msg2 = f"Actual unparsed git command output:\n{out}"
super().__init__(f"{top}\n\n{errwrap(msg)}\n\n{example}\n\n{msg2}")
class NonEmptyWorkingTreeError(RuntimeError):
@beartype
def __init__(self, repo: git.Repo):
top = "fatal: Non-empty working tree in freshly cloned repo at "
top += f"'{repo.working_dir}'"
msg = """
The working tree in a fresh clone should always be empty, and so if it
isn't, this means that some files were either errantly generated during
the clone process, or were not committed when they should have been.
This may indicate a bug in ki. Please report this on GitHub at
https://github.com/langfield/ki/issues.
"""
details = "\nUntracked files:\n"
for untracked in repo.untracked_files:
details += f" * {untracked}\n"
details += "\nChanged files:\n"
for item in repo.index.diff(None):
details += f" * {item.b_path}\n"
super().__init__(f"{top}\n\n{errwrap(msg)}\n{details}")
# WARNINGS
class NoteFieldValidationWarning(Warning):
@beartype
def __init__(self, nid: int, field: str, notetype: Notetype):
top = f"Warning: Bad field '{field}' for notetype '{notetype}' in note '{nid}'"
msg = "Try correcting the field name or changing the notetype."
msg += f"The fields for the notetype '{notetype}' are:"
fields: List[str] = [field.name for field in notetype.flds]
listing: str = " " + "\n ".join(fields)
super().__init__(f"{top}\n{errwrap(msg)}\n{listing}")
class WrongFieldCountWarning(Warning):
@beartype
def __init__(self, decknote: DeckNote, names: List[str]):
top = f"Warning: Wrong number of fields for model '{decknote.model}'"
msg = f"""
The notetype '{decknote.model}' takes '{len(names)}' fields, but got
'{len(decknote.fields.keys())}' for note with GUID '{decknote.guid}'.
"""
super().__init__(f"{top}\n{errwrap(msg)}")
class InconsistentFieldNamesWarning(Warning):
@beartype
def __init__(self, x: str, y: str, decknote: DeckNote):
top = f"Warning: Inconsistent field names ('{x}' != '{y}')"
msg = f"""
Expected a field '{x}' for notetype '{decknote.model}', but got a field
'{y}' in note with GUID '{decknote.guid}'.
"""
super().__init__(f"{top}\n{errwrap(msg)}")
class DeletedFileNotFoundWarning(Warning):
@beartype
def __init__(self, path: Path):
top = f"Deleted file not found in source commit: '{path}'"
msg = """
Unexpected: this may indicate a bug in ki. The source commit is what we
are diffing against, and so we expect all files whose change type is
'DELETED' to appear in a checkout of that reference. However, we return
a 'Warning' instead of an 'Exception' in order to avoid interrupting
the execution of a 'push()' call where it is not strictly necessary.
"""
super().__init__(f"{top}\n{errwrap(msg)}")
class DiffTargetFileNotFoundWarning(Warning):
@beartype
def __init__(self, path: Path):
top = f"Diff target file not found: '{path}'"
msg1 = """
Unexpected: this sometimes happens when a git repository is copied into
a subdirectory of a ki repository, and then added with 'git add'
instead of being added as a git submodule with 'git submodule add'. If
git displayed a warning on a recent 'git add' command, refer to the
hints within that warning.
"""
msg2 = """
Otherwise, this may indicate a bug in ki. The caller prevents this
warning from being instantiated unless the git change type is one of
'ADDED', 'MODIFIED', or 'RENAMED'. In all cases, the file being diffed
should be extant in the target commit of the repository. However, we
return a 'Warning' instead of an 'Exception' in order to avoid
interrupting the execution of a 'push()' call where it is not strictly
necessary.
"""
super().__init__(f"{top}\n\n{errwrap(msg1)}\n\n{errwrap(msg2)}")
class RenamedMediaFileWarning(Warning):
@beartype
def __init__(self, src: str, dst: str):
top = f"Media file '{src}' renamed to '{dst}'"
msg = """
This happens when we push a media file to a collection that already
contains another media file with the same name. In this case, Anki does
some deduplication by renaming the new one.
"""
super().__init__(f"{top}\n{errwrap(msg)}")
class NotetypeCollisionWarning(Warning):
@beartype
def __init__(self, model: Notetype, existing: Notetype):
msg = """
Collision: new notetype '{model.name}' has same name as existing
notetype with mid '{existing.id}', but hashes differ.
"""
super().__init__(f"{errwrap(msg)}\n\n{nt_str(model)}\n\n{nt_str(existing)}")
class EmptyNoteWarning(Warning):
@beartype
def __init__(self, note: Note, health: int):
top = f"Found empty note with nid '{note.id}'"
msg = f"""
Anki fields health check code: '{health}'
"""
super().__init__(f"{top}\n{errwrap(msg)}")
class DuplicateNoteWarning(Warning):
@beartype
def __init__(self, note: Note, health: int, rep: str):
top = "Failed to add duplicate note to collection"
msg = f"""
Notetype/fields of note with nid '{note.id}' are duplicate of existing note.
"""
field = f"First field\n-----------\n{rep}"
code = f"Anki fields health check code: {health}"
super().__init__(f"{top}\n{errwrap(msg)}\n\n{field}\n\n{code}")
class UnhealthyNoteWarning(Warning):
@beartype
def __init__(self, note: Note, health: int):
top = f"Note with nid '{note.id}' failed fields check with unknown error code"
msg = f"""
Anki fields health check code: '{health}'
"""
super().__init__(f"{top}\n{errwrap(msg)}")
class MediaDirectoryDeckNameCollisionWarning(Warning):
@beartype
def __init__(self):
top = "Decks with name '_media' skipped as name is reserved"
super().__init__(f"{top}")
Functions
def errwrap(msg: str) ‑> str
-
Wrap an error message to a fixed width.
Expand source code
@beartype def errwrap(msg: str) -> str: """Wrap an error message to a fixed width.""" out: str = textwrap.fill(textwrap.dedent(msg), width=ERROR_MESSAGE_WIDTH) out = out.lstrip() out = out.rstrip() return out
def notetype_json(notetype: Notetype) ‑> str
-
Return the JSON for a notetype as a string.
Expand source code
@beartype def notetype_json(notetype: Notetype) -> str: """Return the JSON for a notetype as a string.""" dictionary: Dict[str, Any] = dataclasses.asdict(notetype) dictionary.pop("id") inner = dictionary["dict"] inner.pop("id") inner.pop("mod") dictionary["dict"] = inner return json.dumps(dictionary, sort_keys=True, indent=4)
def nt_str(notetype: Notetype) ‑> str
-
Display a notetype and its JSON.
Expand source code
@beartype def nt_str(notetype: Notetype) -> str: """Display a notetype and its JSON.""" # pylint: disable=invalid-name s = notetype_json(notetype) return f"JSON for '{notetype.id}':\n{s}"
Classes
class AddedMedia (file: File, new_name: str)
-
An added media file and its (possibly changed) filename.
Expand source code
@beartype @dataclass(frozen=True) class AddedMedia: """An added media file and its (possibly changed) filename.""" file: File new_name: str
Class variables
var file : File
var new_name : str
class AnkiAlreadyOpenError (msg: str)
-
Unspecified run-time error.
Expand source code
class AnkiAlreadyOpenError(RuntimeError): @beartype def __init__(self, msg: str): super().__init__(f"fatal: {msg}")
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class AnkiDBNoteMissingFieldsError (decknote: DeckNote, nid: int, key: str)
-
Unspecified run-time error.
Expand source code
class AnkiDBNoteMissingFieldsError(RuntimeError): @beartype def __init__(self, decknote: DeckNote, nid: int, key: str): top = f"fatal: Note with GUID '{decknote.guid}' missing DB field '{key}'" msg = f""" This is strange, should only happen if the `add_db_note()` call fails or behaves strangely. This may indicate a bug in ki. Please report this on GitHub at https://github.com/langfield/ki/issues. Note ID: '{nid}'. """ super().__init__(f"{top}\n\n{errwrap(msg)}")
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class CardFile (card: anki.cards.Card, file: File)
-
A card written to disk, either as a link or a file.
Expand source code
@beartype @dataclass(frozen=True) class CardFile: """A card written to disk, either as a link or a file.""" card: Card file: File
Class variables
var card : anki.cards.Card
var file : File
class ColNote (n: anki.notes.Note, new: bool, deck: str, title: str, markdown: bool, notetype: Notetype, sfld: str)
-
A note that exists in the Anki DB.
Expand source code
@beartype @dataclass(frozen=True) class ColNote: """A note that exists in the Anki DB.""" n: Note new: bool deck: str title: str markdown: bool notetype: Notetype sfld: str
Class variables
var deck : str
var markdown : bool
var n : anki.notes.Note
var new : bool
var notetype : Notetype
var sfld : str
var title : str
class CollectionChecksumError (col_file: File)
-
Unspecified run-time error.
Expand source code
class CollectionChecksumError(RuntimeError): @beartype def __init__(self, col_file: File): msg = f"Checksum mismatch on {col_file}. Was file changed?" super().__init__(errwrap(msg))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class Deck (did: int, node: anki.decks_pb2.DeckTreeNode, deckd: Dir, mediad: Dir, children: list['Deck'], fullname: str)
-
Deck(did: int, node: anki.decks_pb2.DeckTreeNode, deckd: ki.types.Dir, mediad: ki.types.Dir, children: list['Deck'], fullname: str)
Expand source code
@beartype @dataclass(frozen=True) class Deck: did: DeckId node: DeckTreeNode deckd: Dir mediad: Dir children: List["Deck"] fullname: str
Class variables
var children : list['Deck']
var deckd : Dir
var did : int
var fullname : str
var mediad : Dir
var node : anki.decks_pb2.DeckTreeNode
class DeckNote (title: str, guid: str, deck: str, model: str, tags: list[str], fields: dict[str, str])
-
Flat (as possible) representation of a note, but with deck.
Expand source code
@beartype @dataclass(frozen=True) class DeckNote: """Flat (as possible) representation of a note, but with deck.""" title: str guid: str deck: str model: str tags: List[str] fields: Dict[str, str]
Class variables
var deck : str
var fields : dict[str, str]
var guid : str
var model : str
var title : str
class DeletedFileNotFoundWarning (path: pathlib.Path)
-
Base class for warning categories.
Expand source code
class DeletedFileNotFoundWarning(Warning): @beartype def __init__(self, path: Path): top = f"Deleted file not found in source commit: '{path}'" msg = """ Unexpected: this may indicate a bug in ki. The source commit is what we are diffing against, and so we expect all files whose change type is 'DELETED' to appear in a checkout of that reference. However, we return a 'Warning' instead of an 'Exception' in order to avoid interrupting the execution of a 'push()' call where it is not strictly necessary. """ super().__init__(f"{top}\n{errwrap(msg)}")
Ancestors
- builtins.Warning
- builtins.Exception
- builtins.BaseException
class Delta (status: GitChangeType, path: File, relpath: pathlib.Path)
-
The git delta for a single file.
We don't instead store a root and a relative path, because we need the
File
object to avoid making unnecessary syscalls to check that stuff exists.Expand source code
@beartype @dataclass(frozen=True) class Delta: """ The git delta for a single file. We don't instead store a root and a relative path, because we need the `File` object to avoid making unnecessary syscalls to check that stuff exists. """ status: GitChangeType path: File relpath: Path
Class variables
var path : File
var relpath : pathlib.Path
var status : GitChangeType
class DiffTargetFileNotFoundWarning (path: pathlib.Path)
-
Base class for warning categories.
Expand source code
class DiffTargetFileNotFoundWarning(Warning): @beartype def __init__(self, path: Path): top = f"Diff target file not found: '{path}'" msg1 = """ Unexpected: this sometimes happens when a git repository is copied into a subdirectory of a ki repository, and then added with 'git add' instead of being added as a git submodule with 'git submodule add'. If git displayed a warning on a recent 'git add' command, refer to the hints within that warning. """ msg2 = """ Otherwise, this may indicate a bug in ki. The caller prevents this warning from being instantiated unless the git change type is one of 'ADDED', 'MODIFIED', or 'RENAMED'. In all cases, the file being diffed should be extant in the target commit of the repository. However, we return a 'Warning' instead of an 'Exception' in order to avoid interrupting the execution of a 'push()' call where it is not strictly necessary. """ super().__init__(f"{top}\n\n{errwrap(msg1)}\n\n{errwrap(msg2)}")
Ancestors
- builtins.Warning
- builtins.Exception
- builtins.BaseException
class Dir (*args, **kwargs)
-
UNSAFE: Indicates that dir was extant when it was resolved.
Expand source code
class Dir(type(Path())): """UNSAFE: Indicates that dir *was* extant when it was resolved."""
Ancestors
- pathlib.PosixPath
- pathlib.Path
- pathlib.PurePosixPath
- pathlib.PurePath
Subclasses
class DotKi (config: File, backups: EmptyDir)
-
DotKi(config: ki.types.File, backups: ki.types.EmptyDir)
Expand source code
@beartype @dataclass(frozen=True) class DotKi: config: File backups: EmptyDir
Class variables
var backups : EmptyDir
var config : File
class DuplicateNoteWarning (note: anki.notes.Note, health: int, rep: str)
-
Base class for warning categories.
Expand source code
class DuplicateNoteWarning(Warning): @beartype def __init__(self, note: Note, health: int, rep: str): top = "Failed to add duplicate note to collection" msg = f""" Notetype/fields of note with nid '{note.id}' are duplicate of existing note. """ field = f"First field\n-----------\n{rep}" code = f"Anki fields health check code: {health}" super().__init__(f"{top}\n{errwrap(msg)}\n\n{field}\n\n{code}")
Ancestors
- builtins.Warning
- builtins.Exception
- builtins.BaseException
class EmptyDir (*args, **kwargs)
-
UNSAFE: Indicates that dir was empty (and extant) when it was resolved.
Expand source code
class EmptyDir(Dir): """UNSAFE: Indicates that dir *was* empty (and extant) when it was resolved."""
Ancestors
- Dir
- pathlib.PosixPath
- pathlib.Path
- pathlib.PurePosixPath
- pathlib.PurePath
class EmptyNoteWarning (note: anki.notes.Note, health: int)
-
Base class for warning categories.
Expand source code
class EmptyNoteWarning(Warning): @beartype def __init__(self, note: Note, health: int): top = f"Found empty note with nid '{note.id}'" msg = f""" Anki fields health check code: '{health}' """ super().__init__(f"{top}\n{errwrap(msg)}")
Ancestors
- builtins.Warning
- builtins.Exception
- builtins.BaseException
class ExpectedDirectoryButGotFileError (path: pathlib.Path, info: str = '')
-
Unspecified run-time error.
Expand source code
class ExpectedDirectoryButGotFileError(RuntimeError): @beartype def __init__(self, path: Path, info: str = ""): msg = "A directory was expected at this location, but got a file: " msg += f"'{path}'{info.rstrip()}" super().__init__(errwrap(msg))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class ExpectedEmptyDirectoryButGotNonEmptyDirectoryError (path: pathlib.Path, info: str = '')
-
Unspecified run-time error.
Expand source code
class ExpectedEmptyDirectoryButGotNonEmptyDirectoryError(RuntimeError): @beartype def __init__(self, path: Path, info: str = ""): msg = "An empty directory was expected at this location, but it is nonempty: " msg += f"'{path}'{info.rstrip()}" super().__init__(errwrap(msg))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class ExpectedFileButGotDirectoryError (path: pathlib.Path, info: str = '')
-
File not found.
Expand source code
class ExpectedFileButGotDirectoryError(FileNotFoundError): @beartype def __init__(self, path: Path, info: str = ""): msg = "A file was expected at this location, but got a directory: " msg += f"'{path}'{info.rstrip()}" super().__init__(errwrap(msg))
Ancestors
- builtins.FileNotFoundError
- builtins.OSError
- builtins.Exception
- builtins.BaseException
class ExpectedNonexistentPathError (path: pathlib.Path, info: str = '')
-
File already exists.
Expand source code
class ExpectedNonexistentPathError(FileExistsError): @beartype def __init__(self, path: Path, info: str = ""): top = f""" Expected this path not to exist, but it does: '{path}'{info.rstrip()} """ msg = """ If the path is to the `.ki/` metadata directory, this error may have been caused by a `.gitignore` file that does not include `.ki/` (this metadata should not be tracked by git). Check if this pattern is included in the `.gitignore` file, and if it is not included, try adding it. """ super().__init__(f"{top}\n\n{errwrap(msg)}")
Ancestors
- builtins.FileExistsError
- builtins.OSError
- builtins.Exception
- builtins.BaseException
class Field (name: str, ord: Optional[int])
-
A typechecked version of
anki.models.FieldDict
for use within ki.Expand source code
@beartype @dataclass(frozen=True) class Field: """A typechecked version of `anki.models.FieldDict` for use within ki.""" name: str ord: Optional[int]
Class variables
var name : str
var ord : Optional[int]
class File (*args, **kwargs)
-
UNSAFE: Indicates that file was extant when it was resolved.
Expand source code
class File(type(Path())): """UNSAFE: Indicates that file *was* extant when it was resolved."""
Ancestors
- pathlib.PosixPath
- pathlib.Path
- pathlib.PurePosixPath
- pathlib.PurePath
class GitChangeType (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
Enum for git file change types.
Expand source code
class GitChangeType(Enum): """Enum for git file change types.""" ADDED = "A" DELETED = "D" RENAMED = "R" MODIFIED = "M" TYPECHANGED = "T"
Ancestors
- enum.Enum
Class variables
var ADDED
var DELETED
var MODIFIED
var RENAMED
var TYPECHANGED
class GitFileModeParseError (file: pathlib.Path, out: str)
-
Unspecified run-time error.
Expand source code
class GitFileModeParseError(RuntimeError): @beartype def __init__(self, file: Path, out: str): top = f"fatal: Failed to parse git file mode for media file '{file}'" msg = """ A 'git ls-files' call is used to figure out the git file mode for cloned media files. This is done in order to detect symlinks on Windows, and follow them manually. This error is raised when we are unable to parse the output of 'git ls-files' for some reason or another, which for a symlink called 'filename', should look like this: """ example = "120000 a35bd1f49b7b9225a76d052e9a35fb711a8646a6 0 filename" msg2 = f"Actual unparsed git command output:\n{out}" super().__init__(f"{top}\n\n{errwrap(msg)}\n\n{example}\n\n{msg2}")
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class GitHeadRefNotFoundError (repo: git.repo.base.Repo, error: Exception)
-
Unspecified run-time error.
Expand source code
class GitHeadRefNotFoundError(RuntimeError): @beartype def __init__(self, repo: git.Repo, error: Exception): msg = f""" ValueError raised while trying to get rev 'HEAD' from repo at '{repo.working_dir}': '{error}'. This may have occurred because there are no commits in the current repository. However, this should never be the case, because ki repositories must be instantiated with a 'ki clone <collection>' command, and this command creates an initial commit. """ super().__init__(errwrap(msg))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class GitRefNotFoundError (repo: git.repo.base.Repo, sha: str)
-
Unspecified run-time error.
Expand source code
class GitRefNotFoundError(RuntimeError): @beartype def __init__(self, repo: git.Repo, sha: str): msg = f"Repo at '{repo.working_dir}' doesn't contain rev '{sha}'" super().__init__(errwrap(msg))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class InconsistentFieldNamesWarning (x: str, y: str, decknote: DeckNote)
-
Base class for warning categories.
Expand source code
class InconsistentFieldNamesWarning(Warning): @beartype def __init__(self, x: str, y: str, decknote: DeckNote): top = f"Warning: Inconsistent field names ('{x}' != '{y}')" msg = f""" Expected a field '{x}' for notetype '{decknote.model}', but got a field '{y}' in note with GUID '{decknote.guid}'. """ super().__init__(f"{top}\n{errwrap(msg)}")
Ancestors
- builtins.Warning
- builtins.Exception
- builtins.BaseException
class KiRepo (repo: git.repo.base.Repo, root: Dir, ki: Dir, col_file: File, backups_dir: Dir, config_file: File, hashes_file: File, models_file: File)
-
UNSAFE: A ki repository, including: - .ki/hashes - .ki/config
Existence of collection path is guaranteed.
Expand source code
@beartype @dataclass(frozen=True) class KiRepo: """ UNSAFE: A ki repository, including: - .ki/hashes - .ki/config Existence of collection path is guaranteed. """ # pylint: disable=invalid-name repo: git.Repo root: Dir ki: Dir col_file: File backups_dir: Dir config_file: File hashes_file: File models_file: File
Class variables
var backups_dir : Dir
var col_file : File
var config_file : File
var hashes_file : File
var ki : Dir
var models_file : File
var repo : git.repo.base.Repo
var root : Dir
class KiRev (kirepo: KiRepo, sha: str)
-
UNSAFE: A repo-commit pair, where
sha
is guaranteed to be an extant commit hash ofrepo
.Expand source code
@beartype @dataclass(frozen=True) class KiRev: """ UNSAFE: A repo-commit pair, where `sha` is guaranteed to be an extant commit hash of `repo`. """ kirepo: KiRepo sha: str
Class variables
var kirepo : KiRepo
var sha : str
class Link (*args, **kwargs)
-
UNSAFE: Indicates that this path was a symlink when tested.
Expand source code
class Link(type(Path())): """UNSAFE: Indicates that this path was a symlink when tested."""
Ancestors
- pathlib.PosixPath
- pathlib.Path
- pathlib.PurePosixPath
- pathlib.PurePath
class MediaBytes (file: File, old: bytes, new: bytes)
-
A media file, its old bytes (from collection) and new bytes (from file).
Expand source code
@beartype @dataclass(frozen=True) class MediaBytes: """A media file, its old bytes (from collection) and new bytes (from file).""" file: File old: bytes new: bytes
Class variables
var file : File
var new : bytes
var old : bytes
class MediaDirectoryDeckNameCollisionWarning
-
Base class for warning categories.
Expand source code
class MediaDirectoryDeckNameCollisionWarning(Warning): @beartype def __init__(self): top = "Decks with name '_media' skipped as name is reserved" super().__init__(f"{top}")
Ancestors
- builtins.Warning
- builtins.Exception
- builtins.BaseException
class MissingDirectoryError (path: pathlib.Path, info: str = '')
-
Unspecified run-time error.
Expand source code
class MissingDirectoryError(RuntimeError): @beartype def __init__(self, path: Path, info: str = ""): msg = f"Directory not found: '{path}'{info.rstrip()}" super().__init__(errwrap(msg))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class MissingFieldOrdinalError (ord: int, model: str)
-
Unspecified run-time error.
Expand source code
class MissingFieldOrdinalError(RuntimeError): # pylint: disable=redefined-builtin @beartype def __init__(self, ord: int, model: str): msg = f"Field with ordinal {ord} missing from notetype '{model}'." super().__init__(errwrap(msg))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class MissingFileError (path: pathlib.Path, info: str = '')
-
File not found.
Expand source code
class MissingFileError(FileNotFoundError): @beartype def __init__(self, path: Path, info: str = ""): header = f"File not found: '{path}'" msg = f"{info.rstrip()}" super().__init__(f"{header}\n\n{errwrap(msg)}")
Ancestors
- builtins.FileNotFoundError
- builtins.OSError
- builtins.Exception
- builtins.BaseException
class MissingMediaDirectoryError (col_path: str, media_dir: pathlib.Path)
-
Unspecified run-time error.
Expand source code
class MissingMediaDirectoryError(RuntimeError): @beartype def __init__(self, col_path: str, media_dir: Path): top = f"Missing or bad Anki collection media directory '{media_dir}' " top += f"while processing collection '{col_path}':" msg = """ This should *never* happen, as Anki generates a media directory at the relevant location whenever a `Collection` object is instantiated. It is possible that the collection's containing directory was manually tampered with, or an old version of Anki incompatible with ki is installed. """ super().__init__(f"{top}\n{errwrap(msg)}")
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class MissingNoteIdError (nid: int)
-
Unspecified run-time error.
Expand source code
class MissingNoteIdError(RuntimeError): @beartype def __init__(self, nid: int): msg = f"Failed to locate note with nid '{nid}' in Anki database." super().__init__(errwrap(msg))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class MissingNotetypeError (model: str)
-
Unspecified run-time error.
Expand source code
class MissingNotetypeError(RuntimeError): @beartype def __init__(self, model: str): msg = f""" Notetype '{model}' doesn't exist. Create it in Anki before adding notes via ki. This may be caused by a corrupted '{MODELS_FILE}' file. The models file must contain definitions for all models that appear in all note files. """ super().__init__(errwrap(msg))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class MissingTidyExecutableError (err: FileNotFoundError)
-
File not found.
Expand source code
class MissingTidyExecutableError(FileNotFoundError): @beartype def __init__(self, err: FileNotFoundError): top = "Command not found: 'tidy' (Is 'html5-tidy' installed?)" msg = f"Original exception: {err}" super().__init__(f"{top}\n{errwrap(msg)}")
Ancestors
- builtins.FileNotFoundError
- builtins.OSError
- builtins.Exception
- builtins.BaseException
class NoFile (*args, **kwargs)
-
A nonexistent file in an extant directory.
Expand source code
class NoFile(NoPath): """A nonexistent file in an extant directory.""" @property def parent(self): return Dir(super().parent)
Ancestors
- NoPath
- pathlib.PosixPath
- pathlib.Path
- pathlib.PurePosixPath
- pathlib.PurePath
Instance variables
var parent
-
The logical parent of the path.
Expand source code
@property def parent(self): return Dir(super().parent)
class NoPath (*args, **kwargs)
-
UNSAFE: Indicates that path was not extant when it was resolved.
Expand source code
class NoPath(type(Path())): """UNSAFE: Indicates that path *was not* extant when it was resolved."""
Ancestors
- pathlib.PosixPath
- pathlib.Path
- pathlib.PurePosixPath
- pathlib.PurePath
Subclasses
class NonEmptyWorkingTreeError (repo: git.repo.base.Repo)
-
Unspecified run-time error.
Expand source code
class NonEmptyWorkingTreeError(RuntimeError): @beartype def __init__(self, repo: git.Repo): top = "fatal: Non-empty working tree in freshly cloned repo at " top += f"'{repo.working_dir}'" msg = """ The working tree in a fresh clone should always be empty, and so if it isn't, this means that some files were either errantly generated during the clone process, or were not committed when they should have been. This may indicate a bug in ki. Please report this on GitHub at https://github.com/langfield/ki/issues. """ details = "\nUntracked files:\n" for untracked in repo.untracked_files: details += f" * {untracked}\n" details += "\nChanged files:\n" for item in repo.index.diff(None): details += f" * {item.b_path}\n" super().__init__(f"{top}\n\n{errwrap(msg)}\n{details}")
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class NotKiRepoError
-
Unspecified run-time error.
Expand source code
class NotKiRepoError(RuntimeError): @beartype def __init__(self): msg = "fatal: not a ki repository (or any parent up to mount point /)\n" msg += "Stopping at filesystem boundary." super().__init__(errwrap(msg))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class NoteDBRow (nid: int, guid: str, mid: int, mod: int, usn: int, tags: str, flds: str, sfld: Union[str, int], csum: int, flags: int, data: str)
-
NoteDBRow(nid: int, guid: str, mid: int, mod: int, usn: int, tags: str, flds: str, sfld: Union[str, int], csum: int, flags: int, data: str)
Expand source code
@beartype @dataclass(frozen=True) class NoteDBRow: nid: int guid: str mid: int mod: int usn: int tags: str flds: str sfld: Union[str, int] csum: int flags: int data: str
Class variables
var csum : int
var data : str
var flags : int
var flds : str
var guid : str
var mid : int
var mod : int
var nid : int
var sfld : Union[str, int]
var usn : int
class NoteFieldKeyError (key: str, nid: int)
-
Unspecified run-time error.
Expand source code
class NoteFieldKeyError(RuntimeError): @beartype def __init__(self, key: str, nid: int): msg = f""" Expected field {key} not found in note '{nid}'. This should *never* happen, and indicates a serious failure, since we only ever index `anki.notes.Note` objects on names pulled from their own notetype dictionary. """ super().__init__(errwrap(msg))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class NoteFieldValidationWarning (nid: int, field: str, notetype: Notetype)
-
Base class for warning categories.
Expand source code
class NoteFieldValidationWarning(Warning): @beartype def __init__(self, nid: int, field: str, notetype: Notetype): top = f"Warning: Bad field '{field}' for notetype '{notetype}' in note '{nid}'" msg = "Try correcting the field name or changing the notetype." msg += f"The fields for the notetype '{notetype}' are:" fields: List[str] = [field.name for field in notetype.flds] listing: str = " " + "\n ".join(fields) super().__init__(f"{top}\n{errwrap(msg)}\n{listing}")
Ancestors
- builtins.Warning
- builtins.Exception
- builtins.BaseException
class NoteMetadata (nid: int, mod: int, mid: int)
-
The nid, mod, and mid of a note.
Expand source code
@beartype @dataclass(frozen=True) class NoteMetadata: """The nid, mod, and mid of a note.""" nid: int mod: int mid: int
Class variables
var mid : int
var mod : int
var nid : int
class Notetype (id: int, name: str, type: int, flds: list[Field], tmpls: list[Template], sortf: Field, dict: dict[str, typing.Any])
-
A typechecked version of
anki.models.NotetypeDict
for use within ki.Expand source code
@beartype @dataclass(frozen=True) class Notetype: """A typechecked version of `anki.models.NotetypeDict` for use within ki.""" # pylint: disable=invalid-name id: int name: str type: int flds: List[Field] tmpls: List[Template] sortf: Field # A copy of the `NotetypeDict` object as it was returned from the Anki # database. We keep this around to preserve extra keys that may not always # exist, but the ones above should be required for Anki to function. dict: Dict[str, Any]
Class variables
var dict : dict[str, typing.Any]
var flds : list[Field]
var id : int
var name : str
var sortf : Field
var tmpls : list[Template]
var type : int
class NotetypeCollisionWarning (model: Notetype, existing: Notetype)
-
Base class for warning categories.
Expand source code
class NotetypeCollisionWarning(Warning): @beartype def __init__(self, model: Notetype, existing: Notetype): msg = """ Collision: new notetype '{model.name}' has same name as existing notetype with mid '{existing.id}', but hashes differ. """ super().__init__(f"{errwrap(msg)}\n\n{nt_str(model)}\n\n{nt_str(existing)}")
Ancestors
- builtins.Warning
- builtins.Exception
- builtins.BaseException
class NotetypeKeyError (key: str, name: str)
-
Unspecified run-time error.
Expand source code
class NotetypeKeyError(RuntimeError): @beartype def __init__(self, key: str, name: str): msg = f""" Expected key {key} not found in notetype '{name}' parsed from a '{MODELS_FILE}' file in the current repository (may be contained in a subdirectory). """ super().__init__(errwrap(msg))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class NotetypeMismatchError (decknote: DeckNote, new_notetype: Notetype)
-
Unspecified run-time error.
Expand source code
class NotetypeMismatchError(RuntimeError): @beartype def __init__(self, decknote: DeckNote, new_notetype: Notetype): msg = f"Notetype '{decknote.model}' " msg += f"specified in DeckNote with GUID '{decknote.guid}' " msg += f"does not match passed notetype '{new_notetype}'. " msg += "This should NEVER happen, " msg += "and indicates a bug in the caller to 'update_note()'." super().__init__(errwrap(msg))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class Patch (a: pathlib.Path, b: pathlib.Path, diff: whatthepatch.patch.diff)
-
Relative paths and a Diff object.
Expand source code
@beartype @dataclass(frozen=True) class Patch: """Relative paths and a Diff object.""" a: Path b: Path diff: whatthepatch.patch.diffobj
Class variables
var a : pathlib.Path
var b : pathlib.Path
var diff : whatthepatch.patch.diff
class PlannedLink (link: NoFile, tgt: Union[File, Link])
-
A not-yet-created symlink path and its extant target.
Expand source code
@beartype @dataclass(frozen=True) class PlannedLink: """A not-yet-created symlink path and its extant target.""" link: NoFile tgt: Union[File, Link]
Class variables
var link : NoFile
var tgt : Union[File, Link]
class PseudoFile (*args, **kwargs)
-
UNSAFE: Indicates that path was extant but weird (e.g. a device or socket) when it was resolved.
Expand source code
class PseudoFile(type(Path())): """ UNSAFE: Indicates that path was extant but weird (e.g. a device or socket) when it was resolved. """
Ancestors
- pathlib.PosixPath
- pathlib.Path
- pathlib.PurePosixPath
- pathlib.PurePath
class PushResult (value, names=None, *, module=None, qualname=None, type=None, start=1)
-
Enum for
push()
return codes.Expand source code
class PushResult(Enum): """Enum for `push()` return codes.""" NONTRIVIAL = "NONTRIVIAL" UP_TO_DATE = "UP_TO_DATE"
Ancestors
- enum.Enum
Class variables
var NONTRIVIAL
var UP_TO_DATE
class RenamedMediaFileWarning (src: str, dst: str)
-
Base class for warning categories.
Expand source code
class RenamedMediaFileWarning(Warning): @beartype def __init__(self, src: str, dst: str): top = f"Media file '{src}' renamed to '{dst}'" msg = """ This happens when we push a media file to a collection that already contains another media file with the same name. In this case, Anki does some deduplication by renaming the new one. """ super().__init__(f"{top}\n{errwrap(msg)}")
Ancestors
- builtins.Warning
- builtins.Exception
- builtins.BaseException
class Rev (repo: git.repo.base.Repo, sha: str)
-
UNSAFE: A repo-commit pair, where
sha
is guaranteed to be an extant commit hash ofrepo
.Expand source code
@beartype @dataclass(frozen=True) class Rev: """ UNSAFE: A repo-commit pair, where `sha` is guaranteed to be an extant commit hash of `repo`. """ repo: git.Repo sha: str
Class variables
var repo : git.repo.base.Repo
var sha : str
class Root (did: int, node: anki.decks_pb2.DeckTreeNode, deckd: None, mediad: None, children: list[Deck], fullname: str)
-
Root(did: int, node: anki.decks_pb2.DeckTreeNode, deckd: None, mediad: None, children: list[ki.types.Deck], fullname: str)
Expand source code
@beartype @dataclass(frozen=True) class Root: did: DeckId node: DeckTreeNode deckd: None mediad: None children: List[Deck] fullname: str
Class variables
var children : list[Deck]
var deckd : None
var did : int
var fullname : str
var mediad : None
var node : anki.decks_pb2.DeckTreeNode
class SQLiteLockError (col_file: File, err: sqlite3.DatabaseError)
-
Unspecified run-time error.
Expand source code
class SQLiteLockError(RuntimeError): @beartype def __init__(self, col_file: File, err: sqlite3.DatabaseError): if str(err) == DATABASE_LOCKED_MSG: header = f"fatal: {DATABASE_LOCKED_MSG} (Anki must not be running)." super().__init__(header) return header = "Unexpected SQLite3 error while attempting to acquire lock on file: " header += f"'{col_file}':" msg = f""" A 'sqlite3.DatabaseError' was raised with error message: '{str(err)}'. This may indicate that either the database file at the location specified above is corrupted, or the config file at '.ki/config' is pointing to the wrong location. (The latter may occur in the unlikely event that the collection file in the Anki data directory has been accidentally overwritten.) """ super().__init__(f"{header}\n{errwrap(msg)}")
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class Singleton (*args, **kwargs)
-
UNSAFE: A path consisting of a single component (e.g.
file
, notdir/file
).Expand source code
class Singleton(type(Path())): """UNSAFE: A path consisting of a single component (e.g. `file`, not `dir/file`)."""
Ancestors
- pathlib.PosixPath
- pathlib.Path
- pathlib.PurePosixPath
- pathlib.PurePath
class StrangeExtantPathError (path: pathlib.Path, info: str = '')
-
Unspecified run-time error.
Expand source code
class StrangeExtantPathError(RuntimeError): @beartype def __init__(self, path: Path, info: str = ""): msg = "A normal file or directory was expected, but got a weird pseudofile " msg += "(e.g. a socket, or a device): " msg += f"'{path}'{info.rstrip()}" super().__init__(errwrap(msg))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class Submodule (sm: git.objects.submodule.base.Submodule, sm_repo: git.repo.base.Repo, rel_root: pathlib.Path, branch: str)
-
Submodule(sm: git.objects.submodule.base.Submodule, sm_repo: git.repo.base.Repo, rel_root: pathlib.Path, branch: str)
Expand source code
@beartype @dataclass(frozen=True) class Submodule: sm: git.Submodule sm_repo: git.Repo rel_root: Path branch: str
Class variables
var branch : str
var rel_root : pathlib.Path
var sm : git.objects.submodule.base.Submodule
var sm_repo : git.repo.base.Repo
class TargetExistsError (target: pathlib.Path)
-
Unspecified run-time error.
Expand source code
class TargetExistsError(RuntimeError): @beartype def __init__(self, target: Path): msg = f"fatal: destination path '{target}' already exists and is " msg += "not an empty directory." super().__init__(errwrap(msg))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class Template (name: str, qfmt: str, afmt: str, ord: Optional[int])
-
A typechecked version of
anki.models.TemplateDict
for use within ki.Expand source code
@beartype @dataclass(frozen=True) class Template: """A typechecked version of `anki.models.TemplateDict` for use within ki.""" name: str qfmt: str afmt: str ord: Optional[int]
Class variables
var afmt : str
var name : str
var ord : Optional[int]
var qfmt : str
class UnhealthyNoteWarning (note: anki.notes.Note, health: int)
-
Base class for warning categories.
Expand source code
class UnhealthyNoteWarning(Warning): @beartype def __init__(self, note: Note, health: int): top = f"Note with nid '{note.id}' failed fields check with unknown error code" msg = f""" Anki fields health check code: '{health}' """ super().__init__(f"{top}\n{errwrap(msg)}")
Ancestors
- builtins.Warning
- builtins.Exception
- builtins.BaseException
class UnnamedNotetypeError (nt: dict[str, typing.Any])
-
Unspecified run-time error.
Expand source code
class UnnamedNotetypeError(RuntimeError): @beartype def __init__(self, nt: NotetypeDict): msg = f""" Failed to find 'name' field for a notetype while parsing a '{MODELS_FILE}' file in the current repository (may be contained in a subdirectory): """ super().__init__(errwrap(msg) + "\n" + str(nt))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class UpdatesRejectedError (col_file: File)
-
Unspecified run-time error.
Expand source code
class UpdatesRejectedError(RuntimeError): @beartype def __init__(self, col_file: File): msg = f"Failed to push some commits to '{col_file}'\n{HINT}" super().__init__(errwrap(msg))
Ancestors
- builtins.RuntimeError
- builtins.Exception
- builtins.BaseException
class WrongFieldCountWarning (decknote: DeckNote, names: list[str])
-
Base class for warning categories.
Expand source code
class WrongFieldCountWarning(Warning): @beartype def __init__(self, decknote: DeckNote, names: List[str]): top = f"Warning: Wrong number of fields for model '{decknote.model}'" msg = f""" The notetype '{decknote.model}' takes '{len(names)}' fields, but got '{len(decknote.fields.keys())}' for note with GUID '{decknote.guid}'. """ super().__init__(f"{top}\n{errwrap(msg)}")
Ancestors
- builtins.Warning
- builtins.Exception
- builtins.BaseException