add a bunch of return types

This commit is contained in:
Damien Elmes 2021-02-01 23:28:21 +10:00
parent f15715fb07
commit a56b09b987
41 changed files with 359 additions and 338 deletions

View file

@ -10,7 +10,7 @@ import os
import sys
import tempfile
import traceback
from typing import Any, Callable, Dict, Optional, Union
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
import anki.lang
from anki import version as _version
@ -102,10 +102,10 @@ class DialogManager:
self._dialogs[name][1] = instance
return instance
def markClosed(self, name: str):
def markClosed(self, name: str) -> None:
self._dialogs[name] = [self._dialogs[name][0], None]
def allClosed(self):
def allClosed(self) -> bool:
return not any(x[1] for x in self._dialogs.values())
def closeAll(self, onsuccess: Callable[[], None]) -> Optional[bool]:
@ -119,7 +119,7 @@ class DialogManager:
if not instance:
continue
def callback():
def callback() -> None:
if self.allClosed():
onsuccess()
else:
@ -189,12 +189,12 @@ def setupLangAndBackend(
pass
# add _ and ngettext globals used by legacy code
def fn__(arg):
def fn__(arg) -> None:
print("".join(traceback.format_stack()[-2]))
print("_ global will break in the future; please see anki/lang.py")
return arg
def fn_ngettext(a, b, c):
def fn_ngettext(a, b, c) -> None:
print("".join(traceback.format_stack()[-2]))
print("ngettext global will break in the future; please see anki/lang.py")
return b
@ -244,11 +244,11 @@ class AnkiApp(QApplication):
KEY = "anki" + checksum(getpass.getuser())
TMOUT = 30000
def __init__(self, argv):
def __init__(self, argv) -> None:
QApplication.__init__(self, argv)
self._argv = argv
def secondInstance(self):
def secondInstance(self) -> bool:
# we accept only one command line argument. if it's missing, send
# a blank screen to just raise the existing window
opts, args = parseArgs(self._argv)
@ -267,7 +267,7 @@ class AnkiApp(QApplication):
self._srv.listen(self.KEY)
return False
def sendMsg(self, txt):
def sendMsg(self, txt) -> bool:
sock = QLocalSocket(self)
sock.connectToServer(self.KEY, QIODevice.WriteOnly)
if not sock.waitForConnected(self.TMOUT):
@ -286,7 +286,7 @@ class AnkiApp(QApplication):
sock.disconnectFromServer()
return True
def onRecv(self):
def onRecv(self) -> None:
sock = self._srv.nextPendingConnection()
if not sock.waitForReadyRead(self.TMOUT):
sys.stderr.write(sock.errorString())
@ -298,14 +298,14 @@ class AnkiApp(QApplication):
# OS X file/url handler
##################################################
def event(self, evt):
def event(self, evt) -> bool:
if evt.type() == QEvent.FileOpen:
self.appMsg.emit(evt.file() or "raise") # type: ignore
return True
return QApplication.event(self, evt)
def parseArgs(argv):
def parseArgs(argv) -> Tuple[argparse.Namespace, List[str]]:
"Returns (opts, args)."
# py2app fails to strip this in some instances, then anki dies
# as there's no such profile
@ -330,7 +330,7 @@ def parseArgs(argv):
return parser.parse_known_args(argv[1:])
def setupGL(pm):
def setupGL(pm) -> None:
if isMac:
return
@ -343,7 +343,7 @@ def setupGL(pm):
ctypes.CDLL("libGL.so.1", ctypes.RTLD_GLOBAL)
# catch opengl errors
def msgHandler(category, ctx, msg):
def msgHandler(category, ctx, msg) -> None:
if category == QtDebugMsg:
category = "debug"
elif category == QtInfoMsg:
@ -400,7 +400,7 @@ def setupGL(pm):
PROFILE_CODE = os.environ.get("ANKI_PROFILE_CODE")
def write_profile_results():
def write_profile_results() -> None:
profiler.disable()
profiler.dump_stats("anki.prof")
@ -408,7 +408,7 @@ def write_profile_results():
print("use 'bazel run qt:profile' to explore")
def run():
def run() -> None:
try:
_run()
except Exception as e:
@ -420,7 +420,7 @@ def run():
)
def _run(argv=None, exec=True):
def _run(argv=None, exec=True) -> Optional[AnkiApp]:
"""Start AnkiQt application or reuse an existing instance if one exists.
If the function is invoked with exec=False, the AnkiQt will not enter
@ -441,12 +441,12 @@ def _run(argv=None, exec=True):
if opts.version:
print(f"Anki {appVersion}")
return
return None
elif opts.syncserver:
from anki.syncserver import serve
serve()
return
return None
if PROFILE_CODE:
@ -465,7 +465,7 @@ def _run(argv=None, exec=True):
except AnkiRestart as error:
if error.exitcode:
sys.exit(error.exitcode)
return
return None
except:
# will handle below
traceback.print_exc()
@ -500,7 +500,7 @@ def _run(argv=None, exec=True):
app = AnkiApp(argv)
if app.secondInstance():
# we've signaled the primary instance, so we should close
return
return None
if not pm:
QMessageBox.critical(
@ -508,7 +508,7 @@ def _run(argv=None, exec=True):
tr(TR.QT_MISC_ERROR),
tr(TR.PROFILES_COULD_NOT_CREATE_DATA_FOLDER),
)
return
return None
# disable icons on mac; this must be done before window created
if isMac:
@ -548,7 +548,7 @@ def _run(argv=None, exec=True):
tr(TR.QT_MISC_ERROR),
tr(TR.QT_MISC_NO_TEMP_FOLDER),
)
return
return None
if pmLoadResult.firstTime:
pm.setDefaultLang(lang[0])
@ -590,3 +590,5 @@ def _run(argv=None, exec=True):
if PROFILE_CODE:
write_profile_results()
return None

View file

@ -13,20 +13,20 @@ from aqt.utils import TR, disable_help_button, supportText, tooltip, tr
class ClosableQDialog(QDialog):
def reject(self):
def reject(self) -> None:
aqt.dialogs.markClosed("About")
QDialog.reject(self)
def accept(self):
def accept(self) -> None:
aqt.dialogs.markClosed("About")
QDialog.accept(self)
def closeWithCallback(self, callback):
def closeWithCallback(self, callback) -> None:
self.reject()
callback()
def show(mw):
def show(mw) -> QDialog:
dialog = ClosableQDialog(mw)
disable_help_button(dialog)
mw.setupDialogGC(dialog)
@ -55,7 +55,7 @@ def show(mw):
modified = "mod"
return f"{name} ['{addon.dir_name}', {installed}, '{addon.human_version}', {modified}]"
def onCopy():
def onCopy() -> None:
addmgr = mw.addonManager
active = []
activeids = []

View file

@ -65,7 +65,7 @@ class AddCards(QDialog):
)
self.deckChooser = aqt.deckchooser.DeckChooser(self.mw, self.form.deckArea)
def helpRequested(self):
def helpRequested(self) -> None:
openHelp(HelpPage.ADDING_CARD_AND_NOTE)
def setupButtons(self) -> None:
@ -161,7 +161,7 @@ class AddCards(QDialog):
gui_hooks.add_cards_will_show_history_menu(self, m)
m.exec_(self.historyButton.mapToGlobal(QPoint(0, 0)))
def editHistory(self, nid):
def editHistory(self, nid) -> None:
self.mw.browser_search(SearchTerm(nid=nid))
def addNote(self, note) -> Optional[Note]:
@ -225,7 +225,7 @@ class AddCards(QDialog):
QDialog.reject(self)
def ifCanClose(self, onOk: Callable) -> None:
def afterSave():
def afterSave() -> None:
ok = self.editor.fieldsAreBlank(self.previousNote) or askUser(
tr(TR.ADDING_CLOSE_AND_LOSE_CURRENT_INPUT), defaultno=True
)
@ -234,8 +234,8 @@ class AddCards(QDialog):
self.editor.saveNow(afterSave)
def closeWithCallback(self, cb):
def doClose():
def closeWithCallback(self, cb) -> None:
def doClose() -> None:
self._reject()
cb()

View file

@ -605,7 +605,7 @@ class AddonManager:
def _addon_schema_path(self, dir: str) -> str:
return os.path.join(self.addonsFolder(dir), "config.schema.json")
def _addon_schema(self, dir: str):
def _addon_schema(self, dir: str) -> Any:
path = self._addon_schema_path(dir)
try:
if not os.path.exists(path):

View file

@ -4,7 +4,7 @@
import copy
import json
import re
from typing import Any, Dict, List, Optional
from typing import Any, Dict, List, Match, Optional
import aqt
from anki.cards import Card
@ -140,7 +140,7 @@ class CardLayout(QDialog):
combo.setEnabled(not self._isCloze())
self.ignore_change_signals = False
def _summarizedName(self, idx: int, tmpl: Dict):
def _summarizedName(self, idx: int, tmpl: Dict) -> str:
return "{}: {}: {} -> {}".format(
idx + 1,
tmpl["name"],
@ -284,7 +284,7 @@ class CardLayout(QDialog):
self.fill_fields_from_template()
def on_search_changed(self, text: str):
def on_search_changed(self, text: str) -> None:
editor = self.tform.edit_area
if not editor.find(text):
# try again from top
@ -294,7 +294,7 @@ class CardLayout(QDialog):
if not editor.find(text):
tooltip("No matches found.")
def on_search_next(self):
def on_search_next(self) -> None:
text = self.tform.search_edit.text()
self.on_search_changed(text)
@ -341,7 +341,7 @@ class CardLayout(QDialog):
self.fill_empty_action_toggled = not self.fill_empty_action_toggled
self.on_preview_toggled()
def on_night_mode_action_toggled(self):
def on_night_mode_action_toggled(self) -> None:
self.night_mode_is_enabled = not self.night_mode_is_enabled
self.on_preview_toggled()
@ -520,7 +520,7 @@ class CardLayout(QDialog):
txt = txt.replace("<hr id=answer>", "")
hadHR = origLen != len(txt)
def answerRepl(match):
def answerRepl(match: Match) -> str:
res = self.mw.reviewer.correct("exomple", "an example")
if hadHR:
res = "<hr id=answer>" + res
@ -556,14 +556,15 @@ class CardLayout(QDialog):
# Card operations
######################################################################
def onRemove(self):
def onRemove(self) -> None:
if len(self.templates) < 2:
return showInfo(tr(TR.CARD_TEMPLATES_AT_LEAST_ONE_CARD_TYPE_IS))
showInfo(tr(TR.CARD_TEMPLATES_AT_LEAST_ONE_CARD_TYPE_IS))
return
def get_count():
def get_count() -> int:
return self.mm.template_use_count(self.model["id"], self.ord)
def on_done(fut):
def on_done(fut) -> None:
card_cnt = fut.result()
template = self.current_template()
@ -593,7 +594,7 @@ class CardLayout(QDialog):
self.redraw_everything()
def onRename(self):
def onRename(self) -> None:
template = self.current_template()
name = getOnlyText(tr(TR.ACTIONS_NEW_NAME), default=template["name"]).replace(
'"', ""
@ -604,7 +605,7 @@ class CardLayout(QDialog):
template["name"] = name
self.redraw_everything()
def onReorder(self):
def onReorder(self) -> None:
n = len(self.templates)
template = self.current_template()
current_pos = self.templates.index(template) + 1
@ -629,7 +630,7 @@ class CardLayout(QDialog):
self.ord = new_idx
self.redraw_everything()
def _newCardName(self):
def _newCardName(self) -> str:
n = len(self.templates) + 1
while 1:
name = without_unicode_isolation(tr(TR.CARD_TEMPLATES_CARD, val=n))
@ -638,7 +639,7 @@ class CardLayout(QDialog):
n += 1
return name
def onAddCard(self):
def onAddCard(self) -> None:
cnt = self.mw.col.models.useCount(self.model)
txt = tr(TR.CARD_TEMPLATES_THIS_WILL_CREATE_CARD_PROCEED, count=cnt)
if not askUser(txt):
@ -654,12 +655,12 @@ class CardLayout(QDialog):
self.ord = len(self.templates) - 1
self.redraw_everything()
def onFlip(self):
def onFlip(self) -> None:
old = self.current_template()
self._flipQA(old, old)
self.redraw_everything()
def _flipQA(self, src, dst):
def _flipQA(self, src, dst) -> None:
m = re.match("(?s)(.+)<hr id=answer>(.+)", src["afmt"])
if not m:
showInfo(tr(TR.CARD_TEMPLATES_ANKI_COULDNT_FIND_THE_LINE_BETWEEN))
@ -667,7 +668,6 @@ class CardLayout(QDialog):
self.change_tracker.mark_basic()
dst["afmt"] = "{{FrontSide}}\n\n<hr id=answer>\n\n%s" % src["qfmt"]
dst["qfmt"] = m.group(2).strip()
return True
def onMore(self) -> None:
m = QMenu(self)
@ -728,7 +728,7 @@ class CardLayout(QDialog):
if key in t:
del t[key]
def onTargetDeck(self):
def onTargetDeck(self) -> None:
from aqt.tagedit import TagEdit
t = self.current_template()
@ -760,7 +760,7 @@ class CardLayout(QDialog):
else:
t["did"] = self.col.decks.id(te.text())
def onAddField(self):
def onAddField(self) -> None:
diag = QDialog(self)
form = aqt.forms.addfield.Ui_Dialog()
form.setupUi(diag)
@ -780,7 +780,7 @@ class CardLayout(QDialog):
form.size.value(),
)
def _addField(self, field, font, size):
def _addField(self, field, font, size) -> None:
text = self.tform.edit_area.toPlainText()
text += "\n<div style='font-family: %s; font-size: %spx;'>{{%s}}</div>\n" % (
font,
@ -795,10 +795,10 @@ class CardLayout(QDialog):
######################################################################
def accept(self) -> None:
def save():
def save() -> None:
self.mm.save(self.model)
def on_done(fut):
def on_done(fut) -> None:
try:
fut.result()
except TemplateError as e:
@ -829,5 +829,5 @@ class CardLayout(QDialog):
self.rendered_card = None
self.mw = None
def onHelp(self):
def onHelp(self) -> None:
openHelp(HelpPage.TEMPLATES)

View file

@ -57,7 +57,7 @@ class CustomStudy(QDialog):
typeShow = False
ok = tr(TR.CUSTOM_STUDY_OK)
def plus(num):
def plus(num) -> str:
if num == 1000:
num = "1000+"
return "<b>" + str(num) + "</b>"

View file

@ -9,7 +9,7 @@ from aqt.qt import *
from aqt.utils import showText, tooltip
def on_progress(mw: aqt.main.AnkiQt):
def on_progress(mw: aqt.main.AnkiQt) -> None:
progress = mw.col.latest_progress()
if progress.kind != ProgressKind.DatabaseCheck:
return
@ -24,14 +24,14 @@ def on_progress(mw: aqt.main.AnkiQt):
def check_db(mw: aqt.AnkiQt) -> None:
def on_timer():
def on_timer() -> None:
on_progress(mw)
timer = QTimer(mw)
qconnect(timer.timeout, on_timer)
timer.start(100)
def on_future_done(fut):
def on_future_done(fut) -> None:
timer.stop()
ret, ok = fut.result()

View file

@ -19,7 +19,7 @@ from aqt.utils import TR, askUser, getOnlyText, openLink, shortcut, showWarning,
class DeckBrowserBottomBar:
def __init__(self, deck_browser: DeckBrowser):
def __init__(self, deck_browser: DeckBrowser) -> None:
self.deck_browser = deck_browser
@ -51,14 +51,14 @@ class DeckBrowser:
self.bottom = BottomBar(mw, mw.bottomWeb)
self.scrollPos = QPoint(0, 0)
def show(self):
def show(self) -> None:
av_player.stop_and_clear_queue()
self.web.set_bridge_command(self._linkHandler, self)
self._renderPage()
# redraw top bar for theme change
self.mw.toolbar.redraw()
def refresh(self):
def refresh(self) -> None:
self._renderPage()
# Event handlers
@ -90,7 +90,7 @@ class DeckBrowser:
self._collapse(int(arg))
return False
def _selDeck(self, did):
def _selDeck(self, did) -> None:
self.mw.col.decks.select(did)
self.mw.onOverview()
@ -108,14 +108,14 @@ class DeckBrowser:
</center>
"""
def _renderPage(self, reuse=False):
def _renderPage(self, reuse=False) -> None:
if not reuse:
self._dueTree = self.mw.col.sched.deck_due_tree()
self.__renderPage(None)
return
self.web.evalWithCallback("window.pageYOffset", self.__renderPage)
def __renderPage(self, offset):
def __renderPage(self, offset) -> None:
content = DeckBrowserContent(
tree=self._renderDeckTree(self._dueTree),
stats=self._renderStats(),
@ -137,10 +137,10 @@ class DeckBrowser:
self._scrollToOffset(offset)
gui_hooks.deck_browser_did_render(self)
def _scrollToOffset(self, offset):
def _scrollToOffset(self, offset) -> None:
self.web.eval("$(function() { window.scrollTo(0, %d, 'instant'); });" % offset)
def _renderStats(self):
def _renderStats(self) -> str:
return '<div id="studiedToday"><span>{}</span></div>'.format(
self.mw.col.studied_today(),
)
@ -170,7 +170,7 @@ class DeckBrowser:
due = node.review_count + node.learn_count
def indent():
def indent() -> str:
return "&nbsp;" * 6 * (node.level - 1)
if node.deck_id == ctx.current_deck_id:
@ -202,7 +202,7 @@ class DeckBrowser:
node.name,
)
# due counts
def nonzeroColour(cnt, klass):
def nonzeroColour(cnt: int, klass: str) -> str:
if not cnt:
klass = "zero-count"
return f'<span class="{klass}">{cnt}</span>'
@ -222,7 +222,7 @@ class DeckBrowser:
buf += self._render_deck_node(child, ctx)
return buf
def _topLevelDragRow(self):
def _topLevelDragRow(self) -> str:
return "<tr class='top-level-drag-row'><td colspan='6'>&nbsp;</td></tr>"
# Options
@ -260,7 +260,7 @@ class DeckBrowser:
return
self.show()
def _options(self, did):
def _options(self, did) -> None:
# select the deck first, because the dyn deck conf assumes the deck
# we're editing is the current one
self.mw.col.decks.select(did)
@ -297,10 +297,10 @@ class DeckBrowser:
def _delete(self, did: int) -> None:
if self.ask_delete_deck(did):
def do_delete():
def do_delete() -> None:
return self.mw.col.decks.rem(did, True)
def on_done(fut: Future):
def on_done(fut: Future) -> None:
self.show()
res = fut.result() # Required to check for errors
@ -316,7 +316,7 @@ class DeckBrowser:
["Ctrl+Shift+I", "import", tr(TR.DECKS_IMPORT_FILE)],
]
def _drawButtons(self):
def _drawButtons(self) -> None:
buf = ""
drawLinks = deepcopy(self.drawLinks)
for b in drawLinks:
@ -332,5 +332,5 @@ class DeckBrowser:
web_context=DeckBrowserBottomBar(self),
)
def _onShared(self):
def _onShared(self) -> None:
openLink(aqt.appShared + "decks/")

View file

@ -2,12 +2,13 @@
# -*- coding: utf-8 -*-
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from operator import itemgetter
from typing import Any, Dict
from typing import Any, Dict, Optional
from PyQt5.QtWidgets import QLineEdit
import aqt
from anki.consts import NEW_CARDS_RANDOM
from anki.decks import DeckConfig
from anki.lang import without_unicode_isolation
from aqt import gui_hooks
from aqt.qt import *
@ -28,7 +29,7 @@ from aqt.utils import (
class DeckConf(QDialog):
def __init__(self, mw: aqt.AnkiQt, deck: Dict):
def __init__(self, mw: aqt.AnkiQt, deck: Dict) -> None:
QDialog.__init__(self, mw)
self.mw = mw
self.deck = deck
@ -60,7 +61,7 @@ class DeckConf(QDialog):
self.exec_()
saveGeom(self, "deckconf")
def setupCombos(self):
def setupCombos(self) -> None:
import anki.consts as cs
f = self.form
@ -70,12 +71,12 @@ class DeckConf(QDialog):
# Conf list
######################################################################
def setupConfs(self):
def setupConfs(self) -> None:
qconnect(self.form.dconf.currentIndexChanged, self.onConfChange)
self.conf = None
self.conf: Optional[DeckConfig] = None
self.loadConfs()
def loadConfs(self):
def loadConfs(self) -> None:
current = self.deck["conf"]
self.confList = self.mw.col.decks.allConf()
self.confList.sort(key=itemgetter("name"))
@ -92,7 +93,7 @@ class DeckConf(QDialog):
self._origNewOrder = self.confList[startOn]["new"]["order"]
self.onConfChange(startOn)
def confOpts(self):
def confOpts(self) -> None:
m = QMenu(self.mw)
a = m.addAction(tr(TR.ACTIONS_ADD))
qconnect(a.triggered, self.addGroup)
@ -106,7 +107,7 @@ class DeckConf(QDialog):
a.setEnabled(False)
m.exec_(QCursor.pos())
def onConfChange(self, idx):
def onConfChange(self, idx) -> None:
if self.ignoreConfChange:
return
if self.conf:
@ -159,7 +160,7 @@ class DeckConf(QDialog):
self.saveConf()
self.loadConfs()
def setChildren(self):
def setChildren(self) -> None:
if not askUser(tr(TR.SCHEDULING_SET_ALL_DECKS_BELOW_TO, val=self.deck["name"])):
return
for did in self.childDids:
@ -173,8 +174,8 @@ class DeckConf(QDialog):
# Loading
##################################################
def listToUser(self, l):
def num_to_user(n: Union[int, float]):
def listToUser(self, l) -> str:
def num_to_user(n: Union[int, float]) -> str:
if n == round(n):
return str(int(n))
else:
@ -182,7 +183,7 @@ class DeckConf(QDialog):
return " ".join(map(num_to_user, l))
def parentLimText(self, type="new"):
def parentLimText(self, type="new") -> str:
# top level?
if "::" not in self.deck["name"]:
return ""
@ -196,7 +197,7 @@ class DeckConf(QDialog):
lim = min(x, lim)
return tr(TR.SCHEDULING_PARENT_LIMIT, val=lim)
def loadConf(self):
def loadConf(self) -> None:
self.conf = self.mw.col.decks.confForDid(self.deck["id"])
# new
c = self.conf["new"]
@ -238,7 +239,7 @@ class DeckConf(QDialog):
f.desc.setPlainText(self.deck["desc"])
gui_hooks.deck_conf_did_load_config(self, self.deck, self.conf)
def onRestore(self):
def onRestore(self) -> None:
self.mw.progress.start()
self.mw.col.decks.restoreToDefault(self.conf)
self.mw.progress.finish()
@ -247,7 +248,7 @@ class DeckConf(QDialog):
# New order
##################################################
def onNewOrderChanged(self, new):
def onNewOrderChanged(self, new) -> None:
old = self.conf["new"]["order"]
if old == new:
return
@ -280,7 +281,7 @@ class DeckConf(QDialog):
return
conf[key] = ret
def saveConf(self):
def saveConf(self) -> None:
# new
c = self.conf["new"]
f = self.form
@ -324,10 +325,10 @@ class DeckConf(QDialog):
self.mw.col.decks.save(self.deck)
self.mw.col.decks.save(self.conf)
def reject(self):
def reject(self) -> None:
self.accept()
def accept(self):
def accept(self) -> None:
self.saveConf()
self.mw.reset()
QDialog.accept(self)

View file

@ -145,7 +145,7 @@ class DeckConf(QDialog):
self.mw.col.decks.save(d)
def reject(self):
def reject(self) -> None:
self.ok = False
QDialog.reject(self)
@ -164,7 +164,7 @@ class DeckConf(QDialog):
# Step load/save - fixme: share with std options screen
########################################################
def listToUser(self, l):
def listToUser(self, l) -> str:
return " ".join([str(x) for x in l])
def userToList(self, w, minSize=1) -> Optional[List[Union[float, int]]]:

View file

@ -48,7 +48,7 @@ class EditCurrent(QDialog):
return
self.editor.setNote(n)
def reopen(self, mw):
def reopen(self, mw) -> None:
tooltip("Please finish editing the existing card first.")
self.onReset()
@ -74,8 +74,8 @@ class EditCurrent(QDialog):
aqt.dialogs.markClosed("EditCurrent")
QDialog.reject(self)
def closeWithCallback(self, onsuccess):
def callback():
def closeWithCallback(self, onsuccess) -> None:
def callback() -> None:
self._saveAndClose()
onsuccess()

View file

@ -15,7 +15,7 @@ from aqt.utils import TR, disable_help_button, restoreGeom, saveGeom, tooltip, t
def show_empty_cards(mw: aqt.main.AnkiQt) -> None:
mw.progress.start()
def on_done(fut):
def on_done(fut) -> None:
mw.progress.finish()
report: EmptyCardsReport = fut.result()
if not report.notes:
@ -54,7 +54,7 @@ class EmptyCardsDialog(QDialog):
style = "<style>.allempty { color: red; }</style>"
self.form.webview.stdHtml(style + html, context=self)
def on_finished(code):
def on_finished(code) -> None:
saveGeom(self, "emptycards")
qconnect(self.finished, on_finished)
@ -65,16 +65,16 @@ class EmptyCardsDialog(QDialog):
self._delete_button.setAutoDefault(False)
self._delete_button.clicked.connect(self._on_delete)
def _on_note_link_clicked(self, link):
def _on_note_link_clicked(self, link) -> None:
self.mw.browser_search(link)
def _on_delete(self) -> None:
self.mw.progress.start()
def delete():
def delete() -> int:
return self._delete_cards(self.form.keep_notes.isChecked())
def on_done(fut):
def on_done(fut) -> None:
self.mw.progress.finish()
try:
count = fut.result()

View file

@ -16,7 +16,7 @@ from aqt.utils import TR, showText, showWarning, supportText, tr
if not os.environ.get("DEBUG"):
def excepthook(etype, val, tb):
def excepthook(etype, val, tb) -> None:
sys.stderr.write(
"Caught exception:\n%s\n"
% ("".join(traceback.format_exception(etype, val, tb)))
@ -65,7 +65,7 @@ class ErrorHandler(QObject):
self.timer.setSingleShot(True)
self.timer.start()
def tempFolderMsg(self):
def tempFolderMsg(self) -> str:
return tr(TR.QT_MISC_UNABLE_TO_ACCESS_ANKI_MEDIA_FOLDER)
def onTimeout(self) -> None:
@ -105,7 +105,7 @@ class ErrorHandler(QObject):
txt = txt + "<div style='white-space: pre-wrap'>" + error + "</div>"
showText(txt, type="html", copyBtn=True)
def _addonText(self, error):
def _addonText(self, error: str) -> str:
matches = re.findall(r"addons21/(.*?)/", error)
if not matches:
return ""

View file

@ -42,7 +42,7 @@ class ExportDialog(QDialog):
self.setup(did)
self.exec_()
def setup(self, did: Optional[int]):
def setup(self, did: Optional[int]) -> None:
self.exporters = exporters(self.col)
# if a deck specified, start with .apkg type selected
idx = 0
@ -155,17 +155,17 @@ class ExportDialog(QDialog):
os.unlink(file)
# progress handler
def exported_media(cnt):
def exported_media(cnt) -> None:
self.mw.taskman.run_on_main(
lambda: self.mw.progress.update(
label=tr(TR.EXPORTING_EXPORTED_MEDIA_FILE, count=cnt)
)
)
def do_export():
def do_export() -> None:
self.exporter.exportInto(file)
def on_done(future: Future):
def on_done(future: Future) -> None:
self.mw.progress.finish()
hooks.media_files_did_export.remove(exported_media)
# raises if exporter failed

View file

@ -23,7 +23,7 @@ from aqt.utils import (
class FieldDialog(QDialog):
def __init__(self, mw: AnkiQt, nt: NoteType, parent=None):
def __init__(self, mw: AnkiQt, nt: NoteType, parent=None) -> None:
QDialog.__init__(self, parent or mw)
self.mw = mw
self.col = self.mw.col
@ -68,7 +68,7 @@ class FieldDialog(QDialog):
qconnect(f.sortField.clicked, self.onSortField)
qconnect(f.buttonBox.helpRequested, self.onHelp)
def onDrop(self, ev):
def onDrop(self, ev) -> None:
fieldList = self.form.fieldList
indicatorPos = fieldList.dropIndicatorPosition()
dropPos = fieldList.indexAt(ev.pos()).row()
@ -113,7 +113,7 @@ class FieldDialog(QDialog):
return None
return txt
def onRename(self):
def onRename(self) -> None:
idx = self.currentIdx
f = self.model["flds"][idx]
name = self._uniqueName(tr(TR.ACTIONS_NEW_NAME), self.currentIdx, f["name"])
@ -141,9 +141,10 @@ class FieldDialog(QDialog):
self.fillFields()
self.form.fieldList.setCurrentRow(len(self.model["flds"]) - 1)
def onDelete(self):
def onDelete(self) -> None:
if len(self.model["flds"]) < 2:
return showWarning(tr(TR.FIELDS_NOTES_REQUIRE_AT_LEAST_ONE_FIELD))
showWarning(tr(TR.FIELDS_NOTES_REQUIRE_AT_LEAST_ONE_FIELD))
return
count = self.mm.useCount(self.model)
c = tr(TR.BROWSING_NOTE_COUNT, count=count)
if not askUser(tr(TR.FIELDS_DELETE_FIELD_FROM, val=c)):
@ -157,7 +158,7 @@ class FieldDialog(QDialog):
self.fillFields()
self.form.fieldList.setCurrentRow(0)
def onPosition(self, delta=-1):
def onPosition(self, delta=-1) -> None:
idx = self.currentIdx
l = len(self.model["flds"])
txt = getOnlyText(tr(TR.FIELDS_NEW_POSITION_1, val=l), default=str(idx + 1))
@ -171,16 +172,16 @@ class FieldDialog(QDialog):
return
self.moveField(pos)
def onSortField(self):
def onSortField(self) -> None:
if not self.change_tracker.mark_schema():
return False
return
# don't allow user to disable; it makes no sense
self.form.sortField.setChecked(True)
self.mm.set_sort_index(self.model, self.form.fieldList.currentRow())
def moveField(self, pos):
def moveField(self, pos) -> None:
if not self.change_tracker.mark_schema():
return False
return
self.saveField()
f = self.model["flds"][self.currentIdx]
self.mm.reposition_field(self.model, f, pos - 1)
@ -231,10 +232,10 @@ class FieldDialog(QDialog):
def accept(self) -> None:
self.saveField()
def save():
def save() -> None:
self.mm.save(self.model)
def on_done(fut):
def on_done(fut) -> None:
try:
fut.result()
except TemplateError as e:
@ -247,5 +248,5 @@ class FieldDialog(QDialog):
self.mw.taskman.with_progress(save, on_done, self)
def onHelp(self):
def onHelp(self) -> None:
openHelp(HelpPage.CUSTOMIZING_FIELDS)

View file

@ -35,7 +35,7 @@ from aqt.utils import (
class ChangeMap(QDialog):
def __init__(self, mw: AnkiQt, model, current):
def __init__(self, mw: AnkiQt, model, current) -> None:
QDialog.__init__(self, mw, Qt.Window)
self.mw = mw
self.model = model
@ -60,11 +60,11 @@ class ChangeMap(QDialog):
self.frm.fields.setCurrentRow(n + 1)
self.field: Optional[str] = None
def getField(self):
def getField(self) -> str:
self.exec_()
return self.field
def accept(self):
def accept(self) -> None:
row = self.frm.fields.currentRow()
if row < len(self.model["flds"]):
self.field = self.model["flds"][row]["name"]
@ -74,7 +74,7 @@ class ChangeMap(QDialog):
self.field = None
QDialog.accept(self)
def reject(self):
def reject(self) -> None:
self.accept()
@ -119,7 +119,7 @@ class ImportDialog(QDialog):
self.importer.initMapping()
self.showMapping()
def onDelimiter(self):
def onDelimiter(self) -> None:
str = (
getOnlyText(
tr(TR.IMPORTING_BY_DEFAULT_ANKI_WILL_DETECT_THE),
@ -136,7 +136,7 @@ class ImportDialog(QDialog):
return
self.hideMapping()
def updateDelim():
def updateDelim() -> None:
self.importer.delimiter = str
self.importer.updateDelimiter()
@ -183,7 +183,7 @@ class ImportDialog(QDialog):
self.mw.progress.start()
self.mw.checkpoint(tr(TR.ACTIONS_IMPORT))
def on_done(future: Future):
def on_done(future: Future) -> None:
self.mw.progress.finish()
try:
@ -221,7 +221,7 @@ class ImportDialog(QDialog):
self.mapbox.setContentsMargins(0, 0, 0, 0)
self.mapwidget: Optional[QWidget] = None
def hideMapping(self):
def hideMapping(self) -> None:
self.frm.mappingGroup.hide()
def showMapping(
@ -258,7 +258,7 @@ class ImportDialog(QDialog):
self.grid.addWidget(button, num, 2)
qconnect(button.clicked, lambda _, s=self, n=num: s.changeMappingNum(n))
def changeMappingNum(self, n):
def changeMappingNum(self, n) -> None:
f = ChangeMap(self.mw, self.importer.model, self.mapping[n]).getField()
try:
# make sure we don't have it twice
@ -270,7 +270,7 @@ class ImportDialog(QDialog):
if getattr(self.importer, "delimiter", False):
self.savedDelimiter = self.importer.delimiter
def updateDelim():
def updateDelim() -> None:
self.importer.delimiter = self.savedDelimiter
self.showMapping(hook=updateDelim, keepMapping=True)
@ -283,17 +283,17 @@ class ImportDialog(QDialog):
gui_hooks.current_note_type_did_change.remove(self.modelChanged)
QDialog.reject(self)
def helpRequested(self):
def helpRequested(self) -> None:
openHelp(HelpPage.IMPORTING)
def importModeChanged(self, newImportMode):
def importModeChanged(self, newImportMode) -> None:
if newImportMode == 0:
self.frm.tagModified.setEnabled(True)
else:
self.frm.tagModified.setEnabled(False)
def showUnicodeWarning():
def showUnicodeWarning() -> None:
"""Shorthand to show a standard warning."""
showWarning(tr(TR.IMPORTING_SELECTED_FILE_WAS_NOT_IN_UTF8))
@ -374,7 +374,7 @@ def importFile(mw: AnkiQt, file: str) -> None:
# importing non-colpkg files
mw.progress.start(immediate=True)
def on_done(future: Future):
def on_done(future: Future) -> None:
mw.progress.finish()
try:
future.result()
@ -405,7 +405,7 @@ def importFile(mw: AnkiQt, file: str) -> None:
mw.taskman.run_in_background(importer.run, on_done)
def invalidZipMsg():
def invalidZipMsg() -> str:
return tr(TR.IMPORTING_THIS_FILE_DOES_NOT_APPEAR_TO)
@ -430,14 +430,14 @@ def setupApkgImport(mw: AnkiQt, importer: AnkiPackageImporter) -> bool:
return False
def replaceWithApkg(mw, file, backup):
def replaceWithApkg(mw, file, backup) -> None:
mw.unloadCollection(lambda: _replaceWithApkg(mw, file, backup))
def _replaceWithApkg(mw, filename, backup):
def _replaceWithApkg(mw, filename, backup) -> None:
mw.progress.start(immediate=True)
def do_import():
def do_import() -> None:
z = zipfile.ZipFile(filename)
# v2 scheduler?
@ -472,7 +472,7 @@ def _replaceWithApkg(mw, filename, backup):
z.close()
def on_done(future: Future):
def on_done(future: Future) -> None:
mw.progress.finish()
try:

View file

@ -6,7 +6,7 @@
Legacy support
"""
from typing import List
from typing import Any, List
import anki
import aqt
@ -31,7 +31,14 @@ def stripSounds(text) -> str:
return aqt.mw.col.media.strip_av_tags(text)
def fmtTimeSpan(time, pad=0, point=0, short=False, inTime=False, unit=99):
def fmtTimeSpan(
time: Any,
pad: Any = 0,
point: Any = 0,
short: Any = False,
inTime: Any = False,
unit: Any = 99,
) -> Any:
print("fmtTimeSpan() has become col.format_timespan()")
return aqt.mw.col.format_timespan(time)

View file

@ -87,7 +87,7 @@ class ResetReason(enum.Enum):
class ResetRequired:
def __init__(self, mw: AnkiQt):
def __init__(self, mw: AnkiQt) -> None:
self.mw = mw
@ -139,7 +139,7 @@ class AnkiQt(QMainWindow):
else:
fn = self.setupProfile
def on_window_init():
def on_window_init() -> None:
fn()
gui_hooks.main_window_did_init()
@ -175,7 +175,7 @@ class AnkiQt(QMainWindow):
"Actions that are deferred until after add-on loading."
self.toolbar.draw()
def setupProfileAfterWebviewsLoaded(self):
def setupProfileAfterWebviewsLoaded(self) -> None:
for w in (self.web, self.bottomWeb):
if not w._domDone:
self.progress.timer(
@ -206,7 +206,7 @@ class AnkiQt(QMainWindow):
self.onClose.emit() # type: ignore
evt.accept()
def closeWithoutQuitting(self):
def closeWithoutQuitting(self) -> None:
self.closeFires = False
self.close()
self.closeFires = True
@ -275,9 +275,10 @@ class AnkiQt(QMainWindow):
name = self.pm.profiles()[n]
self.pm.load(name)
def openProfile(self):
def openProfile(self) -> None:
name = self.pm.profiles()[self.profileForm.profiles.currentRow()]
return self.pm.load(name)
self.pm.load(name)
return
def onOpenProfile(self) -> None:
self.profileDiag.hide()
@ -288,34 +289,37 @@ class AnkiQt(QMainWindow):
def profileNameOk(self, name: str) -> bool:
return not checkInvalidFilename(name) and name != "addons21"
def onAddProfile(self):
def onAddProfile(self) -> None:
name = getOnlyText(tr(TR.ACTIONS_NAME)).strip()
if name:
if name in self.pm.profiles():
return showWarning(tr(TR.QT_MISC_NAME_EXISTS))
showWarning(tr(TR.QT_MISC_NAME_EXISTS))
return
if not self.profileNameOk(name):
return
self.pm.create(name)
self.pm.name = name
self.refreshProfilesList()
def onRenameProfile(self):
def onRenameProfile(self) -> None:
name = getOnlyText(tr(TR.ACTIONS_NEW_NAME), default=self.pm.name).strip()
if not name:
return
if name == self.pm.name:
return
if name in self.pm.profiles():
return showWarning(tr(TR.QT_MISC_NAME_EXISTS))
showWarning(tr(TR.QT_MISC_NAME_EXISTS))
return
if not self.profileNameOk(name):
return
self.pm.rename(name)
self.refreshProfilesList()
def onRemProfile(self):
def onRemProfile(self) -> None:
profs = self.pm.profiles()
if len(profs) < 2:
return showWarning(tr(TR.QT_MISC_THERE_MUST_BE_AT_LEAST_ONE))
showWarning(tr(TR.QT_MISC_THERE_MUST_BE_AT_LEAST_ONE))
return
# sure?
if not askUser(
tr(TR.QT_MISC_ALL_CARDS_NOTES_AND_MEDIA_FOR),
@ -326,7 +330,7 @@ class AnkiQt(QMainWindow):
self.pm.remove(self.pm.name)
self.refreshProfilesList()
def onOpenBackup(self):
def onOpenBackup(self) -> None:
if not askUser(
tr(TR.QT_MISC_REPLACE_YOUR_COLLECTION_WITH_AN_EARLIER),
msgfunc=QMessageBox.warning,
@ -334,7 +338,7 @@ class AnkiQt(QMainWindow):
):
return
def doOpen(path):
def doOpen(path) -> None:
self._openBackup(path)
getFile(
@ -345,7 +349,7 @@ class AnkiQt(QMainWindow):
dir=self.pm.backupFolder(),
)
def _openBackup(self, path):
def _openBackup(self, path) -> None:
try:
# move the existing collection to the trash, as it may not open
self.pm.trashCollection()
@ -360,14 +364,14 @@ class AnkiQt(QMainWindow):
self.onOpenProfile()
def _on_downgrade(self):
def _on_downgrade(self) -> None:
self.progress.start()
profiles = self.pm.profiles()
def downgrade():
def downgrade() -> List[str]:
return self.pm.downgrade(profiles)
def on_done(future):
def on_done(future) -> None:
self.progress.finish()
problems = future.result()
if not problems:
@ -409,7 +413,7 @@ class AnkiQt(QMainWindow):
self.pendingImport = None
gui_hooks.profile_did_open()
def _onsuccess():
def _onsuccess() -> None:
self._refresh_after_sync()
if onsuccess:
onsuccess()
@ -417,7 +421,7 @@ class AnkiQt(QMainWindow):
self.maybe_auto_sync_on_open_close(_onsuccess)
def unloadProfile(self, onsuccess: Callable) -> None:
def callback():
def callback() -> None:
self._unloadProfile()
onsuccess()
@ -447,7 +451,7 @@ class AnkiQt(QMainWindow):
def unloadProfileAndExit(self) -> None:
self.unloadProfile(self.cleanupAndExit)
def unloadProfileAndShowProfileManager(self):
def unloadProfileAndShowProfileManager(self) -> None:
self.unloadProfile(self.showProfileManager)
def cleanupAndExit(self) -> None:
@ -520,14 +524,14 @@ class AnkiQt(QMainWindow):
self.col.reopen()
def unloadCollection(self, onsuccess: Callable) -> None:
def after_media_sync():
def after_media_sync() -> None:
self._unloadCollection()
onsuccess()
def after_sync():
def after_sync() -> None:
self.media_syncer.show_diag_until_finished(after_media_sync)
def before_sync():
def before_sync() -> None:
self.setEnabled(False)
self.maybe_auto_sync_on_open_close(after_sync)
@ -565,7 +569,7 @@ class AnkiQt(QMainWindow):
##########################################################################
class BackupThread(Thread):
def __init__(self, path, data):
def __init__(self, path, data) -> None:
Thread.__init__(self)
self.path = path
self.data = data
@ -574,7 +578,7 @@ class AnkiQt(QMainWindow):
with open(self.path, "wb") as file:
pass
def run(self):
def run(self) -> None:
z = zipfile.ZipFile(self.path, "w", zipfile.ZIP_DEFLATED)
z.writestr("collection.anki2", self.data)
z.writestr("media", "{}")
@ -700,7 +704,7 @@ class AnkiQt(QMainWindow):
self.state = self.returnState
self.reset()
def delayedMaybeReset(self):
def delayedMaybeReset(self) -> None:
# if we redraw the page in a button click event it will often crash on
# windows
self.progress.timer(100, self.maybeReset, False)
@ -801,9 +805,9 @@ title="%s" %s>%s</button>""" % (
signal.signal(signal.SIGINT, self.onUnixSignal)
signal.signal(signal.SIGTERM, self.onUnixSignal)
def onUnixSignal(self, signum, frame):
def onUnixSignal(self, signum, frame) -> None:
# schedule a rollback & quit
def quit():
def quit() -> None:
self.col.db.rollback()
self.close()
@ -888,13 +892,13 @@ title="%s" %s>%s</button>""" % (
def _refresh_after_sync(self) -> None:
self.toolbar.redraw()
def _sync_collection_and_media(self, after_sync: Callable[[], None]):
def _sync_collection_and_media(self, after_sync: Callable[[], None]) -> None:
"Caller should ensure auth available."
# start media sync if not already running
if not self.media_syncer.is_syncing():
self.media_syncer.start()
def on_collection_sync_finished():
def on_collection_sync_finished() -> None:
self.col.clearUndo()
self.col.models._clear_cache()
gui_hooks.sync_did_finish()
@ -927,7 +931,7 @@ title="%s" %s>%s</button>""" % (
)
# legacy
def _sync(self):
def _sync(self) -> None:
pass
onSync = on_sync_button_clicked
@ -1057,7 +1061,7 @@ title="%s" %s>%s</button>""" % (
def onEditCurrent(self) -> None:
aqt.dialogs.open("EditCurrent", self)
def onDeckConf(self, deck=None):
def onDeckConf(self, deck=None) -> None:
if not deck:
deck = self.col.decks.current()
if deck["dyn"]:
@ -1069,7 +1073,7 @@ title="%s" %s>%s</button>""" % (
aqt.deckconf.DeckConf(self, deck)
def onOverview(self):
def onOverview(self) -> None:
self.col.reset()
self.moveToState("overview")
@ -1083,7 +1087,7 @@ title="%s" %s>%s</button>""" % (
else:
aqt.dialogs.open("NewDeckStats", self)
def onPrefs(self):
def onPrefs(self) -> None:
aqt.dialogs.open("Preferences", self)
def onNoteTypes(self) -> None:
@ -1091,13 +1095,13 @@ title="%s" %s>%s</button>""" % (
aqt.models.Models(self, self, fromMain=True)
def onAbout(self):
def onAbout(self) -> None:
aqt.dialogs.open("About", self)
def onDonate(self):
def onDonate(self) -> None:
openLink(aqt.appDonate)
def onDocumentation(self):
def onDocumentation(self) -> None:
openHelp(HelpPage.INDEX)
# Importing & exporting
@ -1126,7 +1130,7 @@ title="%s" %s>%s</button>""" % (
# Installing add-ons from CLI / mimetype handler
##########################################################################
def installAddon(self, path: str, startup: bool = False):
def installAddon(self, path: str, startup: bool = False) -> None:
from aqt.addons import installAddonPackages
installAddonPackages(
@ -1201,14 +1205,14 @@ title="%s" %s>%s</button>""" % (
qconnect(self.autoUpdate.clockIsOff, self.clockIsOff)
self.autoUpdate.start()
def newVerAvail(self, ver):
def newVerAvail(self, ver) -> None:
if self.pm.meta.get("suppressUpdate", None) != ver:
aqt.update.askAndUpdate(self, ver)
def newMsg(self, data):
def newMsg(self, data) -> None:
aqt.update.showMessages(self, data)
def clockIsOff(self, diff):
def clockIsOff(self, diff) -> None:
if devMode:
print("clock is off; ignoring")
return
@ -1229,7 +1233,7 @@ title="%s" %s>%s</button>""" % (
# SIGINT/SIGTERM is processed without a long delay
self.progress.timer(1000, lambda: None, True, False)
def onRefreshTimer(self):
def onRefreshTimer(self) -> None:
if self.state == "deckBrowser":
self.deckBrowser.refresh()
elif self.state == "overview":
@ -1256,7 +1260,7 @@ title="%s" %s>%s</button>""" % (
self._activeWindowOnPlay: Optional[QWidget] = None
def onOdueInvalid(self):
def onOdueInvalid(self) -> None:
showWarning(tr(TR.QT_MISC_INVALID_PROPERTY_FOUND_ON_CARD_PLEASE))
def _isVideo(self, tag: AVTag) -> bool:
@ -1343,11 +1347,11 @@ title="%s" %s>%s</button>""" % (
# Debugging
######################################################################
def onDebug(self):
def onDebug(self) -> None:
frm = self.debug_diag_form = aqt.forms.debug.Ui_Dialog()
class DebugDialog(QDialog):
def reject(self):
def reject(self) -> None:
super().reject()
saveSplitter(frm.splitter, "DebugConsoleWindow")
saveGeom(self, "DebugConsoleWindow")
@ -1394,7 +1398,7 @@ title="%s" %s>%s</button>""" % (
mw = self
class Stream:
def write(self, data):
def write(self, data) -> None:
mw._output += data
if on:
@ -1445,7 +1449,7 @@ title="%s" %s>%s</button>""" % (
self._card_repr(card)
return card
def onDebugPrint(self, frm):
def onDebugPrint(self, frm) -> None:
cursor = frm.text.textCursor()
position = cursor.position()
cursor.select(QTextCursor.LineUnderCursor)
@ -1458,7 +1462,7 @@ title="%s" %s>%s</button>""" % (
frm.text.setTextCursor(cursor)
self.onDebugRet(frm)
def onDebugRet(self, frm):
def onDebugRet(self, frm) -> None:
import pprint
import traceback

View file

@ -136,7 +136,7 @@ class MediaChecker:
diag.exec_()
saveGeom(diag, "checkmediadb")
def _on_render_latex(self):
def _on_render_latex(self) -> None:
self.progress_dialog = self.mw.progress.start()
try:
out = self.mw.col.media.render_all_latex(self._on_render_latex_progress)
@ -160,7 +160,7 @@ class MediaChecker:
self.mw.progress.update(tr(TR.MEDIA_CHECK_CHECKED, count=count))
return True
def _on_trash_files(self, fnames: Sequence[str]):
def _on_trash_files(self, fnames: Sequence[str]) -> None:
if not askUser(tr(TR.MEDIA_CHECK_DELETE_UNUSED_CONFIRM)):
return
@ -183,14 +183,14 @@ class MediaChecker:
tooltip(tr(TR.MEDIA_CHECK_DELETE_UNUSED_COMPLETE, count=total))
def _on_empty_trash(self):
def _on_empty_trash(self) -> None:
self.progress_dialog = self.mw.progress.start()
self._set_progress_enabled(True)
def empty_trash():
def empty_trash() -> None:
self.mw.col.media.empty_trash()
def on_done(fut: Future):
def on_done(fut: Future) -> None:
self.mw.progress.finish()
self._set_progress_enabled(False)
# check for errors
@ -200,14 +200,14 @@ class MediaChecker:
self.mw.taskman.run_in_background(empty_trash, on_done)
def _on_restore_trash(self):
def _on_restore_trash(self) -> None:
self.progress_dialog = self.mw.progress.start()
self._set_progress_enabled(True)
def restore_trash():
def restore_trash() -> None:
self.mw.col.media.restore_trash()
def on_done(fut: Future):
def on_done(fut: Future) -> None:
self.mw.progress.finish()
self._set_progress_enabled(False)
# check for errors

View file

@ -12,6 +12,7 @@ import threading
import time
import traceback
from http import HTTPStatus
from typing import Tuple
import flask
import flask_cors # type: ignore
@ -52,11 +53,11 @@ class MediaServer(threading.Thread):
_ready = threading.Event()
daemon = True
def __init__(self, mw: aqt.main.AnkiQt, *args, **kwargs):
def __init__(self, mw: aqt.main.AnkiQt, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
self.is_shutdown = False
def run(self):
def run(self) -> None:
try:
if devMode:
# idempotent if logging has already been set up
@ -97,7 +98,7 @@ class MediaServer(threading.Thread):
@app.route("/<path:pathin>", methods=["GET", "POST"])
def allroutes(pathin):
def allroutes(pathin) -> Response:
try:
directory, path = _redirectWebExports(pathin)
except TypeError:
@ -171,7 +172,7 @@ def allroutes(pathin):
)
def _redirectWebExports(path):
def _redirectWebExports(path) -> Tuple[str, str]:
# catch /_anki references and rewrite them to web export folder
targetPath = "_anki/"
if path.startswith(targetPath):

View file

@ -28,14 +28,14 @@ class LogEntryWithTime:
class MediaSyncer:
def __init__(self, mw: aqt.main.AnkiQt):
def __init__(self, mw: aqt.main.AnkiQt) -> None:
self.mw = mw
self._syncing: bool = False
self._log: List[LogEntryWithTime] = []
self._progress_timer: Optional[QTimer] = None
gui_hooks.media_sync_did_start_or_stop.append(self._on_start_stop)
def _on_progress(self):
def _on_progress(self) -> None:
progress = self.mw.col.latest_progress()
if progress.kind != ProgressKind.MediaSync:
return
@ -88,7 +88,7 @@ class MediaSyncer:
else:
self._log_and_notify(tr(TR.SYNC_MEDIA_COMPLETE))
def _handle_sync_error(self, exc: BaseException):
def _handle_sync_error(self, exc: BaseException) -> None:
if isinstance(exc, Interrupted):
self._log_and_notify(tr(TR.SYNC_MEDIA_ABORTED))
return
@ -116,10 +116,10 @@ class MediaSyncer:
def _on_start_stop(self, running: bool) -> None:
self.mw.toolbar.set_sync_active(running)
def show_sync_log(self):
def show_sync_log(self) -> None:
aqt.dialogs.open("sync_log", self.mw, self)
def show_diag_until_finished(self, on_finished: Callable[[], None]):
def show_diag_until_finished(self, on_finished: Callable[[], None]) -> None:
# nothing to do if not syncing
if not self.is_syncing():
return on_finished()
@ -129,7 +129,7 @@ class MediaSyncer:
timer: Optional[QTimer] = None
def check_finished():
def check_finished() -> None:
if not self.is_syncing():
timer.stop()
on_finished()
@ -197,7 +197,7 @@ class MediaSyncDialog(QDialog):
asctime = time.asctime(time.localtime(stamp))
return f"{asctime}: {text}"
def _entry_to_text(self, entry: LogEntryWithTime):
def _entry_to_text(self, entry: LogEntryWithTime) -> str:
if isinstance(entry.entry, str):
txt = entry.entry
elif isinstance(entry.entry, MediaSyncProgress):
@ -209,7 +209,7 @@ class MediaSyncDialog(QDialog):
def _logentry_to_text(self, e: MediaSyncProgress) -> str:
return f"{e.added}, {e.removed}, {e.checked}"
def _on_log_entry(self, entry: LogEntryWithTime):
def _on_log_entry(self, entry: LogEntryWithTime) -> None:
self.form.plainTextEdit.appendPlainText(self._entry_to_text(entry))
if not self._syncer.is_syncing():
self.abort_button.setHidden(True)

View file

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from typing import Optional
from typing import List, Optional
from aqt import AnkiQt, gui_hooks
from aqt.qt import *
@ -74,7 +74,7 @@ class ModelChooser(QHBoxLayout):
# edit button
edit = QPushButton(tr(TR.QT_MISC_MANAGE), clicked=self.onEdit) # type: ignore
def nameFunc():
def nameFunc() -> List[str]:
return sorted(self.deck.models.allNames())
ret = StudyDeck(

View file

@ -219,7 +219,7 @@ class Models(QDialog):
class AddModel(QDialog):
model: Optional[NoteType]
def __init__(self, mw: AnkiQt, parent: Optional[QWidget] = None):
def __init__(self, mw: AnkiQt, parent: Optional[QWidget] = None) -> None:
self.parent_ = parent or mw
self.mw = mw
self.col = mw.col

View file

@ -15,7 +15,7 @@ from aqt.utils import TR, askUserDialog, openLink, shortcut, tooltip, tr
class OverviewBottomBar:
def __init__(self, overview: Overview):
def __init__(self, overview: Overview) -> None:
self.overview = overview
@ -104,12 +104,12 @@ class Overview:
def _filteredDeck(self) -> int:
return self.mw.col.decks.current()["dyn"]
def onRebuildKey(self):
def onRebuildKey(self) -> None:
if self._filteredDeck():
self.mw.col.sched.rebuild_filtered_deck(self.mw.col.decks.selected())
self.mw.reset()
def onEmptyKey(self):
def onEmptyKey(self) -> None:
if self._filteredDeck():
self.mw.col.sched.empty_filtered_deck(self.mw.col.decks.selected())
self.mw.reset()
@ -118,7 +118,7 @@ class Overview:
if not self._filteredDeck():
self.onStudyMore()
def onUnbury(self):
def onUnbury(self) -> None:
if self.mw.col.schedVer() == 1:
self.mw.col.sched.unburyCardsForDeck()
self.mw.reset()

View file

@ -34,7 +34,7 @@ def video_driver_name_for_platform(driver: VideoDriver) -> str:
class Preferences(QDialog):
def __init__(self, mw: AnkiQt):
def __init__(self, mw: AnkiQt) -> None:
QDialog.__init__(self, mw, Qt.Window)
self.mw = mw
self.prof = self.mw.pm.profile
@ -55,7 +55,7 @@ class Preferences(QDialog):
self.setupOptions()
self.show()
def accept(self):
def accept(self) -> None:
# avoid exception if main window is already closed
if not self.mw.col:
return
@ -68,19 +68,19 @@ class Preferences(QDialog):
self.done(0)
aqt.dialogs.markClosed("Preferences")
def reject(self):
def reject(self) -> None:
self.accept()
# Language
######################################################################
def setupLang(self):
def setupLang(self) -> None:
f = self.form
f.lang.addItems([x[0] for x in anki.lang.langs])
f.lang.setCurrentIndex(self.langIdx())
qconnect(f.lang.currentIndexChanged, self.onLangIdxChanged)
def langIdx(self):
def langIdx(self) -> int:
codes = [x[1] for x in anki.lang.langs]
lang = anki.lang.currentLang
if lang in anki.lang.compatMap:
@ -92,7 +92,7 @@ class Preferences(QDialog):
except:
return codes.index("en_US")
def onLangIdxChanged(self, idx):
def onLangIdxChanged(self, idx) -> None:
code = anki.lang.langs[idx][1]
self.mw.pm.setLang(code)
showInfo(
@ -102,7 +102,7 @@ class Preferences(QDialog):
# Collection options
######################################################################
def setupCollection(self):
def setupCollection(self) -> None:
import anki.consts as c
f = self.form
@ -130,7 +130,7 @@ class Preferences(QDialog):
f.newSched.setChecked(True)
f.new_timezone.setChecked(s.new_timezone)
def setup_video_driver(self):
def setup_video_driver(self) -> None:
self.video_drivers = VideoDriver.all_for_platform()
names = [
tr(TR.PREFERENCES_VIDEO_DRIVER, driver=video_driver_name_for_platform(d))
@ -141,13 +141,13 @@ class Preferences(QDialog):
self.video_drivers.index(self.mw.pm.video_driver())
)
def update_video_driver(self):
def update_video_driver(self) -> None:
new_driver = self.video_drivers[self.form.video_driver.currentIndex()]
if new_driver != self.mw.pm.video_driver():
self.mw.pm.set_video_driver(new_driver)
showInfo(tr(TR.PREFERENCES_CHANGES_WILL_TAKE_EFFECT_WHEN_YOU))
def updateCollection(self):
def updateCollection(self) -> None:
f = self.form
d = self.mw.col
@ -176,7 +176,7 @@ class Preferences(QDialog):
# Scheduler version
######################################################################
def _updateSchedVer(self, wantNew):
def _updateSchedVer(self, wantNew) -> None:
haveNew = self.mw.col.schedVer() == 2
# nothing to do?
@ -194,7 +194,7 @@ class Preferences(QDialog):
# Network
######################################################################
def setupNetwork(self):
def setupNetwork(self) -> None:
self.form.media_log.setText(tr(TR.SYNC_MEDIA_LOG_BUTTON))
qconnect(self.form.media_log.clicked, self.on_media_log)
self.form.syncOnProgramOpen.setChecked(self.prof["autoSync"])
@ -207,10 +207,10 @@ class Preferences(QDialog):
qconnect(self.form.syncDeauth.clicked, self.onSyncDeauth)
self.form.syncDeauth.setText(tr(TR.SYNC_LOG_OUT_BUTTON))
def on_media_log(self):
def on_media_log(self) -> None:
self.mw.media_syncer.show_sync_log()
def _hideAuth(self):
def _hideAuth(self) -> None:
self.form.syncDeauth.setVisible(False)
self.form.syncUser.setText("")
self.form.syncLabel.setText(
@ -225,7 +225,7 @@ class Preferences(QDialog):
self.mw.col.media.force_resync()
self._hideAuth()
def updateNetwork(self):
def updateNetwork(self) -> None:
self.prof["autoSync"] = self.form.syncOnProgramOpen.isChecked()
self.prof["syncMedia"] = self.form.syncMedia.isChecked()
self.mw.pm.set_auto_sync_media_minutes(
@ -238,16 +238,16 @@ class Preferences(QDialog):
# Backup
######################################################################
def setupBackup(self):
def setupBackup(self) -> None:
self.form.numBackups.setValue(self.prof["numBackups"])
def updateBackup(self):
def updateBackup(self) -> None:
self.prof["numBackups"] = self.form.numBackups.value()
# Basic & Advanced Options
######################################################################
def setupOptions(self):
def setupOptions(self) -> None:
self.form.pastePNG.setChecked(self.prof.get("pastePNG", False))
self.form.uiScale.setValue(int(self.mw.pm.uiScale() * 100))
self.form.pasteInvert.setChecked(self.prof.get("pasteInvert", False))
@ -270,7 +270,7 @@ class Preferences(QDialog):
self._recording_drivers.index(self.mw.pm.recording_driver())
)
def updateOptions(self):
def updateOptions(self) -> None:
restart_required = False
self.prof["pastePNG"] = self.form.pastePNG.isChecked()

View file

@ -40,7 +40,9 @@ class Previewer(QDialog):
_timer: Optional[QTimer] = None
_show_both_sides = False
def __init__(self, parent: QWidget, mw: AnkiQt, on_close: Callable[[], None]):
def __init__(
self, parent: QWidget, mw: AnkiQt, on_close: Callable[[], None]
) -> None:
super().__init__(None, Qt.Window)
self._open = True
self._parent = parent
@ -259,14 +261,14 @@ class MultiCardPreviewer(Previewer):
qconnect(self._prev.clicked, self._on_prev)
qconnect(self._next.clicked, self._on_next)
def _on_prev(self):
def _on_prev(self) -> None:
if self._state == "answer" and not self._show_both_sides:
self._state = "question"
self.render_card()
else:
self._on_prev_card()
def _on_prev_card(self):
def _on_prev_card(self) -> None:
pass
def _on_next(self) -> None:
@ -276,7 +278,7 @@ class MultiCardPreviewer(Previewer):
else:
self._on_next_card()
def _on_next_card(self):
def _on_next_card(self) -> None:
pass
def _updateButtons(self) -> None:
@ -315,7 +317,7 @@ class BrowserPreviewer(MultiCardPreviewer):
self._last_card_id = c.id
return changed
def _on_prev_card(self):
def _on_prev_card(self) -> None:
self._parent.editor.saveNow(
lambda: self._parent._moveCur(QAbstractItemView.MoveUp)
)

View file

@ -113,13 +113,13 @@ class LoadMetaResult:
class AnkiRestart(SystemExit):
def __init__(self, *args, **kwargs):
def __init__(self, *args, **kwargs) -> None:
self.exitcode = kwargs.pop("exitcode", 0)
super().__init__(*args, **kwargs) # type: ignore
class ProfileManager:
def __init__(self, base: Optional[str] = None) -> None:
def __init__(self, base: Optional[str] = None) -> None: #
## Settings which should be forgotten each Anki restart
self.session: Dict[str, Any] = {}
self.name: Optional[str] = None
@ -185,7 +185,7 @@ class ProfileManager:
self.base = newBase
shutil.move(oldBase, self.base)
def _tryToMigrateFolder(self, oldBase):
def _tryToMigrateFolder(self, oldBase) -> None:
from PyQt5 import QtGui, QtWidgets
app = QtWidgets.QApplication([])
@ -269,7 +269,7 @@ class ProfileManager:
fn = super().find_class(module, name)
if module == "sip" and name == "_unpickle_type":
def wrapper(mod, obj, args):
def wrapper(mod, obj, args) -> Any:
if mod.startswith("PyQt4") and obj == "QByteArray":
# can't trust str objects from python 2
return QByteArray()
@ -534,7 +534,7 @@ create table if not exists profiles
def setDefaultLang(self, idx: int) -> None:
# create dialog
class NoCloseDiag(QDialog):
def reject(self):
def reject(self) -> None:
pass
d = self.langDiag = NoCloseDiag()
@ -665,7 +665,7 @@ create table if not exists profiles
pass
return RecordingDriver.QtAudioInput
def set_recording_driver(self, driver: RecordingDriver):
def set_recording_driver(self, driver: RecordingDriver) -> None:
self.profile["recordingDriver"] = driver.value
######################################################################

View file

@ -43,7 +43,7 @@ class ProgressManager:
timer to fire even when there is no collection, but will still
only fire when there is no current progress dialog."""
def handler():
def handler() -> None:
if requiresCollection and not self.mw.col:
# no current collection; timer is no longer valid
print("Ignored progress func as collection unloaded: %s" % repr(func))
@ -225,14 +225,14 @@ class ProgressDialog(QDialog):
self._closingDown = True
self.hide()
def closeEvent(self, evt):
def closeEvent(self, evt) -> None:
if self._closingDown:
evt.accept()
else:
self.wantCancel = True
evt.ignore()
def keyPressEvent(self, evt):
def keyPressEvent(self, evt) -> None:
if evt.key() == Qt.Key_Escape:
evt.ignore()
self.wantCancel = True

View file

@ -30,7 +30,7 @@ except ImportError:
import sip # type: ignore
def debug():
def debug() -> None:
from pdb import set_trace
pyqtRemoveInputHook()
@ -39,7 +39,7 @@ def debug():
if os.environ.get("DEBUG"):
def info(type, value, tb):
def info(type, value, tb) -> None:
for line in traceback.format_exception(type, value, tb):
sys.stdout.write(line)
pyqtRemoveInputHook()

View file

@ -8,7 +8,7 @@ import html
import json
import re
import unicodedata as ucd
from typing import Any, Callable, List, Optional, Tuple, Union
from typing import Any, Callable, List, Match, Optional, Tuple, Union
from PyQt5.QtCore import Qt
@ -426,7 +426,7 @@ class Reviewer:
# compare with typed answer
res = self.correct(given, cor, showBad=False)
# and update the type answer area
def repl(match):
def repl(match: Match) -> str:
# can't pass a string in directly, and can't use re.escape as it
# escapes too much
s = """
@ -448,7 +448,7 @@ class Reviewer:
if not matches:
return None
def noHint(txt):
def noHint(txt) -> str:
if "::" in txt:
return txt.split("::")[0]
return txt
@ -652,7 +652,7 @@ time = %(time)d;
def _answerButtons(self) -> str:
default = self._defaultEase()
def but(i, label):
def but(i, label) -> str:
if i == default:
extra = """id="defease" class="focus" """
else:
@ -834,7 +834,7 @@ time = %(time)d;
tooltip(tr(TR.STUDYING_NOTE_BURIED))
def onRecordVoice(self) -> None:
def after_record(path: str):
def after_record(path: str) -> None:
self._recordedAudio = path
self.onReplayRecorded()

View file

@ -17,7 +17,7 @@ class Change(enum.Enum):
class ChangeTracker:
_changed = Change.NO_CHANGE
def __init__(self, mw: AnkiQt):
def __init__(self, mw: AnkiQt) -> None:
self.mw = mw
def mark_basic(self) -> None:

View file

@ -438,7 +438,7 @@ class MpvManager(MPV, SoundOrVideoPlayer):
class SimpleMplayerSlaveModePlayer(SimpleMplayerPlayer):
def __init__(self, taskman: TaskManager):
def __init__(self, taskman: TaskManager) -> None:
super().__init__(taskman)
self.args.append("-slave")
@ -494,7 +494,7 @@ def encode_mp3(mw: aqt.AnkiQt, src_wav: str, on_done: Callable[[str], None]) ->
"Encode the provided wav file to .mp3, and call on_done() with the path."
dst_mp3 = src_wav.replace(".wav", "%d.mp3" % time.time())
def _on_done(fut: Future):
def _on_done(fut: Future) -> None:
fut.result()
on_done(dst_mp3)
@ -509,7 +509,7 @@ class Recorder(ABC):
# seconds to wait before recording
STARTUP_DELAY = 0.3
def __init__(self, output_path: str):
def __init__(self, output_path: str) -> None:
self.output_path = output_path
def start(self, on_done: Callable[[], None]) -> None:
@ -517,7 +517,7 @@ class Recorder(ABC):
self._started_at = time.time()
on_done()
def stop(self, on_done: Callable[[str], None]):
def stop(self, on_done: Callable[[str], None]) -> None:
"Stop recording, then call on_done() when finished."
on_done(self.output_path)
@ -525,7 +525,7 @@ class Recorder(ABC):
"Seconds since recording started."
return time.time() - self._started_at
def on_timer(self):
def on_timer(self) -> None:
"Will be called periodically."
@ -534,7 +534,7 @@ class Recorder(ABC):
class QtAudioInputRecorder(Recorder):
def __init__(self, output_path: str, mw: aqt.AnkiQt, parent: QWidget):
def __init__(self, output_path: str, mw: aqt.AnkiQt, parent: QWidget) -> None:
super().__init__(output_path)
self.mw = mw
@ -567,11 +567,11 @@ class QtAudioInputRecorder(Recorder):
self._iodevice.readyRead.connect(self._on_read_ready) # type: ignore
super().start(on_done)
def _on_read_ready(self):
def _on_read_ready(self) -> None:
self._buffer += self._iodevice.readAll()
def stop(self, on_done: Callable[[str], None]):
def on_stop_timer():
def stop(self, on_done: Callable[[str], None]) -> None:
def on_stop_timer() -> None:
# read anything remaining in buffer & stop
self._on_read_ready()
self._audio_input.stop()
@ -580,7 +580,7 @@ class QtAudioInputRecorder(Recorder):
showWarning(f"recording failed: {err}")
return
def write_file():
def write_file() -> None:
# swallow the first 300ms to allow audio device to quiesce
wait = int(44100 * self.STARTUP_DELAY)
if len(self._buffer) <= wait:
@ -595,7 +595,7 @@ class QtAudioInputRecorder(Recorder):
wf.writeframes(self._buffer)
wf.close()
def and_then(fut):
def and_then(fut) -> None:
fut.result()
Recorder.stop(self, on_done)
@ -672,7 +672,7 @@ class PyAudioThreadedRecorder(threading.Thread):
class PyAudioRecorder(Recorder):
def __init__(self, mw: aqt.AnkiQt, output_path: str):
def __init__(self, mw: aqt.AnkiQt, output_path: str) -> None:
super().__init__(output_path)
self.mw = mw
@ -686,7 +686,7 @@ class PyAudioRecorder(Recorder):
while self.duration() < 1:
time.sleep(0.1)
def func(fut):
def func(fut) -> None:
Recorder.stop(self, on_done)
self.thread.finish = True
@ -715,7 +715,7 @@ class RecordDialog(QDialog):
self._start_recording()
self._setup_dialog()
def _setup_dialog(self):
def _setup_dialog(self) -> None:
self.setWindowTitle("Anki")
icon = QLabel()
icon.setPixmap(QPixmap(":/icons/media-record.png"))
@ -740,10 +740,10 @@ class RecordDialog(QDialog):
restoreGeom(self, "audioRecorder2")
self.show()
def _save_diag(self):
def _save_diag(self) -> None:
saveGeom(self, "audioRecorder2")
def _start_recording(self):
def _start_recording(self) -> None:
driver = self.mw.pm.recording_driver()
if driver is RecordingDriver.PyAudio:
self._recorder = PyAudioRecorder(self.mw, namedtmp("rec.wav"))
@ -755,18 +755,18 @@ class RecordDialog(QDialog):
assert_exhaustive(driver)
self._recorder.start(self._start_timer)
def _start_timer(self):
def _start_timer(self) -> None:
self._timer = t = QTimer(self._parent)
t.timeout.connect(self._on_timer) # type: ignore
t.setSingleShot(False)
t.start(100)
def _on_timer(self):
def _on_timer(self) -> None:
self._recorder.on_timer()
duration = self._recorder.duration()
self.label.setText(tr(TR.MEDIA_RECORDINGTIME, secs="%0.1f" % duration))
def accept(self):
def accept(self) -> None:
self._timer.stop()
try:
@ -775,10 +775,10 @@ class RecordDialog(QDialog):
finally:
QDialog.accept(self)
def reject(self):
def reject(self) -> None:
self._timer.stop()
def cleanup(out: str):
def cleanup(out: str) -> None:
os.unlink(out)
try:
@ -790,7 +790,7 @@ class RecordDialog(QDialog):
def record_audio(
parent: QWidget, mw: aqt.AnkiQt, encode: bool, on_done: Callable[[str], None]
):
def after_record(path: str):
def after_record(path: str) -> None:
if not encode:
on_done(path)
else:

View file

@ -25,7 +25,7 @@ from aqt.utils import (
class NewDeckStats(QDialog):
"""New deck stats."""
def __init__(self, mw: aqt.main.AnkiQt):
def __init__(self, mw: aqt.main.AnkiQt) -> None:
QDialog.__init__(self, mw, Qt.Window)
mw.setupDialogGC(self)
self.mw = mw
@ -60,11 +60,11 @@ class NewDeckStats(QDialog):
aqt.dialogs.markClosed("NewDeckStats")
QDialog.reject(self)
def closeWithCallback(self, callback):
def closeWithCallback(self, callback) -> None:
self.reject()
callback()
def _imagePath(self):
def _imagePath(self) -> str:
name = time.strftime("-%Y-%m-%d@%H-%M-%S.pdf", time.localtime(time.time()))
name = "anki-" + tr(TR.STATISTICS_STATS) + name
file = getSaveFile(
@ -77,17 +77,17 @@ class NewDeckStats(QDialog):
)
return file
def saveImage(self):
def saveImage(self) -> None:
path = self._imagePath()
if not path:
return
self.form.web.page().printToPdf(path)
tooltip(tr(TR.STATISTICS_SAVED))
def changePeriod(self, n):
def changePeriod(self, n) -> None:
pass
def changeScope(self, type):
def changeScope(self, type) -> None:
pass
def _on_bridge_cmd(self, cmd: str) -> bool:
@ -149,11 +149,11 @@ class DeckStats(QDialog):
aqt.dialogs.markClosed("DeckStats")
QDialog.reject(self)
def closeWithCallback(self, callback):
def closeWithCallback(self, callback) -> None:
self.reject()
callback()
def _imagePath(self):
def _imagePath(self) -> str:
name = time.strftime("-%Y-%m-%d@%H-%M-%S.pdf", time.localtime(time.time()))
name = "anki-" + tr(TR.STATISTICS_STATS) + name
file = getSaveFile(
@ -166,7 +166,7 @@ class DeckStats(QDialog):
)
return file
def saveImage(self):
def saveImage(self) -> None:
path = self._imagePath()
if not path:
return

View file

@ -135,7 +135,7 @@ class StudyDeck(QDialog):
return False
return True
def onReset(self):
def onReset(self) -> None:
# model updated?
if self.nameFunc:
self.origNames = self.nameFunc()

View file

@ -40,12 +40,14 @@ class FullSyncChoice(enum.Enum):
DOWNLOAD = 2
def get_sync_status(mw: aqt.main.AnkiQt, callback: Callable[[SyncStatus], None]):
def get_sync_status(
mw: aqt.main.AnkiQt, callback: Callable[[SyncStatus], None]
) -> None:
auth = mw.pm.sync_auth()
if not auth:
return SyncStatus(required=SyncStatus.NO_CHANGES) # pylint:disable=no-member
callback(SyncStatus(required=SyncStatus.NO_CHANGES)) # pylint:disable=no-member
def on_future_done(fut):
def on_future_done(fut) -> None:
try:
out = fut.result()
except Exception as e:
@ -57,7 +59,7 @@ def get_sync_status(mw: aqt.main.AnkiQt, callback: Callable[[SyncStatus], None])
mw.taskman.run_in_background(lambda: mw.col.sync_status(auth), on_future_done)
def handle_sync_error(mw: aqt.main.AnkiQt, err: Exception):
def handle_sync_error(mw: aqt.main.AnkiQt, err: Exception) -> None:
if isinstance(err, SyncError):
if err.is_auth_error():
mw.pm.clear_sync_auth()
@ -87,14 +89,14 @@ def sync_collection(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
auth = mw.pm.sync_auth()
assert auth
def on_timer():
def on_timer() -> None:
on_normal_sync_timer(mw)
timer = QTimer(mw)
qconnect(timer.timeout, on_timer)
timer.start(150)
def on_future_done(fut):
def on_future_done(fut) -> None:
mw.col.db.begin()
timer.stop()
try:
@ -171,14 +173,14 @@ def on_full_sync_timer(mw: aqt.main.AnkiQt) -> None:
def full_download(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
mw.col.close_for_full_sync()
def on_timer():
def on_timer() -> None:
on_full_sync_timer(mw)
timer = QTimer(mw)
qconnect(timer.timeout, on_timer)
timer.start(150)
def on_future_done(fut):
def on_future_done(fut) -> None:
timer.stop()
mw.col.reopen(after_full_sync=True)
mw.reset()
@ -199,14 +201,14 @@ def full_download(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
def full_upload(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
mw.col.close_for_full_sync()
def on_timer():
def on_timer() -> None:
on_full_sync_timer(mw)
timer = QTimer(mw)
qconnect(timer.timeout, on_timer)
timer.start(150)
def on_future_done(fut):
def on_future_done(fut) -> None:
timer.stop()
mw.col.reopen(after_full_sync=True)
mw.reset()
@ -235,7 +237,7 @@ def sync_login(
if username and password:
break
def on_future_done(fut):
def on_future_done(fut) -> None:
try:
auth = fut.result()
except SyncError as e:

View file

@ -68,7 +68,7 @@ class TagLimit(QDialog):
idx = self.dialog.inactiveList.indexFromItem(item)
self.dialog.inactiveList.selectionModel().select(idx, mode)
def reject(self):
def reject(self) -> None:
self.tags = ""
QDialog.reject(self)

View file

@ -31,7 +31,7 @@ class TaskManager(QObject):
self._closures_lock = Lock()
qconnect(self._closures_pending, self._on_closures_pending)
def run_on_main(self, closure: Closure):
def run_on_main(self, closure: Closure) -> None:
"Run the provided closure on the main thread."
with self._closures_lock:
self._closures.append(closure)
@ -71,7 +71,7 @@ class TaskManager(QObject):
):
self.mw.progress.start(parent=parent, label=label, immediate=immediate)
def wrapped_done(fut):
def wrapped_done(fut) -> None:
self.mw.progress.finish()
if on_done:
on_done(fut)

View file

@ -481,7 +481,7 @@ if isWin:
return []
return list(map(self._voice_to_object, self.speaker.GetVoices()))
def _voice_to_object(self, voice: Any):
def _voice_to_object(self, voice: Any) -> WindowsVoice:
lang = voice.GetAttribute("language")
lang = lcid_hex_str_to_lang_code(lang)
name = self._tidy_name(voice.GetAttribute("name"))
@ -561,7 +561,7 @@ if isWin:
)
asyncio.run(self.speakText(tag, voice.id))
def _on_done(self, ret: Future, cb: OnDoneCallback):
def _on_done(self, ret: Future, cb: OnDoneCallback) -> None:
ret.result()
# inject file into the top of the audio queue
@ -572,7 +572,7 @@ if isWin:
# then tell player to advance, which will cause the file to be played
cb()
async def speakText(self, tag: TTSTag, voice_id):
async def speakText(self, tag: TTSTag, voice_id) -> None:
import winrt.windows.media.speechsynthesis as speechsynthesis # type: ignore
import winrt.windows.storage.streams as streams # type: ignore

View file

@ -2,6 +2,7 @@
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import time
from typing import Any, Dict
import requests
@ -23,7 +24,7 @@ class LatestVersionFinder(QThread):
self.main = main
self.config = main.pm.meta
def _data(self):
def _data(self) -> Dict[str, Any]:
return {
"ver": versionWithBuild(),
"os": platDesc(),
@ -32,7 +33,7 @@ class LatestVersionFinder(QThread):
"crt": self.config["created"],
}
def run(self):
def run(self) -> None:
if not self.config["updates"]:
return
d = self._data()
@ -55,7 +56,7 @@ class LatestVersionFinder(QThread):
self.clockIsOff.emit(diff) # type: ignore
def askAndUpdate(mw, ver):
def askAndUpdate(mw, ver) -> None:
baseStr = tr(TR.QT_MISC_ANKI_UPDATEDANKI_HAS_BEEN_RELEASED, val=ver)
msg = QMessageBox(mw)
msg.setStandardButtons(QMessageBox.Yes | QMessageBox.No) # type: ignore
@ -72,6 +73,6 @@ def askAndUpdate(mw, ver):
openLink(aqt.appWebsite)
def showMessages(mw, data):
def showMessages(mw, data) -> None:
showText(data["msg"], parent=mw, type="html")
mw.pm.meta["lastMsg"] = data["msgId"]

View file

@ -22,7 +22,7 @@ serverbaseurl = re.compile(r"^.+:\/\/[^\/]+")
class AnkiWebPage(QWebEnginePage):
def __init__(self, onBridgeCmd):
def __init__(self, onBridgeCmd) -> None:
QWebEnginePage.__init__(self)
self._onBridgeCmd = onBridgeCmd
self._setupBridge()
@ -31,7 +31,7 @@ class AnkiWebPage(QWebEnginePage):
def _setupBridge(self) -> None:
class Bridge(QObject):
@pyqtSlot(str, result=str) # type: ignore
def cmd(self, str):
def cmd(self, str) -> Any:
return json.dumps(self.onCmd(str))
self._bridge = Bridge()
@ -74,7 +74,7 @@ class AnkiWebPage(QWebEnginePage):
script.setRunsOnSubFrames(False)
self.profile().scripts().insert(script)
def javaScriptConsoleMessage(self, level, msg, line, srcID):
def javaScriptConsoleMessage(self, level, msg, line, srcID) -> None:
# not translated because console usually not visible,
# and may only accept ascii text
if srcID.startswith("data"):
@ -101,7 +101,7 @@ class AnkiWebPage(QWebEnginePage):
# https://github.com/ankitects/anki/pull/560
sys.stdout.write(buf)
def acceptNavigationRequest(self, url, navType, isMainFrame):
def acceptNavigationRequest(self, url, navType, isMainFrame) -> bool:
if not self.open_links_externally:
return super().acceptNavigationRequest(url, navType, isMainFrame)
@ -120,10 +120,10 @@ class AnkiWebPage(QWebEnginePage):
openLink(url)
return False
def _onCmd(self, str):
def _onCmd(self, str) -> None:
return self._onBridgeCmd(str)
def javaScriptAlert(self, url: QUrl, text: str):
def javaScriptAlert(self, url: QUrl, text: str) -> None:
showInfo(text)
@ -150,7 +150,7 @@ class WebContent:
You should avoid overwriting or interfering with existing data as much
as possible, instead opting to append your own changes, e.g.:
def on_webview_will_set_content(web_content: WebContent, context):
def on_webview_will_set_content(web_content: WebContent, context) -> None:
web_content.body += "<my_html>"
web_content.head += "<my_head>"
@ -173,7 +173,7 @@ class WebContent:
Then append the subpaths to the corresponding web_content fields
within a function subscribing to gui_hooks.webview_will_set_content:
def on_webview_will_set_content(web_content: WebContent, context):
def on_webview_will_set_content(web_content: WebContent, context) -> None:
addon_package = mw.addonManager.addonFromModule(__name__)
web_content.css.append(
f"/_addons/{addon_package}/web/my-addon.css")
@ -251,7 +251,7 @@ class AnkiWebView(QWebEngineView):
def set_open_links_externally(self, enable: bool) -> None:
self._page.open_links_externally = enable
def onEsc(self):
def onEsc(self) -> None:
w = self.parent()
while w:
if isinstance(w, QDialog) or isinstance(w, QMainWindow):
@ -266,7 +266,7 @@ class AnkiWebView(QWebEngineView):
break
w = w.parent()
def onCopy(self):
def onCopy(self) -> None:
if not self.selectedText():
ctx = self._page.contextMenuData()
if ctx and ctx.mediaType() == QWebEngineContextMenuData.MediaTypeImage:
@ -274,16 +274,16 @@ class AnkiWebView(QWebEngineView):
else:
self.triggerPageAction(QWebEnginePage.Copy)
def onCut(self):
def onCut(self) -> None:
self.triggerPageAction(QWebEnginePage.Cut)
def onPaste(self):
def onPaste(self) -> None:
self.triggerPageAction(QWebEnginePage.Paste)
def onMiddleClickPaste(self):
def onMiddleClickPaste(self) -> None:
self.triggerPageAction(QWebEnginePage.Paste)
def onSelectAll(self):
def onSelectAll(self) -> None:
self.triggerPageAction(QWebEnginePage.SelectAll)
def contextMenuEvent(self, evt: QContextMenuEvent) -> None:
@ -293,7 +293,7 @@ class AnkiWebView(QWebEngineView):
gui_hooks.webview_will_show_context_menu(self, m)
m.popup(QCursor.pos())
def dropEvent(self, evt):
def dropEvent(self, evt) -> None:
pass
def setHtml(self, html: str) -> None: # type: ignore
@ -312,7 +312,7 @@ class AnkiWebView(QWebEngineView):
if oldFocus:
oldFocus.setFocus()
def load(self, url: QUrl):
def load(self, url: QUrl) -> None:
# allow queuing actions when loading url directly
self._domDone = False
super().load(url)
@ -364,7 +364,7 @@ class AnkiWebView(QWebEngineView):
else:
return 3
def _getWindowColor(self):
def _getWindowColor(self) -> QColor:
if theme_manager.night_mode:
return theme_manager.qcolor("window-bg")
if isMac:
@ -508,7 +508,7 @@ body {{ zoom: {zoom}; background: {background}; direction: {lang_dir}; {font} }}
def _evalWithCallback(self, js: str, cb: Callable[[Any], Any]) -> None:
if cb:
def handler(val):
def handler(val) -> None:
if self._shouldIgnoreWebEvent():
print("ignored late js callback", cb)
return
@ -597,18 +597,18 @@ body {{ zoom: {zoom}; background: {background}; direction: {lang_dir}; {font} }}
self.onBridgeCmd = func
self._bridge_context = context
def hide_while_preserving_layout(self):
def hide_while_preserving_layout(self) -> None:
"Hide but keep existing size."
sp = self.sizePolicy()
sp.setRetainSizeWhenHidden(True)
self.setSizePolicy(sp)
self.hide()
def inject_dynamic_style_and_show(self):
def inject_dynamic_style_and_show(self) -> None:
"Add dynamic styling, and reveal."
css = self.standard_css()
def after_style(arg):
def after_style(arg) -> None:
gui_hooks.webview_did_inject_style_into_page(self)
self.show()

View file

@ -9,5 +9,5 @@
(sleep 1 && touch aqt)
. ~/pyenv/bin/activate
fswatch -o aqt | xargs -n1 -I{} sh -c 'printf \\033c; dmypy run aqt'
fswatch -o aqt | xargs -n1 -I{} sh -c 'printf \\033c\\n; dmypy run aqt'