From a6d5c949970627f2b4dcea8a02fea3a497e0440f Mon Sep 17 00:00:00 2001 From: David Culley <6276049+davidculley@users.noreply.github.com> Date: Thu, 22 Aug 2024 11:03:44 +0200 Subject: [PATCH] python: add missing type annotations for None values (#3364) * refactor: explicitly add NoneType to type hints If variable can be `None`, don't be implicit. Be explicit. --- pylib/anki/collection.py | 6 +++--- pylib/anki/config.py | 2 +- pylib/anki/decks.py | 2 +- pylib/anki/httpclient.py | 2 +- pylib/anki/models.py | 2 +- pylib/anki/template.py | 2 +- qt/aqt/addons.py | 4 +++- qt/aqt/browser/sidebar/item.py | 2 +- qt/aqt/browser/sidebar/tree.py | 2 +- qt/aqt/browser/table/table.py | 4 +++- qt/aqt/editor.py | 4 ++-- qt/aqt/import_export/exporting.py | 2 +- qt/aqt/importing.py | 2 +- qt/aqt/main.py | 4 ++-- qt/aqt/progress.py | 2 +- qt/aqt/reviewer.py | 8 ++++---- qt/aqt/switch.py | 4 +++- qt/aqt/utils.py | 14 +++++++------- 18 files changed, 37 insertions(+), 31 deletions(-) diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index a501de728..b3bad9922 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -897,7 +897,7 @@ class Collection(DeprecatedNamesMixin): # Config ########################################################################## - def get_config(self, key: str, default: Any = None) -> Any: + def get_config(self, key: str, default: Any | None = None) -> Any: try: return self.conf.get_immutable(key) except KeyError: @@ -939,7 +939,7 @@ class Collection(DeprecatedNamesMixin): return self._backend.set_config_string(key=key, value=value, undoable=undoable) def get_aux_notetype_config( - self, id: NotetypeId, key: str, default: Any = None + self, id: NotetypeId, key: str, default: Any | None = None ) -> Any: key = self._backend.get_aux_notetype_config_key(id=id, key=key) return self.get_config(key, default=default) @@ -951,7 +951,7 @@ class Collection(DeprecatedNamesMixin): return self.set_config(key, value, undoable=undoable) def get_aux_template_config( - self, id: NotetypeId, card_ordinal: int, key: str, default: Any = None + self, id: NotetypeId, card_ordinal: int, key: str, default: Any | None = None ) -> Any: key = self._backend.get_aux_template_config_key( notetype_id=id, card_ordinal=card_ordinal, key=key diff --git a/pylib/anki/config.py b/pylib/anki/config.py index 834af5004..775557c84 100644 --- a/pylib/anki/config.py +++ b/pylib/anki/config.py @@ -76,7 +76,7 @@ class ConfigManager: def __setitem__(self, key: str, value: Any) -> None: self.set(key, value) - def get(self, key: str, default: Any = None) -> Any: + def get(self, key: str, default: Any | None = None) -> Any: try: return self[key] except KeyError: diff --git a/pylib/anki/decks.py b/pylib/anki/decks.py index e61721dd8..c53275ae3 100644 --- a/pylib/anki/decks.py +++ b/pylib/anki/decks.py @@ -85,7 +85,7 @@ class DeckManager(DeprecatedNamesMixin): self.col = col.weakref() self.decks = DecksDictProxy(col) - def save(self, deck_or_config: DeckDict | DeckConfigDict = None) -> None: + def save(self, deck_or_config: DeckDict | DeckConfigDict | None = None) -> None: "Can be called with either a deck or a deck configuration." if not deck_or_config: print("col.decks.save() should be passed the changed deck") diff --git a/pylib/anki/httpclient.py b/pylib/anki/httpclient.py index 186f90623..d0a9fdd65 100644 --- a/pylib/anki/httpclient.py +++ b/pylib/anki/httpclient.py @@ -57,7 +57,7 @@ class HttpClient(DeprecatedNamesMixin): verify=self.verify, ) # pytype: disable=wrong-arg-types - def get(self, url: str, headers: dict[str, str] = None) -> Response: + def get(self, url: str, headers: dict[str, str] | None = None) -> Response: if headers is None: headers = {} headers["User-Agent"] = self._agent_name() diff --git a/pylib/anki/models.py b/pylib/anki/models.py index 4dfdb0211..fb08fd6f3 100644 --- a/pylib/anki/models.py +++ b/pylib/anki/models.py @@ -563,7 +563,7 @@ and notes.mid = ? and cards.ord = ?""", self._mutate_after_write(notetype) # @deprecated(replaced_by=update_dict) - def save(self, notetype: NotetypeDict = None, **legacy_kwargs: bool) -> None: + def save(self, notetype: NotetypeDict | None = None, **legacy_kwargs: bool) -> None: "Save changes made to provided note type." if not notetype: print_deprecation_warning( diff --git a/pylib/anki/template.py b/pylib/anki/template.py index deda8c819..2a145c9e5 100644 --- a/pylib/anki/template.py +++ b/pylib/anki/template.py @@ -147,7 +147,7 @@ class TemplateRenderContext: card: anki.cards.Card, note: anki.notes.Note, browser: bool = False, - notetype: NotetypeDict = None, + notetype: NotetypeDict | None = None, template: dict | None = None, fill_empty: bool = False, ) -> None: diff --git a/qt/aqt/addons.py b/qt/aqt/addons.py index 00d68c098..b4f3868e7 100644 --- a/qt/aqt/addons.py +++ b/qt/aqt/addons.py @@ -409,7 +409,9 @@ class AddonManager: all_conflicts[other_dir].append(addon.dir_name) return all_conflicts - def _disableConflicting(self, module: str, conflicts: list[str] = None) -> set[str]: + def _disableConflicting( + self, module: str, conflicts: list[str] | None = None + ) -> set[str]: if not self.isEnabled(module): # disabled add-ons should not trigger conflict handling return set() diff --git a/qt/aqt/browser/sidebar/item.py b/qt/aqt/browser/sidebar/item.py index 576d0b455..8da2f3df6 100644 --- a/qt/aqt/browser/sidebar/item.py +++ b/qt/aqt/browser/sidebar/item.py @@ -62,7 +62,7 @@ class SidebarItem: name: str, icon: str | ColoredIcon, search_node: SearchNode | None = None, - on_expanded: Callable[[bool], None] = None, + on_expanded: Callable[[bool], None] | None = None, expanded: bool = False, item_type: SidebarItemType = SidebarItemType.CUSTOM, id: int = 0, diff --git a/qt/aqt/browser/sidebar/tree.py b/qt/aqt/browser/sidebar/tree.py index 6189e925c..89600f231 100644 --- a/qt/aqt/browser/sidebar/tree.py +++ b/qt/aqt/browser/sidebar/tree.py @@ -159,7 +159,7 @@ class SidebarTreeView(QTreeView): self.refresh() self._refresh_needed = False - def refresh(self, new_current: SidebarItem = None) -> None: + def refresh(self, new_current: SidebarItem | None = None) -> None: "Refresh list. No-op if sidebar is not visible." if not self.isVisible(): return diff --git a/qt/aqt/browser/table/table.py b/qt/aqt/browser/table/table.py index 345386acb..54631ebfa 100644 --- a/qt/aqt/browser/table/table.py +++ b/qt/aqt/browser/table/table.py @@ -600,7 +600,9 @@ class Table: self._view.verticalScrollBar().setValue(vertical) def _move_current( - self, direction: QAbstractItemView.CursorAction, index: QModelIndex = None + self, + direction: QAbstractItemView.CursorAction, + index: QModelIndex | None = None, ) -> None: if not self.has_current(): return diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index 6f0a994d7..74cc6e55b 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -233,9 +233,9 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too func: Callable[[Editor], None], tip: str = "", label: str = "", - id: str = None, + id: str | None = None, toggleable: bool = False, - keys: str = None, + keys: str | None = None, disables: bool = True, rightside: bool = True, ) -> str: diff --git a/qt/aqt/import_export/exporting.py b/qt/aqt/import_export/exporting.py index a33395f98..ad7fc4ef6 100644 --- a/qt/aqt/import_export/exporting.py +++ b/qt/aqt/import_export/exporting.py @@ -137,7 +137,7 @@ class ExportDialog(QDialog): return path def options(self, out_path: str) -> ExportOptions: - limit: ExportLimit = None + limit: ExportLimit | None = None if self.nids: limit = NoteIdsLimit(self.nids) elif current_deck_id := self.current_deck_id(): diff --git a/qt/aqt/importing.py b/qt/aqt/importing.py index b00d0b69b..ffc173a8b 100644 --- a/qt/aqt/importing.py +++ b/qt/aqt/importing.py @@ -120,7 +120,7 @@ class ImportDialog(QDialog): ) self.deck = aqt.deckchooser.DeckChooser(self.mw, self.frm.deckArea, label=False) - def modelChanged(self, unused: Any = None) -> None: + def modelChanged(self, unused: Any | None = None) -> None: self.importer.model = self.mw.col.models.current() self.importer.initMapping() self.showMapping() diff --git a/qt/aqt/main.py b/qt/aqt/main.py index 9508cc10d..a27ec8abd 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -862,8 +862,8 @@ class AnkiQt(QMainWindow): def requireReset( self, modal: bool = False, - reason: Any = None, - context: Any = None, + reason: Any | None = None, + context: Any | None = None, ) -> None: traceback.print_stack(file=sys.stdout) print("requireReset() is obsolete; please use CollectionOp()") diff --git a/qt/aqt/progress.py b/qt/aqt/progress.py index dd8fb26e1..84d23a22e 100644 --- a/qt/aqt/progress.py +++ b/qt/aqt/progress.py @@ -42,7 +42,7 @@ class ProgressManager: repeat: bool, requiresCollection: bool = True, *, - parent: QObject = None, + parent: QObject | None = None, ) -> QTimer: """Create and start a standard Anki timer. For an alternative see `single_shot()`. diff --git a/qt/aqt/reviewer.py b/qt/aqt/reviewer.py index 680d323b1..0913d049d 100644 --- a/qt/aqt/reviewer.py +++ b/qt/aqt/reviewer.py @@ -152,8 +152,8 @@ class Reviewer: self.previous_card: Card | None = None self._answeredIds: list[CardId] = [] self._recordedAudio: str | None = None - self.typeCorrect: str = None # web init happens before this is set - self.state: Literal["question", "answer", "transition", None] = None + self.typeCorrect: str | None = None # web init happens before this is set + self.state: Literal["question", "answer", "transition"] | None = None self._refresh_needed: RefreshNeeded | None = None self._v3: V3CardInfo | None = None self._state_mutation_key = str(random.randint(0, 2**64 - 1)) @@ -162,7 +162,7 @@ class Reviewer: self._previous_card_info = PreviousReviewerCardInfo(self.mw) self._states_mutated = True self._state_mutation_js = None - self._reps: int = None + self._reps: int | None = None self._show_question_timer: QTimer | None = None self._show_answer_timer: QTimer | None = None self.auto_advance_enabled = False @@ -369,7 +369,7 @@ class Reviewer: def _showQuestion(self) -> None: self._reps += 1 self.state = "question" - self.typedAnswer: str = None + self.typedAnswer: str | None = None c = self.card # grab the question and play audio q = c.question() diff --git a/qt/aqt/switch.py b/qt/aqt/switch.py index 15f3c0890..fb3c2da6c 100644 --- a/qt/aqt/switch.py +++ b/qt/aqt/switch.py @@ -1,5 +1,7 @@ # Copyright: Ankitects Pty Ltd and contributors # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +from __future__ import annotations + from typing import cast from aqt import colors, props @@ -21,7 +23,7 @@ class Switch(QAbstractButton): right_label: str = "", left_color: dict[str, str] = colors.ACCENT_CARD | {}, right_color: dict[str, str] = colors.ACCENT_NOTE | {}, - parent: QWidget = None, + parent: QWidget | None = None, ) -> None: super().__init__(parent=parent) self.setCheckable(True) diff --git a/qt/aqt/utils.py b/qt/aqt/utils.py index 98e4cee10..8d7774555 100644 --- a/qt/aqt/utils.py +++ b/qt/aqt/utils.py @@ -393,8 +393,8 @@ def showText( def askUser( text: str, - parent: QWidget = None, - help: HelpPageArgument = None, + parent: QWidget | None = None, + help: HelpPageArgument | None = None, defaultno: bool = False, msgfunc: Callable | None = None, title: str = "Anki", @@ -426,7 +426,7 @@ class ButtonedDialog(QMessageBox): text: str, buttons: list[str], parent: QWidget | None = None, - help: HelpPageArgument = None, + help: HelpPageArgument | None = None, title: str = "Anki", ): QMessageBox.__init__(self, parent) @@ -459,7 +459,7 @@ def askUserDialog( text: str, buttons: list[str], parent: QWidget | None = None, - help: HelpPageArgument = None, + help: HelpPageArgument | None = None, title: str = "Anki", ) -> ButtonedDialog: if not parent: @@ -473,7 +473,7 @@ class GetTextDialog(QDialog): self, parent: QWidget | None, question: str, - help: HelpPageArgument = None, + help: HelpPageArgument | None = None, edit: QLineEdit | None = None, default: str = "", title: str = "Anki", @@ -525,7 +525,7 @@ class GetTextDialog(QDialog): def getText( prompt: str, parent: QWidget | None = None, - help: HelpPageArgument = None, + help: HelpPageArgument | None = None, edit: QLineEdit | None = None, default: str = "", title: str = "Anki", @@ -558,7 +558,7 @@ def getOnlyText(*args: Any, **kwargs: Any) -> str: # fixme: these utilities could be combined into a single base class # unused by Anki, but used by add-ons def chooseList( - prompt: str, choices: list[str], startrow: int = 0, parent: Any = None + prompt: str, choices: list[str], startrow: int = 0, parent: Any | None = None ) -> int: if not parent: parent = aqt.mw.app.activeWindow()