diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py index 350a24446..03187804b 100644 --- a/qt/aqt/__init__.py +++ b/qt/aqt/__init__.py @@ -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 diff --git a/qt/aqt/about.py b/qt/aqt/about.py index 66165ee62..b014ab7a5 100644 --- a/qt/aqt/about.py +++ b/qt/aqt/about.py @@ -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 = [] diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py index 296dda0a0..bb1a80820 100644 --- a/qt/aqt/addcards.py +++ b/qt/aqt/addcards.py @@ -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() diff --git a/qt/aqt/addons.py b/qt/aqt/addons.py index 5b32d21e2..e91317edb 100644 --- a/qt/aqt/addons.py +++ b/qt/aqt/addons.py @@ -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): diff --git a/qt/aqt/clayout.py b/qt/aqt/clayout.py index 1525f9793..2386bb5b3 100644 --- a/qt/aqt/clayout.py +++ b/qt/aqt/clayout.py @@ -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("
", "") hadHR = origLen != len(txt) - def answerRepl(match): + def answerRepl(match: Match) -> str: res = self.mw.reviewer.correct("exomple", "an example") if hadHR: res = "
" + 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)(.+)
(.+)", 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
\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
{{%s}}
\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) diff --git a/qt/aqt/customstudy.py b/qt/aqt/customstudy.py index ff0ed557c..70fe0483b 100644 --- a/qt/aqt/customstudy.py +++ b/qt/aqt/customstudy.py @@ -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 "" + str(num) + "" diff --git a/qt/aqt/dbcheck.py b/qt/aqt/dbcheck.py index ad46bf231..3024973b6 100644 --- a/qt/aqt/dbcheck.py +++ b/qt/aqt/dbcheck.py @@ -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() diff --git a/qt/aqt/deckbrowser.py b/qt/aqt/deckbrowser.py index ef8e0a621..1d0b28cdb 100644 --- a/qt/aqt/deckbrowser.py +++ b/qt/aqt/deckbrowser.py @@ -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: """ - 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 '
{}
'.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 " " * 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'{cnt}' @@ -222,7 +222,7 @@ class DeckBrowser: buf += self._render_deck_node(child, ctx) return buf - def _topLevelDragRow(self): + def _topLevelDragRow(self) -> str: return " " # 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/") diff --git a/qt/aqt/deckconf.py b/qt/aqt/deckconf.py index f5446fa85..66b792f94 100644 --- a/qt/aqt/deckconf.py +++ b/qt/aqt/deckconf.py @@ -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) diff --git a/qt/aqt/dyndeckconf.py b/qt/aqt/dyndeckconf.py index 0082537d5..b63c638c7 100644 --- a/qt/aqt/dyndeckconf.py +++ b/qt/aqt/dyndeckconf.py @@ -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]]]: diff --git a/qt/aqt/editcurrent.py b/qt/aqt/editcurrent.py index f066c76ae..25616d466 100644 --- a/qt/aqt/editcurrent.py +++ b/qt/aqt/editcurrent.py @@ -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() diff --git a/qt/aqt/emptycards.py b/qt/aqt/emptycards.py index aa1016776..0c64e2e0b 100644 --- a/qt/aqt/emptycards.py +++ b/qt/aqt/emptycards.py @@ -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 = "" 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() diff --git a/qt/aqt/errors.py b/qt/aqt/errors.py index 485ddc898..d085b4b6d 100644 --- a/qt/aqt/errors.py +++ b/qt/aqt/errors.py @@ -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 + "
" + error + "
" 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 "" diff --git a/qt/aqt/exporting.py b/qt/aqt/exporting.py index dd3157365..c22c3019a 100644 --- a/qt/aqt/exporting.py +++ b/qt/aqt/exporting.py @@ -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 diff --git a/qt/aqt/fields.py b/qt/aqt/fields.py index 33b448dba..1f7c2a804 100644 --- a/qt/aqt/fields.py +++ b/qt/aqt/fields.py @@ -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) diff --git a/qt/aqt/importing.py b/qt/aqt/importing.py index e0b77df11..956c8374e 100644 --- a/qt/aqt/importing.py +++ b/qt/aqt/importing.py @@ -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: diff --git a/qt/aqt/legacy.py b/qt/aqt/legacy.py index 50729eb12..47a2242f3 100644 --- a/qt/aqt/legacy.py +++ b/qt/aqt/legacy.py @@ -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) diff --git a/qt/aqt/main.py b/qt/aqt/main.py index c549e5052..17e9619dd 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -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""" % ( 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""" % ( 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""" % ( ) # legacy - def _sync(self): + def _sync(self) -> None: pass onSync = on_sync_button_clicked @@ -1057,7 +1061,7 @@ title="%s" %s>%s""" % ( 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""" % ( 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""" % ( 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""" % ( 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""" % ( # 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""" % ( 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""" % ( # 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""" % ( 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""" % ( # 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""" % ( 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""" % ( 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""" % ( frm.text.setTextCursor(cursor) self.onDebugRet(frm) - def onDebugRet(self, frm): + def onDebugRet(self, frm) -> None: import pprint import traceback diff --git a/qt/aqt/mediacheck.py b/qt/aqt/mediacheck.py index 0f844a643..42cd96a60 100644 --- a/qt/aqt/mediacheck.py +++ b/qt/aqt/mediacheck.py @@ -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 diff --git a/qt/aqt/mediasrv.py b/qt/aqt/mediasrv.py index 1af54e39d..45d52e01e 100644 --- a/qt/aqt/mediasrv.py +++ b/qt/aqt/mediasrv.py @@ -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("/", 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): diff --git a/qt/aqt/mediasync.py b/qt/aqt/mediasync.py index ba5cd95d4..ad9eb9e9c 100644 --- a/qt/aqt/mediasync.py +++ b/qt/aqt/mediasync.py @@ -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) diff --git a/qt/aqt/modelchooser.py b/qt/aqt/modelchooser.py index 754d7b31a..d94dd6f03 100644 --- a/qt/aqt/modelchooser.py +++ b/qt/aqt/modelchooser.py @@ -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( diff --git a/qt/aqt/models.py b/qt/aqt/models.py index df8154ee7..ef17820b2 100644 --- a/qt/aqt/models.py +++ b/qt/aqt/models.py @@ -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 diff --git a/qt/aqt/overview.py b/qt/aqt/overview.py index e2756bbc1..1bd461de4 100644 --- a/qt/aqt/overview.py +++ b/qt/aqt/overview.py @@ -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() diff --git a/qt/aqt/preferences.py b/qt/aqt/preferences.py index 1248696d8..c00783a92 100644 --- a/qt/aqt/preferences.py +++ b/qt/aqt/preferences.py @@ -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() diff --git a/qt/aqt/previewer.py b/qt/aqt/previewer.py index 7557a6412..e0262562e 100644 --- a/qt/aqt/previewer.py +++ b/qt/aqt/previewer.py @@ -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) ) diff --git a/qt/aqt/profiles.py b/qt/aqt/profiles.py index 6a42e4938..efecd2525 100644 --- a/qt/aqt/profiles.py +++ b/qt/aqt/profiles.py @@ -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 ###################################################################### diff --git a/qt/aqt/progress.py b/qt/aqt/progress.py index 47229a3ee..ad351d8ba 100644 --- a/qt/aqt/progress.py +++ b/qt/aqt/progress.py @@ -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 diff --git a/qt/aqt/qt.py b/qt/aqt/qt.py index 43c42b17d..c31e3346b 100644 --- a/qt/aqt/qt.py +++ b/qt/aqt/qt.py @@ -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() diff --git a/qt/aqt/reviewer.py b/qt/aqt/reviewer.py index 8a3e05998..1a806a864 100644 --- a/qt/aqt/reviewer.py +++ b/qt/aqt/reviewer.py @@ -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() diff --git a/qt/aqt/schema_change_tracker.py b/qt/aqt/schema_change_tracker.py index e489f6de6..3d61fe516 100644 --- a/qt/aqt/schema_change_tracker.py +++ b/qt/aqt/schema_change_tracker.py @@ -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: diff --git a/qt/aqt/sound.py b/qt/aqt/sound.py index 6e53bf8fa..04b835e3f 100644 --- a/qt/aqt/sound.py +++ b/qt/aqt/sound.py @@ -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: diff --git a/qt/aqt/stats.py b/qt/aqt/stats.py index d9fe1c870..a1c290fc6 100644 --- a/qt/aqt/stats.py +++ b/qt/aqt/stats.py @@ -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 diff --git a/qt/aqt/studydeck.py b/qt/aqt/studydeck.py index 5ee750687..61f0ec38c 100644 --- a/qt/aqt/studydeck.py +++ b/qt/aqt/studydeck.py @@ -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() diff --git a/qt/aqt/sync.py b/qt/aqt/sync.py index 695fe8d02..9e81277cd 100644 --- a/qt/aqt/sync.py +++ b/qt/aqt/sync.py @@ -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: diff --git a/qt/aqt/taglimit.py b/qt/aqt/taglimit.py index c28392a6d..e9e182ebf 100644 --- a/qt/aqt/taglimit.py +++ b/qt/aqt/taglimit.py @@ -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) diff --git a/qt/aqt/taskman.py b/qt/aqt/taskman.py index 264b4f6c1..53040d7b4 100644 --- a/qt/aqt/taskman.py +++ b/qt/aqt/taskman.py @@ -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) diff --git a/qt/aqt/tts.py b/qt/aqt/tts.py index 0bf9f9342..875983685 100644 --- a/qt/aqt/tts.py +++ b/qt/aqt/tts.py @@ -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 diff --git a/qt/aqt/update.py b/qt/aqt/update.py index 32f956eb6..9c940fe3c 100644 --- a/qt/aqt/update.py +++ b/qt/aqt/update.py @@ -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"] diff --git a/qt/aqt/webview.py b/qt/aqt/webview.py index 8fe1b8438..869f30928 100644 --- a/qt/aqt/webview.py +++ b/qt/aqt/webview.py @@ -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 += "" web_content.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() diff --git a/qt/dmypy-watch.sh b/qt/dmypy-watch.sh index d5e2b3a00..86109eb62 100755 --- a/qt/dmypy-watch.sh +++ b/qt/dmypy-watch.sh @@ -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'