Merge pull request #467 from alanhdu/monkeytype

Add some type annotations [1/n]
This commit is contained in:
Damien Elmes 2020-02-27 17:25:50 +10:00 committed by GitHub
commit 244326c130
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 82 additions and 54 deletions

View file

@ -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)

View file

@ -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...

View file

@ -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

View file

@ -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).

View file

@ -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

View file

@ -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 += "</table>"
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 = "<tr><td align=left style='padding-right: 3px;'>"
txt += "<b>%s</b></td><td>%s</td></tr>" % (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 "<center>%s</center>" % txt
def _section(self, txt) -> str:
def _section(self, txt: str) -> str:
return "<div class=section>%s</div>" % 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 "<table width=400>" + "".join(i) + "</table>"
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 "<h1>%s</h1>%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))

View file

@ -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

View file

@ -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]

View file

@ -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())