add a bunch of extra hints for mypy

This commit is contained in:
Damien Elmes 2019-12-20 16:34:09 +10:00
parent d8d7e78b6b
commit 12093f2f2e
7 changed files with 46 additions and 51 deletions

View file

@ -8,10 +8,9 @@ from anki.storage import Collection
from anki.utils import intTime, splitFields, joinFields from anki.utils import intTime, splitFields, joinFields
from anki.importing.base import Importer from anki.importing.base import Importer
from anki.lang import _ from anki.lang import _
from typing import Any, Optional from typing import Any, Optional, Dict, Tuple, List
from anki.collection import _Collection from anki.collection import _Collection
from typing import List, Union
GUID = 1 GUID = 1
MID = 2 MID = 2
MOD = 3 MOD = 3
@ -19,15 +18,16 @@ MOD = 3
class Anki2Importer(Importer): class Anki2Importer(Importer):
needMapper = False needMapper = False
deckPrefix = None deckPrefix: Optional[str] = None
allowUpdate = True allowUpdate = True
src: _Collection
dst: _Collection
def __init__(self, col: _Collection, file: str) -> None: def __init__(self, col: _Collection, file: str) -> None:
super().__init__(col, file) super().__init__(col, file)
# set later, defined here for typechecking # set later, defined here for typechecking
self.src = None self._decks: Dict[int,int] = {}
self._decks = {}
self.mustResetLearning = False self.mustResetLearning = False
def run(self, media: None = None) -> None: def run(self, media: None = None) -> None:
@ -79,7 +79,7 @@ class Anki2Importer(Importer):
def _importNotes(self) -> None: def _importNotes(self) -> None:
# build guid -> (id,mod,mid) hash & map of existing note ids # build guid -> (id,mod,mid) hash & map of existing note ids
self._notes = {} self._notes: Dict[str, Tuple[int,int,int]] = {}
existing = {} existing = {}
for id, guid, mod, mid in self.dst.db.execute( for id, guid, mod, mid in self.dst.db.execute(
"select id, guid, mod, mid from notes"): "select id, guid, mod, mid from notes"):
@ -87,10 +87,10 @@ class Anki2Importer(Importer):
existing[id] = True existing[id] = True
# we may need to rewrite the guid if the model schemas don't match, # 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 # 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 # we ignore updates to changed schemas. we need to note the ignored
# guids, so we avoid importing invalid cards # guids, so we avoid importing invalid cards
self._ignoredGuids = {} self._ignoredGuids: Dict[str, bool] = {}
# iterate over source collection # iterate over source collection
add = [] add = []
update = [] update = []
@ -188,7 +188,7 @@ class Anki2Importer(Importer):
# determine if note is a duplicate, and adjust mid and/or guid as required # determine if note is a duplicate, and adjust mid and/or guid as required
# returns true if note should be added # 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] origGuid = note[GUID]
srcMid = note[MID] srcMid = note[MID]
dstMid = self._mid(srcMid) dstMid = self._mid(srcMid)
@ -212,7 +212,7 @@ class Anki2Importer(Importer):
def _prepareModels(self) -> None: def _prepareModels(self) -> None:
"Prepare index of schema hashes." "Prepare index of schema hashes."
self._modelMap = {} self._modelMap: Dict[int, int] = {}
def _mid(self, srcMid: int) -> Any: def _mid(self, srcMid: int) -> Any:
"Return local id for remote MID." "Return local id for remote MID."
@ -302,7 +302,7 @@ class Anki2Importer(Importer):
if self.mustResetLearning: if self.mustResetLearning:
self.src.changeSchedulerVer(2) self.src.changeSchedulerVer(2)
# build map of (guid, ord) -> cid and used id cache # build map of (guid, ord) -> cid and used id cache
self._cards = {} self._cards: Dict[Tuple[str,int], int] = {}
existing = {} existing = {}
for guid, ord, cid in self.dst.db.execute( for guid, ord, cid in self.dst.db.execute(
"select f.guid, c.ord, c.id from cards c, notes f " "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: with open(path, "rb") as f:
return f.read() return f.read()
except (IOError, OSError): except (IOError, OSError):
return return b''
def _srcMediaData(self, fname: str) -> bytes: def _srcMediaData(self, fname: str) -> bytes:
"Data for FNAME in src collection." "Data for FNAME in src collection."
@ -423,8 +423,8 @@ insert or ignore into revlog values (?,?,?,?,?,?,?,?,?)""", revlog)
# the user likely used subdirectories # the user likely used subdirectories
pass pass
def _mungeMedia(self, mid: int, fields: str) -> str: def _mungeMedia(self, mid: int, fieldsStr: str) -> str:
fields = splitFields(fields) fields = splitFields(fieldsStr)
def repl(match): def repl(match):
fname = match.group("fname") fname = match.group("fname")
srcData = self._srcMediaData(fname) srcData = self._srcMediaData(fname)

View file

@ -7,18 +7,13 @@ import unicodedata
import json import json
from anki.utils import tmpfile from anki.utils import tmpfile
from anki.importing.anki2 import Anki2Importer from anki.importing.anki2 import Anki2Importer
from typing import Any from typing import Any, Dict, Optional
from anki.collection import _Collection
class AnkiPackageImporter(Anki2Importer): class AnkiPackageImporter(Anki2Importer):
nameToNum: Dict[str, str]
zip: Optional[zipfile.ZipFile]
def __init__(self, col: _Collection, file: str) -> None: def run(self) -> None: # type: ignore
super().__init__(col, file)
# set later; set here for typechecking
self.nameToNum = {}
self.zip = None
def run(self) -> None:
# extract the deck from the zip file # extract the deck from the zip file
self.zip = z = zipfile.ZipFile(self.file) self.zip = z = zipfile.ZipFile(self.file)
# v2 scheduler? # v2 scheduler?
@ -56,5 +51,5 @@ class AnkiPackageImporter(Anki2Importer):
def _srcMediaData(self, fname: str) -> Any: def _srcMediaData(self, fname: str) -> Any:
if fname in self.nameToNum: 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 return None

View file

@ -3,20 +3,21 @@
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from anki.utils import maxID from anki.utils import maxID
from typing import Any from typing import Any, Optional, List
from anki.collection import _Collection
# Base importer # Base importer
########################################################################## ##########################################################################
from anki.collection import _Collection
class Importer: class Importer:
needMapper = False needMapper = False
needDelimiter = False needDelimiter = False
dst: Optional[_Collection]
def __init__(self, col: _Collection, file: str) -> None: def __init__(self, col: _Collection, file: str) -> None:
self.file = file self.file = file
self.log = [] self.log: List[str] = []
self.col = col self.col = col
self.total = 0 self.total = 0
self.dst = None self.dst = None

View file

@ -7,8 +7,7 @@ import re
from anki.importing.noteimp import NoteImporter, ForeignNote from anki.importing.noteimp import NoteImporter, ForeignNote
from anki.lang import _ from anki.lang import _
from typing import List from typing import List, Optional, Any, TextIO, Union
from anki.collection import _Collection from anki.collection import _Collection
class TextImporter(NoteImporter): class TextImporter(NoteImporter):
@ -19,10 +18,12 @@ class TextImporter(NoteImporter):
def __init__(self, col: _Collection, file: str) -> None: def __init__(self, col: _Collection, file: str) -> None:
NoteImporter.__init__(self, col, file) NoteImporter.__init__(self, col, file)
self.lines = None self.lines = None
self.fileobj = None self.fileobj: Optional[TextIO] = None
self.delimiter = None self.delimiter: Optional[str] = None
self.tagsToAdd = [] self.tagsToAdd: List[str] = []
self.numFields = 0 self.numFields = 0
self.dialect: Optional[Any]
self.data: Optional[Union[str, List[str]]]
def foreignNotes(self) -> List[ForeignNote]: def foreignNotes(self) -> List[ForeignNote]:
self.open() self.open()

View file

@ -12,20 +12,19 @@ from anki.utils import fieldChecksum, guid64, timestampID, \
joinFields, intTime, splitFields joinFields, intTime, splitFields
from anki.importing.base import Importer from anki.importing.base import Importer
from anki.lang import ngettext 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 # Stores a list of fields, tags and deck
###################################################################### ######################################################################
from anki.collection import _Collection
from typing import List, Optional, Union
class ForeignNote: class ForeignNote:
"An temporary object storing fields and attributes." "An temporary object storing fields and attributes."
def __init__(self) -> None: def __init__(self) -> None:
self.fields = [] self.fields: List[str] = []
self.tags = [] self.tags: List[str] = []
self.deck = None self.deck = None
self.cards = {} # map of ord -> card self.cards: Dict[int,ForeignCard] = {} # map of ord -> card
self.fieldsStr = "" self.fieldsStr = ""
class ForeignCard: class ForeignCard:
@ -56,12 +55,12 @@ class NoteImporter(Importer):
needDelimiter = False needDelimiter = False
allowHTML = False allowHTML = False
importMode = 0 importMode = 0
mapping: Optional[List[str]]
def __init__(self, col: _Collection, file: str) -> None: def __init__(self, col: _Collection, file: str) -> None:
Importer.__init__(self, col, file) Importer.__init__(self, col, file)
self.model = col.models.current() self.model = col.models.current()
self.mapping = None self.mapping = None
self._deckMap = {}
self._tagsMapped = False self._tagsMapped = False
def run(self) -> None: def run(self) -> None:
@ -105,14 +104,14 @@ class NoteImporter(Importer):
if f == "_tags": if f == "_tags":
self._tagsMapped = True self._tagsMapped = True
# gather checks for duplicate comparison # gather checks for duplicate comparison
csums = {} csums: Dict[str, List[int]] = {}
for csum, id in self.col.db.execute( for csum, id in self.col.db.execute(
"select csum, id from notes where mid = ?", self.model['id']): "select csum, id from notes where mid = ?", self.model['id']):
if csum in csums: if csum in csums:
csums[csum].append(id) csums[csum].append(id)
else: else:
csums[csum] = [id] csums[csum] = [id]
firsts = {} firsts: Dict[str, bool] = {}
fld0idx = self.mapping.index(self.model['flds'][0]['name']) fld0idx = self.mapping.index(self.model['flds'][0]['name'])
self._fmap = self.col.models.fieldMap(self.model) self._fmap = self.col.models.fieldMap(self.model)
self._nextID = timestampID(self.col.db, "notes") self._nextID = timestampID(self.col.db, "notes")
@ -122,11 +121,11 @@ class NoteImporter(Importer):
updateLogTxt = _("First field matched: %s") updateLogTxt = _("First field matched: %s")
dupeLogTxt = _("Added duplicate with first field: %s") dupeLogTxt = _("Added duplicate with first field: %s")
new = [] new = []
self._ids = [] self._ids: List[int] = []
self._cards = [] self._cards: List[Tuple] = []
self._emptyNotes = False self._emptyNotes = False
dupeCount = 0 dupeCount = 0
dupes = [] dupes: List[str] = []
for n in notes: for n in notes:
for c in range(len(n.fields)): for c in range(len(n.fields)):
if not self.allowHTML: if not self.allowHTML:
@ -228,7 +227,7 @@ content in the text file to the correct fields."""))
self._nextID += 1 self._nextID += 1
self._ids.append(id) self._ids.append(id)
if not self.processFields(n): if not self.processFields(n):
return return None
# note id for card updates later # note id for card updates later
for ord, c in list(n.cards.items()): for ord, c in list(n.cards.items()):
self._cards.append((id, ord, c)) 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]: def updateData(self, n: ForeignNote, id: int, sflds: List[str]) -> Optional[list]:
self._ids.append(id) self._ids.append(id)
if not self.processFields(n, sflds): if not self.processFields(n, sflds):
return return None
if self._tagsMapped: if self._tagsMapped:
self.col.tags.register(n.tags) self.col.tags.register(n.tags)
tags = self.col.tags.join(n.tags) tags = self.col.tags.join(n.tags)

View file

@ -6,7 +6,6 @@ import gzip, math, random, time, html
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from anki.importing.noteimp import NoteImporter, ForeignNote, ForeignCard from anki.importing.noteimp import NoteImporter, ForeignNote, ForeignCard
from anki.stdmodels import addForwardReverse from anki.stdmodels import addForwardReverse
from typing import List
ONE_DAY = 60*60*24 ONE_DAY = 60*60*24
@ -29,7 +28,7 @@ class PaukerImporter(NoteImporter):
'''Pauker is Front/Back''' '''Pauker is Front/Back'''
return 2 return 2
def foreignNotes(self) -> List[ForeignNote]: def foreignNotes(self):
'''Build and return a list of notes.''' '''Build and return a list of notes.'''
notes = [] notes = []
@ -67,7 +66,7 @@ class PaukerImporter(NoteImporter):
return notes return notes
def _learnedCard(self, batch, timestamp) -> ForeignCard: def _learnedCard(self, batch, timestamp):
ivl = math.exp(batch) ivl = math.exp(batch)
now = time.time() now = time.time()
due = ivl - (now - timestamp/1000.0)/ONE_DAY due = ivl - (now - timestamp/1000.0)/ONE_DAY

View file

@ -276,7 +276,7 @@ def _incGuid(guid) -> str:
def joinFields(list: List[str]) -> str: def joinFields(list: List[str]) -> str:
return "\x1f".join(list) return "\x1f".join(list)
def splitFields(string: str) -> Any: def splitFields(string: str) -> List[str]:
return string.split("\x1f") return string.split("\x1f")
# Checksums # Checksums