From 12093f2f2e3888665576f56f715d96853f647e92 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 20 Dec 2019 16:34:09 +1000 Subject: [PATCH] add a bunch of extra hints for mypy --- anki/importing/anki2.py | 30 +++++++++++++++--------------- anki/importing/apkg.py | 15 +++++---------- anki/importing/base.py | 7 ++++--- anki/importing/csvfile.py | 11 ++++++----- anki/importing/noteimp.py | 27 +++++++++++++-------------- anki/importing/pauker.py | 5 ++--- anki/utils.py | 2 +- 7 files changed, 46 insertions(+), 51 deletions(-) diff --git a/anki/importing/anki2.py b/anki/importing/anki2.py index 3c12be534..773f00db4 100644 --- a/anki/importing/anki2.py +++ b/anki/importing/anki2.py @@ -8,10 +8,9 @@ from anki.storage import Collection from anki.utils import intTime, splitFields, joinFields from anki.importing.base import Importer from anki.lang import _ -from typing import Any, Optional - +from typing import Any, Optional, Dict, Tuple, List from anki.collection import _Collection -from typing import List, Union + GUID = 1 MID = 2 MOD = 3 @@ -19,15 +18,16 @@ MOD = 3 class Anki2Importer(Importer): needMapper = False - deckPrefix = None + deckPrefix: Optional[str] = None allowUpdate = True + src: _Collection + dst: _Collection def __init__(self, col: _Collection, file: str) -> None: super().__init__(col, file) # set later, defined here for typechecking - self.src = None - self._decks = {} + self._decks: Dict[int,int] = {} self.mustResetLearning = False def run(self, media: None = None) -> None: @@ -79,7 +79,7 @@ class Anki2Importer(Importer): def _importNotes(self) -> None: # build guid -> (id,mod,mid) hash & map of existing note ids - self._notes = {} + self._notes: Dict[str, Tuple[int,int,int]] = {} existing = {} for id, guid, mod, mid in self.dst.db.execute( "select id, guid, mod, mid from notes"): @@ -87,10 +87,10 @@ class Anki2Importer(Importer): existing[id] = True # we may need to rewrite the guid if the model schemas don't match, # so we need to keep track of the changes for the card import stage - self._changedGuids = {} + self._changedGuids: Dict[str, bool] = {} # we ignore updates to changed schemas. we need to note the ignored # guids, so we avoid importing invalid cards - self._ignoredGuids = {} + self._ignoredGuids: Dict[str, bool] = {} # iterate over source collection add = [] update = [] @@ -188,7 +188,7 @@ class Anki2Importer(Importer): # determine if note is a duplicate, and adjust mid and/or guid as required # returns true if note should be added - def _uniquifyNote(self, note: List[Union[int, str]]) -> bool: + def _uniquifyNote(self, note: List[Any]) -> bool: origGuid = note[GUID] srcMid = note[MID] dstMid = self._mid(srcMid) @@ -212,7 +212,7 @@ class Anki2Importer(Importer): def _prepareModels(self) -> None: "Prepare index of schema hashes." - self._modelMap = {} + self._modelMap: Dict[int, int] = {} def _mid(self, srcMid: int) -> Any: "Return local id for remote MID." @@ -302,7 +302,7 @@ class Anki2Importer(Importer): if self.mustResetLearning: self.src.changeSchedulerVer(2) # build map of (guid, ord) -> cid and used id cache - self._cards = {} + self._cards: Dict[Tuple[str,int], int] = {} existing = {} for guid, ord, cid in self.dst.db.execute( "select f.guid, c.ord, c.id from cards c, notes f " @@ -403,7 +403,7 @@ insert or ignore into revlog values (?,?,?,?,?,?,?,?,?)""", revlog) with open(path, "rb") as f: return f.read() except (IOError, OSError): - return + return b'' def _srcMediaData(self, fname: str) -> bytes: "Data for FNAME in src collection." @@ -423,8 +423,8 @@ insert or ignore into revlog values (?,?,?,?,?,?,?,?,?)""", revlog) # the user likely used subdirectories pass - def _mungeMedia(self, mid: int, fields: str) -> str: - fields = splitFields(fields) + def _mungeMedia(self, mid: int, fieldsStr: str) -> str: + fields = splitFields(fieldsStr) def repl(match): fname = match.group("fname") srcData = self._srcMediaData(fname) diff --git a/anki/importing/apkg.py b/anki/importing/apkg.py index 2057a012d..63735bedf 100644 --- a/anki/importing/apkg.py +++ b/anki/importing/apkg.py @@ -7,18 +7,13 @@ import unicodedata import json from anki.utils import tmpfile from anki.importing.anki2 import Anki2Importer -from typing import Any +from typing import Any, Dict, Optional -from anki.collection import _Collection class AnkiPackageImporter(Anki2Importer): + nameToNum: Dict[str, str] + zip: Optional[zipfile.ZipFile] - def __init__(self, col: _Collection, file: str) -> None: - super().__init__(col, file) - # set later; set here for typechecking - self.nameToNum = {} - self.zip = None - - def run(self) -> None: + def run(self) -> None: # type: ignore # extract the deck from the zip file self.zip = z = zipfile.ZipFile(self.file) # v2 scheduler? @@ -56,5 +51,5 @@ class AnkiPackageImporter(Anki2Importer): def _srcMediaData(self, fname: str) -> Any: if fname in self.nameToNum: - return self.zip.read(self.nameToNum[fname]) + return self.zip.read(self.nameToNum[fname]) # pytype: disable=attribute-error return None diff --git a/anki/importing/base.py b/anki/importing/base.py index 7a00641d6..1159aabdd 100644 --- a/anki/importing/base.py +++ b/anki/importing/base.py @@ -3,20 +3,21 @@ # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html from anki.utils import maxID -from typing import Any +from typing import Any, Optional, List +from anki.collection import _Collection # Base importer ########################################################################## -from anki.collection import _Collection class Importer: needMapper = False needDelimiter = False + dst: Optional[_Collection] def __init__(self, col: _Collection, file: str) -> None: self.file = file - self.log = [] + self.log: List[str] = [] self.col = col self.total = 0 self.dst = None diff --git a/anki/importing/csvfile.py b/anki/importing/csvfile.py index 253591ca5..79e8a3bc7 100644 --- a/anki/importing/csvfile.py +++ b/anki/importing/csvfile.py @@ -7,8 +7,7 @@ import re from anki.importing.noteimp import NoteImporter, ForeignNote from anki.lang import _ -from typing import List - +from typing import List, Optional, Any, TextIO, Union from anki.collection import _Collection class TextImporter(NoteImporter): @@ -19,10 +18,12 @@ class TextImporter(NoteImporter): def __init__(self, col: _Collection, file: str) -> None: NoteImporter.__init__(self, col, file) self.lines = None - self.fileobj = None - self.delimiter = None - self.tagsToAdd = [] + self.fileobj: Optional[TextIO] = None + self.delimiter: Optional[str] = None + self.tagsToAdd: List[str] = [] self.numFields = 0 + self.dialect: Optional[Any] + self.data: Optional[Union[str, List[str]]] def foreignNotes(self) -> List[ForeignNote]: self.open() diff --git a/anki/importing/noteimp.py b/anki/importing/noteimp.py index c2314b691..9ea8ecaae 100644 --- a/anki/importing/noteimp.py +++ b/anki/importing/noteimp.py @@ -12,20 +12,19 @@ from anki.utils import fieldChecksum, guid64, timestampID, \ joinFields, intTime, splitFields from anki.importing.base import Importer from anki.lang import ngettext -from typing import Any, List, Optional +from typing import Any, List, Optional, Tuple, Dict, Union +from anki.collection import _Collection # Stores a list of fields, tags and deck ###################################################################### -from anki.collection import _Collection -from typing import List, Optional, Union class ForeignNote: "An temporary object storing fields and attributes." def __init__(self) -> None: - self.fields = [] - self.tags = [] + self.fields: List[str] = [] + self.tags: List[str] = [] self.deck = None - self.cards = {} # map of ord -> card + self.cards: Dict[int,ForeignCard] = {} # map of ord -> card self.fieldsStr = "" class ForeignCard: @@ -56,12 +55,12 @@ class NoteImporter(Importer): needDelimiter = False allowHTML = False importMode = 0 + mapping: Optional[List[str]] def __init__(self, col: _Collection, file: str) -> None: Importer.__init__(self, col, file) self.model = col.models.current() self.mapping = None - self._deckMap = {} self._tagsMapped = False def run(self) -> None: @@ -105,14 +104,14 @@ class NoteImporter(Importer): if f == "_tags": self._tagsMapped = True # gather checks for duplicate comparison - csums = {} + csums: Dict[str, List[int]] = {} for csum, id in self.col.db.execute( "select csum, id from notes where mid = ?", self.model['id']): if csum in csums: csums[csum].append(id) else: csums[csum] = [id] - firsts = {} + firsts: Dict[str, bool] = {} fld0idx = self.mapping.index(self.model['flds'][0]['name']) self._fmap = self.col.models.fieldMap(self.model) self._nextID = timestampID(self.col.db, "notes") @@ -122,11 +121,11 @@ class NoteImporter(Importer): updateLogTxt = _("First field matched: %s") dupeLogTxt = _("Added duplicate with first field: %s") new = [] - self._ids = [] - self._cards = [] + self._ids: List[int] = [] + self._cards: List[Tuple] = [] self._emptyNotes = False dupeCount = 0 - dupes = [] + dupes: List[str] = [] for n in notes: for c in range(len(n.fields)): if not self.allowHTML: @@ -228,7 +227,7 @@ content in the text file to the correct fields.""")) self._nextID += 1 self._ids.append(id) if not self.processFields(n): - return + return None # note id for card updates later for ord, c in list(n.cards.items()): self._cards.append((id, ord, c)) @@ -245,7 +244,7 @@ content in the text file to the correct fields.""")) def updateData(self, n: ForeignNote, id: int, sflds: List[str]) -> Optional[list]: self._ids.append(id) if not self.processFields(n, sflds): - return + return None if self._tagsMapped: self.col.tags.register(n.tags) tags = self.col.tags.join(n.tags) diff --git a/anki/importing/pauker.py b/anki/importing/pauker.py index aeb58671b..d13e4cade 100644 --- a/anki/importing/pauker.py +++ b/anki/importing/pauker.py @@ -6,7 +6,6 @@ import gzip, math, random, time, html import xml.etree.ElementTree as ET from anki.importing.noteimp import NoteImporter, ForeignNote, ForeignCard from anki.stdmodels import addForwardReverse -from typing import List ONE_DAY = 60*60*24 @@ -29,7 +28,7 @@ class PaukerImporter(NoteImporter): '''Pauker is Front/Back''' return 2 - def foreignNotes(self) -> List[ForeignNote]: + def foreignNotes(self): '''Build and return a list of notes.''' notes = [] @@ -67,7 +66,7 @@ class PaukerImporter(NoteImporter): return notes - def _learnedCard(self, batch, timestamp) -> ForeignCard: + def _learnedCard(self, batch, timestamp): ivl = math.exp(batch) now = time.time() due = ivl - (now - timestamp/1000.0)/ONE_DAY diff --git a/anki/utils.py b/anki/utils.py index 7dbdea4c8..4b47fa34d 100644 --- a/anki/utils.py +++ b/anki/utils.py @@ -276,7 +276,7 @@ def _incGuid(guid) -> str: def joinFields(list: List[str]) -> str: return "\x1f".join(list) -def splitFields(string: str) -> Any: +def splitFields(string: str) -> List[str]: return string.split("\x1f") # Checksums