diff --git a/pylib/anki/importing/anki2.py b/pylib/anki/importing/anki2.py index ba829c412..098265c3f 100644 --- a/pylib/anki/importing/anki2.py +++ b/pylib/anki/importing/anki2.py @@ -2,10 +2,11 @@ # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # pylint: disable=invalid-name +from __future__ import annotations import os import unicodedata -from typing import Any, Optional +from typing import Any from anki.cards import CardId from anki.collection import Collection @@ -31,7 +32,7 @@ class MediaMapInvalid(Exception): class Anki2Importer(Importer): needMapper = False - deckPrefix: Optional[str] = None + deckPrefix: str | None = None allowUpdate = True src: Collection dst: Collection @@ -409,7 +410,7 @@ insert or ignore into revlog values (?,?,?,?,?,?,?,?,?)""", if fname.startswith("_") and not self.dst.media.have(fname): self._writeDstMedia(fname, self._srcMediaData(fname)) - def _mediaData(self, fname: str, dir: Optional[str] = None) -> bytes: + def _mediaData(self, fname: str, dir: str | None = None) -> bytes: if not dir: dir = self.src.media.dir() path = os.path.join(dir, fname) diff --git a/pylib/anki/importing/apkg.py b/pylib/anki/importing/apkg.py index 31d1cc4fd..ea2325960 100644 --- a/pylib/anki/importing/apkg.py +++ b/pylib/anki/importing/apkg.py @@ -2,12 +2,13 @@ # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # pylint: disable=invalid-name +from __future__ import annotations import json import os import unicodedata import zipfile -from typing import Any, Optional +from typing import Any from anki.importing.anki2 import Anki2Importer, MediaMapInvalid from anki.utils import tmpfile @@ -15,7 +16,7 @@ from anki.utils import tmpfile class AnkiPackageImporter(Anki2Importer): nameToNum: dict[str, str] - zip: Optional[zipfile.ZipFile] + zip: zipfile.ZipFile | None def run(self) -> None: # type: ignore # extract the deck from the zip file diff --git a/pylib/anki/importing/base.py b/pylib/anki/importing/base.py index 4834181cd..2ddcaaebf 100644 --- a/pylib/anki/importing/base.py +++ b/pylib/anki/importing/base.py @@ -2,8 +2,9 @@ # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html # pylint: disable=invalid-name +from __future__ import annotations -from typing import Any, Optional +from typing import Any from anki.collection import Collection from anki.utils import max_id @@ -15,7 +16,7 @@ from anki.utils import max_id class Importer: needMapper = False needDelimiter = False - dst: Optional[Collection] + dst: Collection | None def __init__(self, col: Collection, file: str) -> None: self.file = file diff --git a/pylib/anki/importing/supermemo_xml.py b/pylib/anki/importing/supermemo_xml.py index 3e9127877..202592c2e 100644 --- a/pylib/anki/importing/supermemo_xml.py +++ b/pylib/anki/importing/supermemo_xml.py @@ -3,13 +3,13 @@ # pytype: disable=attribute-error # type: ignore # pylint: disable=C +from __future__ import annotations import re import sys import time import unicodedata from string import capwords -from typing import Optional, Union from xml.dom import minidom from xml.dom.minidom import Element, Text @@ -329,7 +329,7 @@ class SupermemoXmlImporter(NoteImporter): self.logger("Load done.") # PARSE - def parse(self, node: Optional[Union[Text, Element]] = None) -> None: + def parse(self, node: Text | Element | None = None) -> None: "Parse method - parses document elements" if node is None and self.xmldoc is not None: diff --git a/pylib/tools/hookslib.py b/pylib/tools/hookslib.py index 6361c633e..8325124ad 100644 --- a/pylib/tools/hookslib.py +++ b/pylib/tools/hookslib.py @@ -5,12 +5,13 @@ Code for generating hooks. """ +from __future__ import annotations + import os import subprocess import sys from dataclasses import dataclass from operator import attrgetter -from typing import Optional sys.path.append("pylib/anki/_vendor") @@ -23,19 +24,19 @@ class Hook: name: str # string of the typed arguments passed to the callback, eg # ["kind: str", "val: int"] - args: list[str] = None + args: list[str] | None = None # string of the return type. if set, hook is a filter. - return_type: Optional[str] = None + return_type: str | None = None # if add-ons may be relying on the legacy hook name, add it here - legacy_hook: Optional[str] = None + legacy_hook: str | None = None # if legacy hook takes no arguments but the new hook does, set this legacy_no_args: bool = False # if the hook replaces a deprecated one, add its name here - replaces: Optional[str] = None + replaces: str | None = None # arguments that the hook being replaced took - replaced_hook_args: Optional[list[str]] = None + replaced_hook_args: list[str] | None = None # docstring to add to hook class - doc: Optional[str] = None + doc: str | None = None def callable(self) -> str: "Convert args into a Callable." @@ -47,7 +48,7 @@ class Hook: types_str = ", ".join(types) return f"Callable[[{types_str}], {self.return_type or 'None'}]" - def arg_names(self, args: Optional[list[str]]) -> list[str]: + def arg_names(self, args: list[str] | None) -> list[str]: names = [] for arg in args or []: if not arg: diff --git a/qt/aqt/editcurrent.py b/qt/aqt/editcurrent.py index e3e8ab517..744bce949 100644 --- a/qt/aqt/editcurrent.py +++ b/qt/aqt/editcurrent.py @@ -1,7 +1,8 @@ # 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 collections.abc import Callable -from typing import Optional import aqt.editor from anki.collection import OpChanges @@ -38,7 +39,7 @@ class EditCurrent(QMainWindow): self.show() def on_operation_did_execute( - self, changes: OpChanges, handler: Optional[object] + self, changes: OpChanges, handler: object | None ) -> None: if changes.note_text and handler is not self.editor: # reload note diff --git a/qt/aqt/importing.py b/qt/aqt/importing.py index a78e4a8d2..89a4218bf 100644 --- a/qt/aqt/importing.py +++ b/qt/aqt/importing.py @@ -1,12 +1,14 @@ # Copyright: Ankitects Pty Ltd and contributors # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html +from __future__ import annotations + import os import re import traceback import zipfile from collections.abc import Callable from concurrent.futures import Future -from typing import Any, Optional +from typing import Any import anki.importing as importing import aqt.deckchooser @@ -55,7 +57,7 @@ class ChangeMap(QDialog): self.frm.fields.setCurrentRow(n) else: self.frm.fields.setCurrentRow(n + 1) - self.field: Optional[str] = None + self.field: str | None = None def getField(self) -> str: self.exec() @@ -231,13 +233,13 @@ class ImportDialog(QDialog): self.frm.mappingArea.setWidget(self.frame) self.mapbox = QVBoxLayout(self.frame) self.mapbox.setContentsMargins(0, 0, 0, 0) - self.mapwidget: Optional[QWidget] = None + self.mapwidget: QWidget | None = None def hideMapping(self) -> None: self.frm.mappingGroup.hide() def showMapping( - self, keepMapping: bool = False, hook: Optional[Callable] = None + self, keepMapping: bool = False, hook: Callable | None = None ) -> None: if hook: hook() diff --git a/qt/aqt/modelchooser.py b/qt/aqt/modelchooser.py index f2fceccc2..cdc75cd06 100644 --- a/qt/aqt/modelchooser.py +++ b/qt/aqt/modelchooser.py @@ -1,8 +1,8 @@ # 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 collections.abc import Callable -from typing import Optional from aqt import AnkiQt, gui_hooks from aqt.qt import * @@ -17,7 +17,7 @@ class ModelChooser(QHBoxLayout): mw: AnkiQt, widget: QWidget, label: bool = True, - on_activated: Optional[Callable[[], None]] = None, + on_activated: Callable[[], None] | None = None, ) -> None: """If provided, on_activated() will be called when the button is clicked, and the caller can call .onModelChange() to pull up the dialog when they diff --git a/qt/aqt/mpv.py b/qt/aqt/mpv.py index e3def463b..a3156fa79 100644 --- a/qt/aqt/mpv.py +++ b/qt/aqt/mpv.py @@ -25,6 +25,7 @@ # ------------------------------------------------------------------------------ # pylint: disable=raise-missing-from +from __future__ import annotations import inspect import json @@ -38,7 +39,6 @@ import threading import time from queue import Empty, Full, Queue from shutil import which -from typing import Optional from anki.utils import is_win @@ -77,7 +77,7 @@ class MPVBase: """ executable = which("mpv") - popenEnv: Optional[dict[str, str]] = None + popenEnv: dict[str, str] | None = None default_argv = [ "--idle", diff --git a/qt/aqt/qt/__init__.py b/qt/aqt/qt/__init__.py index 2db1ad40d..ffbdb2dda 100644 --- a/qt/aqt/qt/__init__.py +++ b/qt/aqt/qt/__init__.py @@ -3,6 +3,7 @@ # make sure not to optimize imports on this file # pylint: disable=unused-import +from __future__ import annotations import os import sys @@ -55,9 +56,7 @@ if qtmajor < 5 or (qtmajor == 5 and qtminor < 14): raise Exception("Anki does not support your Qt version.") -def qconnect( - signal: Union[Callable, pyqtSignal, pyqtBoundSignal], func: Callable -) -> None: +def qconnect(signal: Callable | pyqtSignal | pyqtBoundSignal, func: Callable) -> None: """Helper to work around type checking not working with signal.connect(func).""" signal.connect(func) # type: ignore