diff --git a/pylib/anki/db.py b/pylib/anki/db.py index 087dcecee..786ec7b77 100644 --- a/pylib/anki/db.py +++ b/pylib/anki/db.py @@ -5,7 +5,7 @@ import os import time from sqlite3 import Cursor from sqlite3 import dbapi2 as sqlite -from typing import Any, List +from typing import Any, List, Type DBError = sqlite.Error @@ -110,5 +110,5 @@ class DB: def _textFactory(self, data: bytes) -> str: return str(data, errors="ignore") - def cursor(self, factory=Cursor) -> Cursor: + def cursor(self, factory: Type[Cursor] = Cursor) -> Cursor: return self._db.cursor(factory) diff --git a/pylib/anki/decks.py b/pylib/anki/decks.py index 084dc3238..a149adc81 100644 --- a/pylib/anki/decks.py +++ b/pylib/anki/decks.py @@ -446,7 +446,7 @@ class DeckManager: return deck["name"] return _("[no deck]") - def nameOrNone(self, did) -> Any: + def nameOrNone(self, did: int) -> Any: deck = self.get(did, default=False) if deck: return deck["name"] @@ -530,7 +530,7 @@ class DeckManager: self, force_default: bool = True, assume_no_child: bool = False, - default_deck=None, + default_deck: Optional[Dict[str, Any]] = None, ) -> bool: """Whether the default deck should appear in main window, browser side list, filter, deck selection... diff --git a/pylib/anki/errors.py b/pylib/anki/errors.py index f2b9d6056..4bfc9aa24 100644 --- a/pylib/anki/errors.py +++ b/pylib/anki/errors.py @@ -18,7 +18,7 @@ class AnkiError(Exception): class DeckRenameError(Exception): - def __init__(self, description) -> None: + def __init__(self, description: str) -> None: super().__init__() self.description = description diff --git a/pylib/anki/latex.py b/pylib/anki/latex.py index f0df41d4e..9aeac75e9 100644 --- a/pylib/anki/latex.py +++ b/pylib/anki/latex.py @@ -33,14 +33,16 @@ if isMac: os.environ["PATH"] += ":/usr/texbin:/Library/TeX/texbin" -def on_card_did_render(output: TemplateRenderOutput, ctx: TemplateRenderContext): +def on_card_did_render( + output: TemplateRenderOutput, ctx: TemplateRenderContext +) -> None: output.question_text = render_latex( output.question_text, ctx.note_type(), ctx.col() ) output.answer_text = render_latex(output.answer_text, ctx.note_type(), ctx.col()) -def render_latex(html: str, model: NoteType, col: anki.storage._Collection,) -> str: +def render_latex(html: str, model: NoteType, col: anki.storage._Collection) -> str: "Convert embedded latex tags in text to image links." html, err = render_latex_returning_errors(html, model, col) if err: @@ -49,7 +51,10 @@ def render_latex(html: str, model: NoteType, col: anki.storage._Collection,) -> def render_latex_returning_errors( - html: str, model: NoteType, col: anki.storage._Collection, expand_clozes=False + html: str, + model: NoteType, + col: anki.storage._Collection, + expand_clozes: bool = False, ) -> Tuple[str, List[str]]: """Returns (text, errors). diff --git a/pylib/anki/notes.py b/pylib/anki/notes.py index 6d6cf4174..aed366aee 100644 --- a/pylib/anki/notes.py +++ b/pylib/anki/notes.py @@ -63,7 +63,7 @@ class Note: self.mid, self.mod, self.usn, - self.tags, + tags, fields, self.flags, self.data, @@ -74,7 +74,7 @@ from notes where id = ?""", self.id, ) self.fields = splitFields(fields) - self.tags = self.col.tags.split(self.tags) + self.tags = self.col.tags.split(tags) self._model = self.col.models.get(self.mid) self._fmap = self.col.models.fieldMap(self._model) self.scm = self.col.scm diff --git a/pylib/anki/stats.py b/pylib/anki/stats.py index 2f24babe6..cd21d7018 100644 --- a/pylib/anki/stats.py +++ b/pylib/anki/stats.py @@ -6,7 +6,7 @@ from __future__ import annotations import datetime import json import time -from typing import Any, Dict, List, Optional, Tuple +from typing import Any, Dict, List, Optional, Sequence, Tuple, Union import anki from anki.consts import * @@ -17,6 +17,7 @@ from anki.utils import ids2str # Card stats ########################################################################## + PERIOD_MONTH = 0 PERIOD_YEAR = 1 PERIOD_LIFE = 2 @@ -71,15 +72,15 @@ class CardStats: self.txt += "" return self.txt - def addLine(self, k, v) -> None: + def addLine(self, k: str, v: Union[int, str]) -> None: self.txt += self.makeLine(k, v) - def makeLine(self, k, v) -> str: + def makeLine(self, k: str, v: Union[str, int]) -> str: txt = "" txt += "%s%s" % (k, v) return txt - def date(self, tm) -> str: + def date(self, tm: float) -> str: return time.strftime("%Y-%m-%d", time.localtime(tm)) def time(self, tm: float) -> str: @@ -114,7 +115,7 @@ class CollectionStats: self.wholeCollection = False # assumes jquery & plot are available in document - def report(self, type=PERIOD_MONTH) -> str: + def report(self, type: int = PERIOD_MONTH) -> str: # 0=month, 1=year, 2=deck life self.type = type from .statsbg import bg @@ -131,7 +132,7 @@ class CollectionStats: txt += self._section(self.footer()) return "
%s
" % txt - def _section(self, txt) -> str: + def _section(self, txt: str) -> str: return "
%s
" % txt css = """ @@ -212,7 +213,7 @@ from revlog where id > ? """ # Due and cumulative due ###################################################################### - def get_start_end_chunk(self, by="review") -> Tuple[int, Optional[int], int]: + def get_start_end_chunk(self, by: str = "review") -> Tuple[int, Optional[int], int]: start = 0 if self.type == PERIOD_MONTH: end, chunk = 31, 1 @@ -273,7 +274,7 @@ from revlog where id > ? """ txt += self._dueInfo(tot, len(totd) * chunk) return txt - def _dueInfo(self, tot, num) -> str: + def _dueInfo(self, tot: int, num: int) -> str: i: List[str] = [] self._line( i, _("Total"), self.col.tr(TR.STATISTICS_REVIEWS, reviews=tot), @@ -290,7 +291,9 @@ and due = ?""" self._line(i, _("Due tomorrow"), tomorrow) return self._lineTbl(i) - def _due(self, start=None, end=None, chunk=1) -> Any: + def _due( + self, start: Optional[int] = None, end: Optional[int] = None, chunk: int = 1 + ) -> Any: lim = "" if start is not None: lim += " and due-:today >= %d" % start @@ -414,7 +417,13 @@ group by day order by day""" return self._section(txt1) + self._section(txt2) def _ansInfo( - self, totd, studied, first, unit, convHours=False, total=None + self, + totd: List[Tuple[int, float]], + studied: int, + first: int, + unit: str, + convHours: bool = False, + total: Optional[int] = None, ) -> Tuple[str, int]: assert totd tot = totd[-1][1] @@ -460,14 +469,16 @@ group by day order by day""" ) return self._lineTbl(i), int(tot) - def _splitRepData(self, data, spec) -> Tuple[List[dict], List[Tuple[Any, Any]]]: + def _splitRepData( + self, data: List[Tuple[Any, ...]], spec: Sequence[Tuple[int, str, str]], + ) -> Tuple[List[Dict[str, Any]], List[Tuple[Any, Any]]]: sep: Dict[int, Any] = {} totcnt = {} totd: Dict[int, Any] = {} alltot = [] - allcnt = 0 + allcnt: float = 0 for (n, col, lab) in spec: - totcnt[n] = 0 + totcnt[n] = 0.0 totd[n] = [] for row in data: for (n, col, lab) in spec: @@ -497,7 +508,7 @@ group by day order by day""" ) return (ret, alltot) - def _added(self, num=7, chunk=1) -> Any: + def _added(self, num: Optional[int] = 7, chunk: int = 1) -> Any: lims = [] if num is not None: lims.append( @@ -525,7 +536,7 @@ group by day order by day""" chunk=chunk, ) - def _done(self, num=7, chunk=1) -> Any: + def _done(self, num: Optional[int] = 7, chunk: int = 1) -> Any: lims = [] if num is not None: lims.append( @@ -637,7 +648,7 @@ group by day order by day)""" ) return txt + self._lineTbl(i) - def _ivls(self) -> Tuple[list, int]: + def _ivls(self) -> Tuple[List[Any], int]: start, end, chunk = self.get_start_end_chunk() lim = "and grp <= %d" % end if end else "" data = [ @@ -712,7 +723,7 @@ select count(), avg(ivl), max(ivl) from cards where did in %s and queue = {QUEUE txt += self._easeInfo(eases) return txt - def _easeInfo(self, eases) -> str: + def _easeInfo(self, eases: List[Tuple[int, int, int]]) -> str: types = {PERIOD_MONTH: [0, 0], PERIOD_YEAR: [0, 0], PERIOD_LIFE: [0, 0]} for (type, ease, cnt) in eases: if ease == 1: @@ -909,7 +920,9 @@ when you answer "good" on a review.""" ) return txt - def _line(self, i, a, b, bold=True) -> None: + def _line( + self, i: List[str], a: str, b: Union[int, str], bold: bool = True + ) -> None: # T: Symbols separating first and second column in a statistics table. Eg in "Total: 3 reviews". colon = _(":") if bold: @@ -923,7 +936,7 @@ when you answer "good" on a review.""" % (a, colon, b) ) - def _lineTbl(self, i) -> str: + def _lineTbl(self, i: List[str]) -> str: return "" + "".join(i) + "
" def _factors(self) -> Any: @@ -969,7 +982,14 @@ from cards where did in %s""" ###################################################################### def _graph( - self, id, data, conf=None, type="bars", xunit=1, ylabel=_("Cards"), ylabel2="" + self, + id: str, + data: Any, + conf: Optional[Any] = None, + type: str = "bars", + xunit: int = 1, + ylabel: str = _("Cards"), + ylabel2: str = "", ) -> str: if conf is None: conf = {} @@ -1088,10 +1108,10 @@ $(function () { self.col.decks.active() ) - def _title(self, title, subtitle="") -> str: + def _title(self, title: str, subtitle: str = "") -> str: return "

%s

%s" % (title, subtitle) - def _deckAge(self, by) -> int: + def _deckAge(self, by: str) -> int: lim = self._revlogLimit() if lim: lim = " where " + lim @@ -1112,7 +1132,7 @@ $(function () { return None return end * chunk - def _avgDay(self, tot, num, unit) -> str: + def _avgDay(self, tot: float, num: int, unit: str) -> str: vals = [] try: vals.append(_("%(a)0.1f %(b)s/day") % dict(a=tot / float(num), b=unit)) diff --git a/pylib/anki/stdmodels.py b/pylib/anki/stdmodels.py index 3776638ca..0e6985744 100644 --- a/pylib/anki/stdmodels.py +++ b/pylib/anki/stdmodels.py @@ -1,8 +1,9 @@ # Copyright: Ankitects Pty Ltd and contributors # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html -from typing import Any, Callable, List, Tuple, Union +from typing import Any, Callable, List, Optional, Tuple, Union +from anki.collection import _Collection from anki.consts import MODEL_CLOZE from anki.lang import _ from anki.models import NoteType @@ -13,7 +14,7 @@ models: List[Tuple[Union[Callable[[], str], str], Callable[[Any], NoteType]]] = ########################################################################## -def _newBasicModel(col, name=None) -> NoteType: +def _newBasicModel(col: _Collection, name: Optional[str] = None) -> NoteType: mm = col.models m = mm.new(name or _("Basic")) fm = mm.newField(_("Front")) @@ -27,7 +28,7 @@ def _newBasicModel(col, name=None) -> NoteType: return m -def addBasicModel(col) -> NoteType: +def addBasicModel(col: _Collection) -> NoteType: m = _newBasicModel(col) col.models.add(m) return m @@ -39,7 +40,7 @@ models.append((lambda: _("Basic"), addBasicModel)) ########################################################################## -def addBasicTypingModel(col) -> NoteType: +def addBasicTypingModel(col: _Collection) -> NoteType: mm = col.models m = _newBasicModel(col, _("Basic (type in the answer)")) t = m["tmpls"][0] @@ -55,7 +56,7 @@ models.append((lambda: _("Basic (type in the answer)"), addBasicTypingModel)) ########################################################################## -def _newForwardReverse(col, name=None) -> NoteType: +def _newForwardReverse(col: _Collection, name: Optional[str] = None) -> NoteType: mm = col.models m = _newBasicModel(col, name or _("Basic (and reversed card)")) t = mm.newTemplate(_("Card 2")) @@ -65,7 +66,7 @@ def _newForwardReverse(col, name=None) -> NoteType: return m -def addForwardReverse(col) -> NoteType: +def addForwardReverse(col: _Collection) -> NoteType: m = _newForwardReverse(col) col.models.add(m) return m @@ -77,7 +78,7 @@ models.append((lambda: _("Basic (and reversed card)"), addForwardReverse)) ########################################################################## -def addForwardOptionalReverse(col) -> NoteType: +def addForwardOptionalReverse(col: _Collection) -> NoteType: mm = col.models m = _newForwardReverse(col, _("Basic (optional reversed card)")) av = _("Add Reverse") @@ -95,7 +96,7 @@ models.append((lambda: _("Basic (optional reversed card)"), addForwardOptionalRe ########################################################################## -def addClozeModel(col) -> NoteType: +def addClozeModel(col: _Collection) -> NoteType: mm = col.models m = mm.new(_("Cloze")) m["type"] = MODEL_CLOZE diff --git a/pylib/anki/tags.py b/pylib/anki/tags.py index 864c86b11..cfbb9d62e 100644 --- a/pylib/anki/tags.py +++ b/pylib/anki/tags.py @@ -13,7 +13,7 @@ from __future__ import annotations import json import re -from typing import Callable, Dict, List, Tuple +from typing import Callable, Collection, Dict, List, Optional, Tuple import anki # pylint: disable=unused-import from anki import hooks @@ -29,7 +29,7 @@ class TagManager: self.col = col self.tags: Dict[str, int] = {} - def load(self, json_) -> None: + def load(self, json_: str) -> None: self.tags = json.loads(json_) self.changed = False @@ -41,7 +41,7 @@ class TagManager: # Registering and fetching tags ############################################################# - def register(self, tags, usn=None) -> None: + def register(self, tags: Collection[str], usn: Optional[int] = None) -> None: "Given a list of tags, add any missing ones to tag registry." found = False for t in tags: @@ -55,7 +55,7 @@ class TagManager: def all(self) -> List: return list(self.tags.keys()) - def registerNotes(self, nids=None) -> None: + def registerNotes(self, nids: Optional[List[int]] = None) -> None: "Add any missing tags from notes to the tags list." # when called without an argument, the old list is cleared first. if nids: @@ -94,7 +94,7 @@ class TagManager: # Bulk addition/removal from notes ############################################################# - def bulkAdd(self, ids, tags, add=True) -> None: + def bulkAdd(self, ids: List[int], tags: str, add: bool = True) -> None: "Add tags in bulk. TAGS is space-separated." newTags = self.split(tags) if not newTags: @@ -137,23 +137,23 @@ class TagManager: [fix(row) for row in res], ) - def bulkRem(self, ids, tags) -> None: + def bulkRem(self, ids: List[int], tags: str) -> None: self.bulkAdd(ids, tags, False) # String-based utilities ########################################################################## - def split(self, tags) -> List[str]: + def split(self, tags: str) -> List[str]: "Parse a string and return a list of tags." return [t for t in tags.replace("\u3000", " ").split(" ") if t] - def join(self, tags) -> str: + def join(self, tags: List[str]) -> str: "Join tags into a single string, with leading and trailing spaces." if not tags: return "" return " %s " % " ".join(tags) - def addToStr(self, addtags, tags) -> str: + def addToStr(self, addtags: str, tags: str) -> str: "Add tags if they don't exist, and canonify." currentTags = self.split(tags) for tag in self.split(addtags): @@ -161,7 +161,7 @@ class TagManager: currentTags.append(tag) return self.join(self.canonify(currentTags)) - def remFromStr(self, deltags, tags) -> str: + def remFromStr(self, deltags: str, tags: str) -> str: "Delete tags if they exist." def wildcard(pat, str): @@ -183,7 +183,7 @@ class TagManager: # List-based utilities ########################################################################## - def canonify(self, tagList) -> List[str]: + def canonify(self, tagList: List[str]) -> List[str]: "Strip duplicates, adjust case to match existing tags, and sort." strippedTags = [] for t in tagList: @@ -194,7 +194,7 @@ class TagManager: strippedTags.append(s) return sorted(set(strippedTags)) - def inList(self, tag, tags) -> bool: + def inList(self, tag: str, tags: List[str]) -> bool: "True if TAG is in TAGS. Ignore case." return tag.lower() in [t.lower() for t in tags] diff --git a/pylib/anki/template.py b/pylib/anki/template.py index e122b5764..97fde6699 100644 --- a/pylib/anki/template.py +++ b/pylib/anki/template.py @@ -143,7 +143,9 @@ def templates_for_card(card: Card, browser: bool) -> Tuple[str, str]: return q, a # type: ignore -def fields_for_rendering(col: anki.storage._Collection, card: Card, note: Note): +def fields_for_rendering( + col: anki.storage._Collection, card: Card, note: Note +) -> Dict[str, str]: # fields from note fields = dict(note.items())