From 63afb0f8c67edbf2cbaf31dc4f0c0de2c176a958 Mon Sep 17 00:00:00 2001
From: David Culley <6276049+davidculley@users.noreply.github.com>
Date: Sun, 21 Jul 2024 09:00:52 +0200
Subject: [PATCH] Update type annotation syntax (#3283)
* chore: add myself to CONTRIBUTORS file
* refactor: use newer type hints for Union/Optional
* refactor: fix deprecated type annotations
use collections.abc rather than typing
* refactor: use lower letter type annotations
* style: reformat with black
* refactor: remove unused imports
* refactor: add missing imports for type hints
* fixup! refactor: use newer type hints for Union/Optional
* fix: add missing imports for type annotations
* fixup! refactor: use newer type hints for Union/Optional
* fixup! style: reformat with black
* refactor: fix remaining imports re: type hints
---
pylib/anki/_backend.py | 3 ++-
pylib/anki/collection.py | 3 ++-
pylib/anki/dbproxy.py | 3 ++-
pylib/anki/decks.py | 3 ++-
pylib/anki/exporting.py | 15 ++++++++-------
pylib/anki/foreign_data/__init__.py | 6 +++---
pylib/anki/foreign_data/mnemosyne.py | 11 +++++------
pylib/anki/hooks.py | 3 +++
pylib/anki/importing/__init__.py | 3 ++-
pylib/anki/importing/anki2.py | 2 +-
pylib/anki/importing/csvfile.py | 10 +++++-----
pylib/anki/importing/noteimp.py | 12 +++++-------
pylib/anki/media.py | 3 ++-
pylib/anki/models.py | 3 ++-
pylib/anki/notes.py | 3 ++-
pylib/anki/scheduler/base.py | 3 ++-
pylib/anki/scheduler/legacy.py | 6 ++----
pylib/anki/scheduler/v3.py | 7 ++++---
pylib/anki/stats.py | 3 ++-
pylib/anki/tags.py | 3 ++-
pylib/anki/template.py | 3 ++-
pylib/anki/utils.py | 3 ++-
pylib/tests/test_schedv3.py | 2 +-
qt/aqt/__init__.py | 19 ++++++++++---------
qt/aqt/about.py | 1 +
qt/aqt/addcards.py | 8 ++++----
qt/aqt/addons.py | 3 ++-
qt/aqt/browser/browser.py | 3 ++-
qt/aqt/browser/find_and_replace.py | 2 +-
qt/aqt/browser/sidebar/item.py | 3 ++-
qt/aqt/browser/sidebar/toolbar.py | 1 +
qt/aqt/browser/sidebar/tree.py | 7 ++++---
qt/aqt/browser/table/__init__.py | 3 ++-
qt/aqt/browser/table/model.py | 3 ++-
qt/aqt/browser/table/state.py | 3 ++-
qt/aqt/browser/table/table.py | 3 ++-
qt/aqt/changenotetype.py | 2 +-
qt/aqt/clayout.py | 9 +++++----
qt/aqt/customstudy.py | 6 ++----
qt/aqt/debug_console.py | 1 +
qt/aqt/deckchooser.py | 2 ++
qt/aqt/deckconf.py | 4 ++--
qt/aqt/editcurrent.py | 1 +
qt/aqt/errors.py | 4 ++--
qt/aqt/exporting.py | 3 +--
qt/aqt/fields.py | 10 ++++------
qt/aqt/import_export/exporting.py | 6 +++---
qt/aqt/import_export/importing.py | 4 ++--
qt/aqt/importing.py | 1 +
qt/aqt/main.py | 3 ++-
qt/aqt/mediacheck.py | 3 ++-
qt/aqt/modelchooser.py | 1 +
qt/aqt/models.py | 15 +++++++--------
qt/aqt/notetypechooser.py | 2 ++
qt/aqt/operations/card.py | 2 +-
qt/aqt/operations/deck.py | 2 +-
qt/aqt/operations/note.py | 2 +-
qt/aqt/operations/scheduling.py | 2 +-
qt/aqt/operations/tag.py | 2 +-
qt/aqt/preferences.py | 1 +
qt/aqt/profiles.py | 4 ++--
qt/aqt/progress.py | 1 +
qt/aqt/qt/qt5_audio.py | 1 +
qt/aqt/reviewer.py | 9 +++++----
qt/aqt/stats.py | 1 +
qt/aqt/tagedit.py | 2 +-
qt/aqt/taglimit.py | 2 +-
qt/aqt/theme.py | 4 ++--
qt/aqt/toolbar.py | 5 +++--
qt/aqt/utils.py | 3 ++-
qt/aqt/webview.py | 13 +++++++------
71 files changed, 171 insertions(+), 134 deletions(-)
diff --git a/pylib/anki/_backend.py b/pylib/anki/_backend.py
index 70e294e77..c3ac2a114 100644
--- a/pylib/anki/_backend.py
+++ b/pylib/anki/_backend.py
@@ -6,8 +6,9 @@ from __future__ import annotations
import sys
import time
import traceback
+from collections.abc import Iterable, Sequence
from threading import current_thread, main_thread
-from typing import TYPE_CHECKING, Any, Iterable, Sequence
+from typing import TYPE_CHECKING, Any
from weakref import ref
from markdown import markdown
diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py
index a8d5d62b4..e2fa0e1d5 100644
--- a/pylib/anki/collection.py
+++ b/pylib/anki/collection.py
@@ -3,7 +3,8 @@
from __future__ import annotations
-from typing import Any, Generator, Iterable, Literal, Sequence, Union, cast
+from collections.abc import Generator, Iterable, Sequence
+from typing import Any, Literal, Union, cast
from anki import (
ankiweb_pb2,
diff --git a/pylib/anki/dbproxy.py b/pylib/anki/dbproxy.py
index 331598aa4..ad2154355 100644
--- a/pylib/anki/dbproxy.py
+++ b/pylib/anki/dbproxy.py
@@ -4,8 +4,9 @@
from __future__ import annotations
import re
+from collections.abc import Iterable, Sequence
from re import Match
-from typing import TYPE_CHECKING, Any, Callable, Iterable, Sequence, Union
+from typing import TYPE_CHECKING, Any, Callable, Union
if TYPE_CHECKING:
import anki._backend
diff --git a/pylib/anki/decks.py b/pylib/anki/decks.py
index 462806028..e61721dd8 100644
--- a/pylib/anki/decks.py
+++ b/pylib/anki/decks.py
@@ -4,7 +4,8 @@
from __future__ import annotations
import copy
-from typing import TYPE_CHECKING, Any, Iterable, NewType, Sequence
+from collections.abc import Iterable, Sequence
+from typing import TYPE_CHECKING, Any, NewType
if TYPE_CHECKING:
import anki
diff --git a/pylib/anki/exporting.py b/pylib/anki/exporting.py
index 1e75adfae..43713d8b2 100644
--- a/pylib/anki/exporting.py
+++ b/pylib/anki/exporting.py
@@ -13,8 +13,9 @@ import threading
import time
import unicodedata
import zipfile
+from collections.abc import Sequence
from io import BufferedWriter
-from typing import Any, Optional, Sequence
+from typing import Any
from zipfile import ZipFile
from anki import hooks
@@ -26,16 +27,16 @@ from anki.utils import ids2str, namedtmp, split_fields, strip_html
class Exporter:
includeHTML: bool | None = None
- ext: Optional[str] = None
- includeTags: Optional[bool] = None
- includeSched: Optional[bool] = None
- includeMedia: Optional[bool] = None
+ ext: str | None = None
+ includeTags: bool | None = None
+ includeSched: bool | None = None
+ includeMedia: bool | None = None
def __init__(
self,
col: Collection,
- did: Optional[DeckId] = None,
- cids: Optional[list[CardId]] = None,
+ did: DeckId | None = None,
+ cids: list[CardId] | None = None,
) -> None:
self.col = col.weakref()
self.did = did
diff --git a/pylib/anki/foreign_data/__init__.py b/pylib/anki/foreign_data/__init__.py
index 50992487b..afcaf685e 100644
--- a/pylib/anki/foreign_data/__init__.py
+++ b/pylib/anki/foreign_data/__init__.py
@@ -94,8 +94,8 @@ class ForeignCard:
class ForeignNote:
fields: list[str] = field(default_factory=list)
tags: list[str] = field(default_factory=list)
- notetype: Union[str, NotetypeId] = ""
- deck: Union[str, DeckId] = ""
+ notetype: str | NotetypeId = ""
+ deck: str | DeckId = ""
cards: list[ForeignCard] = field(default_factory=list)
@@ -103,7 +103,7 @@ class ForeignNote:
class ForeignData:
notes: list[ForeignNote] = field(default_factory=list)
notetypes: list[ForeignNotetype] = field(default_factory=list)
- default_deck: Union[str, DeckId] = ""
+ default_deck: str | DeckId = ""
def serialize(self) -> str:
return json.dumps(self, cls=ForeignDataEncoder, separators=(",", ":"))
diff --git a/pylib/anki/foreign_data/mnemosyne.py b/pylib/anki/foreign_data/mnemosyne.py
index 4ae3d7f5b..078355571 100644
--- a/pylib/anki/foreign_data/mnemosyne.py
+++ b/pylib/anki/foreign_data/mnemosyne.py
@@ -17,7 +17,6 @@ Notetype | Card Type
import re
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
-from typing import Tuple, Type
from anki.db import DB
from anki.decks import DeckId
@@ -38,7 +37,7 @@ def serialize(db_path: str, deck_id: DeckId) -> str:
def gather_data(db: DB, deck_id: DeckId) -> ForeignData:
facts = gather_facts(db)
gather_cards_into_facts(db, facts)
- used_fact_views: dict[Type[MnemoFactView], bool] = {}
+ used_fact_views: dict[type[MnemoFactView], bool] = {}
notes = [fact.foreign_note(used_fact_views) for fact in facts.values()]
notetypes = [fact_view.foreign_notetype() for fact_view in used_fact_views]
return ForeignData(notes, notetypes, deck_id)
@@ -54,7 +53,7 @@ def open_mnemosyne_db(db_path: str) -> DB:
class MnemoFactView(ABC):
notetype: str
- field_keys: Tuple[str, ...]
+ field_keys: tuple[str, ...]
@classmethod
@abstractmethod
@@ -162,7 +161,7 @@ class MnemoFact:
cards: list[MnemoCard] = field(default_factory=list)
def foreign_note(
- self, used_fact_views: dict[Type[MnemoFactView], bool]
+ self, used_fact_views: dict[type[MnemoFactView], bool]
) -> ForeignNote:
fact_view = self.fact_view()
used_fact_views[fact_view] = True
@@ -173,7 +172,7 @@ class MnemoFact:
cards=self.foreign_cards(),
)
- def fact_view(self) -> Type[MnemoFactView]:
+ def fact_view(self) -> type[MnemoFactView]:
try:
fact_view = self.cards[0].fact_view_id
except IndexError as err:
@@ -190,7 +189,7 @@ class MnemoFact:
raise Exception(f"Fact {id} has unknown fact view: {fact_view}")
- def anki_fields(self, fact_view: Type[MnemoFactView]) -> list[str]:
+ def anki_fields(self, fact_view: type[MnemoFactView]) -> list[str]:
return [munge_field(self.fields.get(k, "")) for k in fact_view.field_keys]
def anki_tags(self) -> list[str]:
diff --git a/pylib/anki/hooks.py b/pylib/anki/hooks.py
index 9e360ebfa..de2b0d2ea 100644
--- a/pylib/anki/hooks.py
+++ b/pylib/anki/hooks.py
@@ -14,6 +14,9 @@ modifying it.
from __future__ import annotations
+from collections.abc import Callable
+from typing import Any
+
import decorator
# You can find the definitions in ../tools/genhooks.py
diff --git a/pylib/anki/importing/__init__.py b/pylib/anki/importing/__init__.py
index 9bd01b0e2..6eb93d9a5 100644
--- a/pylib/anki/importing/__init__.py
+++ b/pylib/anki/importing/__init__.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 typing import Any, Callable, Sequence, Type, Union
+from collections.abc import Sequence
+from typing import Any, Callable, Type, Union
import anki
from anki.collection import Collection
diff --git a/pylib/anki/importing/anki2.py b/pylib/anki/importing/anki2.py
index 004fff06b..ba829c412 100644
--- a/pylib/anki/importing/anki2.py
+++ b/pylib/anki/importing/anki2.py
@@ -5,7 +5,7 @@
import os
import unicodedata
-from typing import Optional
+from typing import Any, Optional
from anki.cards import CardId
from anki.collection import Collection
diff --git a/pylib/anki/importing/csvfile.py b/pylib/anki/importing/csvfile.py
index bebbdf5b2..6826ddecf 100644
--- a/pylib/anki/importing/csvfile.py
+++ b/pylib/anki/importing/csvfile.py
@@ -7,7 +7,7 @@ from __future__ import annotations
import csv
import re
-from typing import Any, Optional, TextIO
+from typing import Any, TextIO
from anki.collection import Collection
from anki.importing.noteimp import ForeignNote, NoteImporter
@@ -20,12 +20,12 @@ class TextImporter(NoteImporter):
def __init__(self, col: Collection, file: str) -> None:
NoteImporter.__init__(self, col, file)
self.lines = None
- self.fileobj: Optional[TextIO] = None
- self.delimiter: Optional[str] = None
+ self.fileobj: TextIO | None = None
+ self.delimiter: str | None = None
self.tagsToAdd: list[str] = []
self.numFields = 0
- self.dialect: Optional[Any]
- self.data: Optional[str | list[str]]
+ self.dialect: Any | None
+ self.data: str | list[str] | None
def foreignNotes(self) -> list[ForeignNote]:
self.open()
diff --git a/pylib/anki/importing/noteimp.py b/pylib/anki/importing/noteimp.py
index 688e75eca..f69696ef8 100644
--- a/pylib/anki/importing/noteimp.py
+++ b/pylib/anki/importing/noteimp.py
@@ -7,7 +7,7 @@ from __future__ import annotations
import html
import unicodedata
-from typing import Optional, Union
+from typing import Union
from anki.collection import Collection
from anki.config import Config
@@ -76,8 +76,8 @@ class NoteImporter(Importer):
needDelimiter = False
allowHTML = False
importMode = UPDATE_MODE
- mapping: Optional[list[str]]
- tagModified: Optional[str]
+ mapping: list[str] | None
+ tagModified: str | None
def __init__(self, col: Collection, file: str) -> None:
Importer.__init__(self, col, file)
@@ -268,7 +268,7 @@ class NoteImporter(Importer):
def updateData(
self, n: ForeignNote, id: NoteId, sflds: list[str]
- ) -> Optional[Updates]:
+ ) -> Updates | None:
self._ids.append(id)
self.processFields(n, sflds)
if self._tagsMapped:
@@ -316,9 +316,7 @@ where id = ? and flds != ?""",
changes2 = self.col.db.scalar("select total_changes()")
self.updateCount = changes2 - changes
- def processFields(
- self, note: ForeignNote, fields: Optional[list[str]] = None
- ) -> None:
+ def processFields(self, note: ForeignNote, fields: list[str] | None = None) -> None:
if not fields:
fields = [""] * len(self.model["flds"])
for c, f in enumerate(self.mapping):
diff --git a/pylib/anki/media.py b/pylib/anki/media.py
index 49d2a9d1f..cb7c1ac5f 100644
--- a/pylib/anki/media.py
+++ b/pylib/anki/media.py
@@ -8,7 +8,8 @@ import pprint
import re
import sys
import time
-from typing import Callable, Sequence
+from collections.abc import Sequence
+from typing import Callable
from anki import media_pb2
from anki._legacy import DeprecatedNamesMixin, deprecated_keywords
diff --git a/pylib/anki/models.py b/pylib/anki/models.py
index 9301ca025..09035b30a 100644
--- a/pylib/anki/models.py
+++ b/pylib/anki/models.py
@@ -7,7 +7,8 @@ import copy
import pprint
import sys
import time
-from typing import Any, NewType, Sequence, Union
+from collections.abc import Sequence
+from typing import Any, NewType, Union
import anki # pylint: disable=unused-import
import anki.collection
diff --git a/pylib/anki/notes.py b/pylib/anki/notes.py
index e0147ac3a..aab8a1b63 100644
--- a/pylib/anki/notes.py
+++ b/pylib/anki/notes.py
@@ -4,7 +4,8 @@
from __future__ import annotations
import copy
-from typing import NewType, Sequence
+from collections.abc import Sequence
+from typing import NewType
import anki # pylint: disable=unused-import
import anki.cards
diff --git a/pylib/anki/scheduler/base.py b/pylib/anki/scheduler/base.py
index f1498ef7c..83ef9d393 100644
--- a/pylib/anki/scheduler/base.py
+++ b/pylib/anki/scheduler/base.py
@@ -22,7 +22,8 @@ ScheduleCardsAsNewDefaults = scheduler_pb2.ScheduleCardsAsNewDefaultsResponse
FilteredDeckForUpdate = decks_pb2.FilteredDeckForUpdate
RepositionDefaults = scheduler_pb2.RepositionDefaultsResponse
-from typing import Sequence, overload
+from collections.abc import Sequence
+from typing import overload
from anki import config_pb2
from anki.cards import CardId
diff --git a/pylib/anki/scheduler/legacy.py b/pylib/anki/scheduler/legacy.py
index a5e90a677..58bed7933 100644
--- a/pylib/anki/scheduler/legacy.py
+++ b/pylib/anki/scheduler/legacy.py
@@ -5,8 +5,6 @@
from __future__ import annotations
-from typing import Optional
-
from anki._legacy import deprecated
from anki.cards import Card, CardId
from anki.consts import (
@@ -54,7 +52,7 @@ class SchedulerBaseWithLegacy(SchedulerBase):
print("_nextDueMsg() is obsolete")
return ""
- def rebuildDyn(self, did: Optional[DeckId] = None) -> Optional[int]:
+ def rebuildDyn(self, did: DeckId | None = None) -> int | None:
did = did or self.col.decks.selected()
count = self.rebuild_filtered_deck(did).count or None
if not count:
@@ -63,7 +61,7 @@ class SchedulerBaseWithLegacy(SchedulerBase):
self.col.decks.select(did)
return count
- def emptyDyn(self, did: Optional[DeckId], lim: Optional[str] = None) -> None:
+ def emptyDyn(self, did: DeckId | None, lim: str | None = None) -> None:
if lim is None:
self.empty_filtered_deck(did)
return
diff --git a/pylib/anki/scheduler/v3.py b/pylib/anki/scheduler/v3.py
index d7d9e1837..2a18ee021 100644
--- a/pylib/anki/scheduler/v3.py
+++ b/pylib/anki/scheduler/v3.py
@@ -14,7 +14,8 @@ as '2' internally.
from __future__ import annotations
-from typing import Literal, Optional, Sequence
+from collections.abc import Sequence
+from typing import Any, Literal
from anki import frontend_pb2, scheduler_pb2
from anki._legacy import deprecated
@@ -109,7 +110,7 @@ class Scheduler(SchedulerBaseWithLegacy):
# backend automatically resets queues as operations are performed
pass
- def getCard(self) -> Optional[Card]:
+ def getCard(self) -> Card | None:
"""Fetch the next card from the queue. None if finished."""
try:
queued_card = self.get_queued_cards().cards[0]
@@ -125,7 +126,7 @@ class Scheduler(SchedulerBaseWithLegacy):
"Don't use this, it is a stop-gap until this code is refactored."
return not self.get_queued_cards().cards
- def counts(self, card: Optional[Card] = None) -> tuple[int, int, int]:
+ def counts(self, card: Card | None = None) -> tuple[int, int, int]:
info = self.get_queued_cards()
return (info.new_count, info.learning_count, info.review_count)
diff --git a/pylib/anki/stats.py b/pylib/anki/stats.py
index dc8997606..4fbc47e07 100644
--- a/pylib/anki/stats.py
+++ b/pylib/anki/stats.py
@@ -8,7 +8,8 @@ from __future__ import annotations
import json
import random
import time
-from typing import Sequence
+from collections.abc import Sequence
+from typing import Any
import anki.cards
import anki.collection
diff --git a/pylib/anki/tags.py b/pylib/anki/tags.py
index 3f75ca793..a54aa7901 100644
--- a/pylib/anki/tags.py
+++ b/pylib/anki/tags.py
@@ -13,7 +13,8 @@ from __future__ import annotations
import pprint
import re
-from typing import Collection, Match, Sequence
+from collections.abc import Collection, Sequence
+from typing import Match
import anki # pylint: disable=unused-import
import anki.collection
diff --git a/pylib/anki/template.py b/pylib/anki/template.py
index c4a31c2ef..deda8c819 100644
--- a/pylib/anki/template.py
+++ b/pylib/anki/template.py
@@ -28,8 +28,9 @@ template_legacy.py file, using the legacy addHook() system.
from __future__ import annotations
+from collections.abc import Sequence
from dataclasses import dataclass
-from typing import Any, Sequence, Union
+from typing import Any, Union
import anki
import anki.cards
diff --git a/pylib/anki/utils.py b/pylib/anki/utils.py
index 150a210fb..929ca3ce1 100644
--- a/pylib/anki/utils.py
+++ b/pylib/anki/utils.py
@@ -13,9 +13,10 @@ import subprocess
import sys
import tempfile
import time
+from collections.abc import Iterable, Iterator
from contextlib import contextmanager
from hashlib import sha1
-from typing import TYPE_CHECKING, Any, Callable, Iterable, Iterator
+from typing import TYPE_CHECKING, Any, Callable
from anki._legacy import DeprecatedNamesMixinForModule
from anki.dbproxy import DBProxy
diff --git a/pylib/tests/test_schedv3.py b/pylib/tests/test_schedv3.py
index 2a1f79213..d4d4bfd4c 100644
--- a/pylib/tests/test_schedv3.py
+++ b/pylib/tests/test_schedv3.py
@@ -385,7 +385,7 @@ def test_reviews():
assert "leech" in c.note().tags
-def review_limits_setup() -> tuple[anki.collection.Collection, Dict]:
+def review_limits_setup() -> tuple[anki.collection.Collection, dict]:
col = getEmptyCol()
parent = col.decks.get(col.decks.id("parent"))
diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py
index 42e02e1c2..0591b95ff 100644
--- a/qt/aqt/__init__.py
+++ b/qt/aqt/__init__.py
@@ -5,6 +5,8 @@ from __future__ import annotations
import logging
import sys
+from collections.abc import Callable
+from typing import TYPE_CHECKING, Any, Union, cast
try:
import pip_system_certs.wrapt_requests
@@ -50,7 +52,6 @@ import os
import tempfile
import traceback
from pathlib import Path
-from typing import TYPE_CHECKING, Any, Callable, Optional, cast
import anki.lang
from anki._backend import RustBackend
@@ -92,8 +93,8 @@ appHelpSite = HELP_SITE
from aqt.main import AnkiQt # isort:skip
from aqt.profiles import ProfileManager, VideoDriver # isort:skip
-profiler: Optional[cProfile.Profile] = None
-mw: Optional[AnkiQt] = None # set on init
+profiler: cProfile.Profile | None = None
+mw: AnkiQt | None = None # set on init
import aqt.forms
@@ -154,7 +155,7 @@ class DialogManager:
def allClosed(self) -> bool:
return not any(x[1] for x in self._dialogs.values())
- def closeAll(self, onsuccess: Callable[[], None]) -> Optional[bool]:
+ def closeAll(self, onsuccess: Callable[[], None]) -> bool | None:
# can we close immediately?
if self.allClosed():
onsuccess()
@@ -181,7 +182,7 @@ class DialogManager:
return True
def register_dialog(
- self, name: str, creator: Union[Callable, type], instance: Optional[Any] = None
+ self, name: str, creator: Callable | type, instance: Any | None = None
) -> None:
"""Allows add-ons to register a custom dialog to be managed by Anki's dialog
manager, which ensures that only one copy of the window is open at once,
@@ -219,13 +220,13 @@ dialogs = DialogManager()
# A reference to the Qt translator needs to be held to prevent it from
# being immediately deallocated.
-_qtrans: Optional[QTranslator] = None
+_qtrans: QTranslator | None = None
def setupLangAndBackend(
pm: ProfileManager,
app: QApplication,
- force: Optional[str] = None,
+ force: str | None = None,
firstTime: bool = False,
) -> RustBackend:
global _qtrans
@@ -288,7 +289,7 @@ def setupLangAndBackend(
class NativeEventFilter(QAbstractNativeEventFilter):
def nativeEventFilter(
self, eventType: Any, message: Any
- ) -> tuple[bool, Optional[sip.voidptr]]:
+ ) -> tuple[bool, sip.voidptr | None]:
if eventType == "windows_generic_MSG":
import ctypes
@@ -563,7 +564,7 @@ def run() -> None:
)
-def _run(argv: Optional[list[str]] = None, exec: bool = True) -> Optional[AnkiApp]:
+def _run(argv: list[str] | None = None, exec: bool = True) -> AnkiApp | None:
"""Start AnkiQt application or reuse an existing instance if one exists.
If the function is invoked with exec=False, the AnkiQt will not enter
diff --git a/qt/aqt/about.py b/qt/aqt/about.py
index 2a3e29224..ec2a71f99 100644
--- a/qt/aqt/about.py
+++ b/qt/aqt/about.py
@@ -2,6 +2,7 @@
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import platform
+from collections.abc import Callable
import aqt.forms
from anki.lang import without_unicode_isolation
diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py
index 34bfb10c5..bf11e5894 100644
--- a/qt/aqt/addcards.py
+++ b/qt/aqt/addcards.py
@@ -3,7 +3,7 @@
from __future__ import annotations
-from typing import Callable, Optional
+from typing import Callable
import aqt.editor
import aqt.forms
@@ -54,7 +54,7 @@ class AddCards(QMainWindow):
self.setupButtons()
self.col.add_image_occlusion_notetype()
self.history: list[NoteId] = []
- self._last_added_note: Optional[Note] = None
+ self._last_added_note: Note | None = None
gui_hooks.operation_did_execute.append(self.on_operation_did_execute)
restoreGeom(self, "add")
gui_hooks.add_cards_did_init(self)
@@ -195,7 +195,7 @@ class AddCards(QMainWindow):
self, old_note.note_type(), new_note.note_type()
)
- def _load_new_note(self, sticky_fields_from: Optional[Note] = None) -> None:
+ def _load_new_note(self, sticky_fields_from: Note | None = None) -> None:
note = self._new_note()
if old_note := sticky_fields_from:
flds = note.note_type()["flds"]
@@ -209,7 +209,7 @@ class AddCards(QMainWindow):
self.setAndFocusNote(note)
def on_operation_did_execute(
- self, changes: OpChanges, handler: Optional[object]
+ self, changes: OpChanges, handler: object | None
) -> None:
if (changes.notetype or changes.deck) and handler is not self.editor:
self.on_notetype_change(
diff --git a/qt/aqt/addons.py b/qt/aqt/addons.py
index 70561b9a0..313f63b69 100644
--- a/qt/aqt/addons.py
+++ b/qt/aqt/addons.py
@@ -11,11 +11,12 @@ import os
import re
import zipfile
from collections import defaultdict
+from collections.abc import Iterable, Sequence
from concurrent.futures import Future
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
-from typing import IO, Any, Callable, Iterable, Sequence, Union
+from typing import IO, Any, Callable, Union
from urllib.parse import parse_qs, urlparse
from zipfile import ZipFile
diff --git a/qt/aqt/browser/browser.py b/qt/aqt/browser/browser.py
index e8dcff326..f8a673410 100644
--- a/qt/aqt/browser/browser.py
+++ b/qt/aqt/browser/browser.py
@@ -6,7 +6,8 @@ from __future__ import annotations
import json
import math
import re
-from typing import Callable, Sequence
+from collections.abc import Sequence
+from typing import Any, Callable
import aqt
import aqt.browser
diff --git a/qt/aqt/browser/find_and_replace.py b/qt/aqt/browser/find_and_replace.py
index a68c75dbc..c1d51e43c 100644
--- a/qt/aqt/browser/find_and_replace.py
+++ b/qt/aqt/browser/find_and_replace.py
@@ -3,7 +3,7 @@
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
import aqt
import aqt.forms
diff --git a/qt/aqt/browser/sidebar/item.py b/qt/aqt/browser/sidebar/item.py
index 6a32097da..e7e4e931b 100644
--- a/qt/aqt/browser/sidebar/item.py
+++ b/qt/aqt/browser/sidebar/item.py
@@ -2,8 +2,9 @@
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from __future__ import annotations
+from collections.abc import Iterable
from enum import Enum, auto
-from typing import Callable, Iterable
+from typing import Callable
from anki.collection import SearchNode
from aqt.theme import ColoredIcon
diff --git a/qt/aqt/browser/sidebar/toolbar.py b/qt/aqt/browser/sidebar/toolbar.py
index a111fe3e4..78906b580 100644
--- a/qt/aqt/browser/sidebar/toolbar.py
+++ b/qt/aqt/browser/sidebar/toolbar.py
@@ -3,6 +3,7 @@
from __future__ import annotations
+from collections.abc import Callable
from enum import Enum, auto
import aqt
diff --git a/qt/aqt/browser/sidebar/tree.py b/qt/aqt/browser/sidebar/tree.py
index ef03cc91e..75fb1a711 100644
--- a/qt/aqt/browser/sidebar/tree.py
+++ b/qt/aqt/browser/sidebar/tree.py
@@ -2,8 +2,9 @@
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from __future__ import annotations
+from collections.abc import Callable, Iterable
from enum import Enum, auto
-from typing import Iterable, cast
+from typing import cast
import aqt
import aqt.browser
@@ -266,7 +267,7 @@ class SidebarTreeView(QTreeView):
def update_search(
self,
- *terms: Union[str, SearchNode],
+ *terms: str | SearchNode,
joiner: SearchJoiner = "AND",
) -> None:
"""Modify the current search string based on modifier keys, then refresh."""
@@ -524,7 +525,7 @@ class SidebarTreeView(QTreeView):
*,
root: SidebarItem,
name: str,
- icon: Union[str, ColoredIcon],
+ icon: str | ColoredIcon,
collapse_key: Config.Bool.V,
type: SidebarItemType | None = None,
) -> SidebarItem:
diff --git a/qt/aqt/browser/table/__init__.py b/qt/aqt/browser/table/__init__.py
index 7be9f1fcb..dc3ed7d5d 100644
--- a/qt/aqt/browser/table/__init__.py
+++ b/qt/aqt/browser/table/__init__.py
@@ -4,8 +4,9 @@ from __future__ import annotations
import copy
import time
+from collections.abc import Generator, Sequence
from dataclasses import dataclass
-from typing import TYPE_CHECKING, Generator, Sequence, Union
+from typing import TYPE_CHECKING, Union
import aqt
import aqt.browser
diff --git a/qt/aqt/browser/table/model.py b/qt/aqt/browser/table/model.py
index 956c51c19..99e2bbb73 100644
--- a/qt/aqt/browser/table/model.py
+++ b/qt/aqt/browser/table/model.py
@@ -3,7 +3,8 @@
from __future__ import annotations
import time
-from typing import Any, Callable, Sequence
+from collections.abc import Sequence
+from typing import Any, Callable
import aqt
import aqt.browser
diff --git a/qt/aqt/browser/table/state.py b/qt/aqt/browser/table/state.py
index 91f368844..75ee69ccb 100644
--- a/qt/aqt/browser/table/state.py
+++ b/qt/aqt/browser/table/state.py
@@ -3,7 +3,8 @@
from __future__ import annotations
from abc import ABC, abstractmethod, abstractproperty
-from typing import Sequence, cast
+from collections.abc import Sequence
+from typing import cast
from anki.browser import BrowserConfig
from anki.cards import Card, CardId
diff --git a/qt/aqt/browser/table/table.py b/qt/aqt/browser/table/table.py
index 384a28e67..0be52243b 100644
--- a/qt/aqt/browser/table/table.py
+++ b/qt/aqt/browser/table/table.py
@@ -2,7 +2,8 @@
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
from __future__ import annotations
-from typing import Any, Callable, Sequence
+from collections.abc import Sequence
+from typing import Any, Callable
import aqt
import aqt.browser
diff --git a/qt/aqt/changenotetype.py b/qt/aqt/changenotetype.py
index 3f637eabf..0484c2ff8 100644
--- a/qt/aqt/changenotetype.py
+++ b/qt/aqt/changenotetype.py
@@ -3,7 +3,7 @@
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
import aqt
import aqt.deckconf
diff --git a/qt/aqt/clayout.py b/qt/aqt/clayout.py
index 19fa0fa22..bdd7f02d3 100644
--- a/qt/aqt/clayout.py
+++ b/qt/aqt/clayout.py
@@ -5,8 +5,9 @@ from __future__ import annotations
import json
import re
+from collections.abc import Callable
from concurrent.futures import Future
-from typing import Any, Match, Optional, cast
+from typing import Any, Match, cast
import aqt
import aqt.forms
@@ -50,7 +51,7 @@ class CardLayout(QDialog):
mw: AnkiQt,
note: Note,
ord: int = 0,
- parent: Optional[QWidget] = None,
+ parent: QWidget | None = None,
fill_empty: bool = False,
) -> None:
QDialog.__init__(self, parent or mw, Qt.WindowType.Window)
@@ -509,7 +510,7 @@ class CardLayout(QDialog):
# Preview
##########################################################################
- _previewTimer: Optional[QTimer] = None
+ _previewTimer: QTimer | None = None
def renderPreview(self) -> None:
# schedule a preview when timing stops
@@ -590,7 +591,7 @@ class CardLayout(QDialog):
return res
type_filter = r"\[\[type:.+?\]\]"
- repl: Union[str, Callable]
+ repl: str | Callable
if type == "q":
repl = ""
diff --git a/qt/aqt/customstudy.py b/qt/aqt/customstudy.py
index d499ae7b3..730f3843b 100644
--- a/qt/aqt/customstudy.py
+++ b/qt/aqt/customstudy.py
@@ -3,8 +3,6 @@
from __future__ import annotations
-from typing import Tuple
-
import aqt
import aqt.forms
import aqt.operations
@@ -37,12 +35,12 @@ class CustomStudy(QDialog):
def fetch_data_and_show(mw: aqt.AnkiQt) -> None:
def fetch_data(
col: Collection,
- ) -> Tuple[DeckId, CustomStudyDefaults]:
+ ) -> tuple[DeckId, CustomStudyDefaults]:
deck_id = mw.col.decks.get_current_id()
defaults = col.sched.custom_study_defaults(deck_id)
return (deck_id, defaults)
- def show_dialog(data: Tuple[DeckId, CustomStudyDefaults]) -> None:
+ def show_dialog(data: tuple[DeckId, CustomStudyDefaults]) -> None:
deck_id, defaults = data
CustomStudy(mw=mw, deck_id=deck_id, defaults=defaults)
diff --git a/qt/aqt/debug_console.py b/qt/aqt/debug_console.py
index a24ade4fc..3ad96fcbf 100644
--- a/qt/aqt/debug_console.py
+++ b/qt/aqt/debug_console.py
@@ -4,6 +4,7 @@
from __future__ import annotations
import os
+from collections.abc import Callable
from dataclasses import dataclass
from functools import partial
from pathlib import Path
diff --git a/qt/aqt/deckchooser.py b/qt/aqt/deckchooser.py
index 6b089d8b2..83af93971 100644
--- a/qt/aqt/deckchooser.py
+++ b/qt/aqt/deckchooser.py
@@ -3,6 +3,8 @@
from __future__ import annotations
+from collections.abc import Callable
+
from anki.collection import OpChanges
from anki.decks import DEFAULT_DECK_ID, DeckId
from aqt import AnkiQt, gui_hooks
diff --git a/qt/aqt/deckconf.py b/qt/aqt/deckconf.py
index b28813196..8626f0a6a 100644
--- a/qt/aqt/deckconf.py
+++ b/qt/aqt/deckconf.py
@@ -173,8 +173,8 @@ class DeckConf(QDialog):
# Loading
##################################################
- def listToUser(self, l: list[Union[int, float]]) -> str:
- def num_to_user(n: Union[int, float]) -> str:
+ def listToUser(self, l: list[int | float]) -> str:
+ def num_to_user(n: int | float) -> str:
if n == round(n):
return str(int(n))
else:
diff --git a/qt/aqt/editcurrent.py b/qt/aqt/editcurrent.py
index 48140d789..e3e8ab517 100644
--- a/qt/aqt/editcurrent.py
+++ b/qt/aqt/editcurrent.py
@@ -1,5 +1,6 @@
# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
+from collections.abc import Callable
from typing import Optional
import aqt.editor
diff --git a/qt/aqt/errors.py b/qt/aqt/errors.py
index 41701d372..b1e729dd9 100644
--- a/qt/aqt/errors.py
+++ b/qt/aqt/errors.py
@@ -5,7 +5,7 @@ from __future__ import annotations
import re
import time
-from typing import TYPE_CHECKING, Optional, TextIO, cast
+from typing import TYPE_CHECKING, TextIO, cast
from markdown import markdown
@@ -169,7 +169,7 @@ class ErrorHandler(QObject):
def __init__(self, mw: AnkiQt) -> None:
QObject.__init__(self, mw)
self.mw = mw
- self.timer: Optional[QTimer] = None
+ self.timer: QTimer | None = None
qconnect(self.errorTimer, self._setTimer)
self.pool = ""
self._oldstderr = sys.stderr
diff --git a/qt/aqt/exporting.py b/qt/aqt/exporting.py
index 149d486e6..ee41030f7 100644
--- a/qt/aqt/exporting.py
+++ b/qt/aqt/exporting.py
@@ -7,7 +7,6 @@ import os
import re
import time
from concurrent.futures import Future
-from typing import Optional
import aqt
import aqt.forms
@@ -35,7 +34,7 @@ class ExportDialog(QDialog):
mw: aqt.main.AnkiQt,
did: DeckId | None = None,
cids: list[CardId] | None = None,
- parent: Optional[QWidget] = None,
+ parent: QWidget | None = None,
):
QDialog.__init__(self, parent or mw, Qt.WindowType.Window)
self.mw = mw
diff --git a/qt/aqt/fields.py b/qt/aqt/fields.py
index 7044e0e85..da81c9ff7 100644
--- a/qt/aqt/fields.py
+++ b/qt/aqt/fields.py
@@ -3,8 +3,6 @@
from __future__ import annotations
-from typing import Optional
-
import aqt
import aqt.forms
import aqt.operations
@@ -32,7 +30,7 @@ class FieldDialog(QDialog):
self,
mw: AnkiQt,
nt: NotetypeDict,
- parent: Optional[QWidget] = None,
+ parent: QWidget | None = None,
open_at: int = 0,
) -> None:
QDialog.__init__(self, parent or mw)
@@ -62,7 +60,7 @@ class FieldDialog(QDialog):
self.form.buttonBox.button(QDialogButtonBox.StandardButton.Save).setAutoDefault(
False
)
- self.currentIdx: Optional[int] = None
+ self.currentIdx: int | None = None
self.fillFields()
self.setupSignals()
self.form.fieldList.setDragDropMode(QAbstractItemView.DragDropMode.InternalMove)
@@ -125,8 +123,8 @@ class FieldDialog(QDialog):
self.loadField(idx)
def _uniqueName(
- self, prompt: str, ignoreOrd: Optional[int] = None, old: str = ""
- ) -> Optional[str]:
+ self, prompt: str, ignoreOrd: int | None = None, old: str = ""
+ ) -> str | None:
txt = getOnlyText(prompt, default=old).replace('"', "").strip()
if not txt:
return None
diff --git a/qt/aqt/import_export/exporting.py b/qt/aqt/import_export/exporting.py
index f06d7a47a..a33395f98 100644
--- a/qt/aqt/import_export/exporting.py
+++ b/qt/aqt/import_export/exporting.py
@@ -7,8 +7,8 @@ import os
import re
import time
from abc import ABC, abstractmethod
+from collections.abc import Sequence
from dataclasses import dataclass
-from typing import Optional, Sequence, Type
import aqt.forms
import aqt.main
@@ -42,7 +42,7 @@ class ExportDialog(QDialog):
mw: aqt.main.AnkiQt,
did: DeckId | None = None,
nids: Sequence[NoteId] | None = None,
- parent: Optional[QWidget] = None,
+ parent: QWidget | None = None,
):
QDialog.__init__(self, parent or mw, Qt.WindowType.Window)
self.mw = mw
@@ -56,7 +56,7 @@ class ExportDialog(QDialog):
self.open()
def setup(self, did: DeckId | None) -> None:
- self.exporter_classes: list[Type[Exporter]] = [
+ self.exporter_classes: list[type[Exporter]] = [
ApkgExporter,
ColpkgExporter,
NoteCsvExporter,
diff --git a/qt/aqt/import_export/importing.py b/qt/aqt/import_export/importing.py
index 05a37aece..e7174533e 100644
--- a/qt/aqt/import_export/importing.py
+++ b/qt/aqt/import_export/importing.py
@@ -5,8 +5,8 @@ from __future__ import annotations
import re
from abc import ABC, abstractmethod
+from collections.abc import Callable
from itertools import chain
-from typing import Type
import aqt.main
from anki.collection import Collection, Progress
@@ -124,7 +124,7 @@ class JsonImporter(Importer):
ImportDialog(mw, JsonFileArgs(path=path))
-IMPORTERS: list[Type[Importer]] = [
+IMPORTERS: list[type[Importer]] = [
ColpkgImporter,
ApkgImporter,
MnemosyneImporter,
diff --git a/qt/aqt/importing.py b/qt/aqt/importing.py
index 8ba173f91..a78e4a8d2 100644
--- a/qt/aqt/importing.py
+++ b/qt/aqt/importing.py
@@ -4,6 +4,7 @@ import os
import re
import traceback
import zipfile
+from collections.abc import Callable
from concurrent.futures import Future
from typing import Any, Optional
diff --git a/qt/aqt/main.py b/qt/aqt/main.py
index e7159977d..fad9c5829 100644
--- a/qt/aqt/main.py
+++ b/qt/aqt/main.py
@@ -10,8 +10,9 @@ import re
import signal
import weakref
from argparse import Namespace
+from collections.abc import Callable, Sequence
from concurrent.futures import Future
-from typing import Any, Literal, Sequence, TypeVar, cast
+from typing import Any, Literal, TypeVar, cast
import anki
import anki.cards
diff --git a/qt/aqt/mediacheck.py b/qt/aqt/mediacheck.py
index d9691c500..ec47550d8 100644
--- a/qt/aqt/mediacheck.py
+++ b/qt/aqt/mediacheck.py
@@ -5,8 +5,9 @@ from __future__ import annotations
import itertools
import time
+from collections.abc import Iterable, Sequence
from concurrent.futures import Future
-from typing import Iterable, Sequence, TypeVar
+from typing import TypeVar
import aqt
import aqt.progress
diff --git a/qt/aqt/modelchooser.py b/qt/aqt/modelchooser.py
index f886c65af..f2fceccc2 100644
--- a/qt/aqt/modelchooser.py
+++ b/qt/aqt/modelchooser.py
@@ -1,6 +1,7 @@
# Copyright: Ankitects Pty Ltd and contributors
# License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
+from collections.abc import Callable
from typing import Optional
from aqt import AnkiQt, gui_hooks
diff --git a/qt/aqt/models.py b/qt/aqt/models.py
index 2ad890616..43b33dabd 100644
--- a/qt/aqt/models.py
+++ b/qt/aqt/models.py
@@ -3,9 +3,10 @@
from __future__ import annotations
+from collections.abc import Callable, Sequence
from concurrent.futures import Future
from operator import itemgetter
-from typing import Any, Optional, Sequence
+from typing import Any
import aqt.clayout
from anki import stdmodels
@@ -40,9 +41,9 @@ class Models(QDialog):
def __init__(
self,
mw: AnkiQt,
- parent: Optional[QWidget] = None,
+ parent: QWidget | None = None,
fromMain: bool = False,
- selected_notetype_id: Optional[NotetypeId] = None,
+ selected_notetype_id: NotetypeId | None = None,
):
self.mw = mw
parent = parent or mw
@@ -231,13 +232,13 @@ class Models(QDialog):
class AddModel(QDialog):
- model: Optional[NotetypeDict]
+ model: NotetypeDict | None
def __init__(
self,
mw: AnkiQt,
on_success: Callable[[NotetypeDict], None],
- parent: Optional[QWidget] = None,
+ parent: QWidget | None = None,
) -> None:
self.parent_ = parent or mw
self.mw = mw
@@ -249,9 +250,7 @@ class AddModel(QDialog):
self.setWindowModality(Qt.WindowModality.ApplicationModal)
disable_help_button(self)
# standard models
- self.notetypes: list[
- Union[NotetypeDict, Callable[[Collection], NotetypeDict]]
- ] = []
+ self.notetypes: list[NotetypeDict | Callable[[Collection], NotetypeDict]] = []
for name, func in stdmodels.get_stock_notetypes(self.col):
item = QListWidgetItem(tr.notetypes_add(val=name))
self.dialog.models.addItem(item)
diff --git a/qt/aqt/notetypechooser.py b/qt/aqt/notetypechooser.py
index e8d614ac3..395af193b 100644
--- a/qt/aqt/notetypechooser.py
+++ b/qt/aqt/notetypechooser.py
@@ -3,6 +3,8 @@
from __future__ import annotations
+from collections.abc import Callable
+
from anki.collection import OpChanges
from anki.models import NotetypeId
from aqt import AnkiQt, gui_hooks
diff --git a/qt/aqt/operations/card.py b/qt/aqt/operations/card.py
index 6071336ee..e8b4b23a6 100644
--- a/qt/aqt/operations/card.py
+++ b/qt/aqt/operations/card.py
@@ -3,7 +3,7 @@
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
from anki.cards import CardId
from anki.collection import OpChangesWithCount
diff --git a/qt/aqt/operations/deck.py b/qt/aqt/operations/deck.py
index bddf07fc2..327f9ab52 100644
--- a/qt/aqt/operations/deck.py
+++ b/qt/aqt/operations/deck.py
@@ -3,7 +3,7 @@
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
from anki.collection import OpChanges, OpChangesWithCount, OpChangesWithId
from anki.decks import DeckCollapseScope, DeckDict, DeckId, UpdateDeckConfigs
diff --git a/qt/aqt/operations/note.py b/qt/aqt/operations/note.py
index 42b2bc950..4a27c1e21 100644
--- a/qt/aqt/operations/note.py
+++ b/qt/aqt/operations/note.py
@@ -3,7 +3,7 @@
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
from anki.collection import OpChanges, OpChangesWithCount
from anki.decks import DeckId
diff --git a/qt/aqt/operations/scheduling.py b/qt/aqt/operations/scheduling.py
index c30fdecb8..61d7cc9d4 100644
--- a/qt/aqt/operations/scheduling.py
+++ b/qt/aqt/operations/scheduling.py
@@ -3,7 +3,7 @@
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
import aqt
import aqt.forms
diff --git a/qt/aqt/operations/tag.py b/qt/aqt/operations/tag.py
index bf3d25945..2ac4504d0 100644
--- a/qt/aqt/operations/tag.py
+++ b/qt/aqt/operations/tag.py
@@ -3,7 +3,7 @@
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Sequence
from anki.collection import OpChanges, OpChangesWithCount
from anki.notes import NoteId
diff --git a/qt/aqt/preferences.py b/qt/aqt/preferences.py
index f1dab502d..777c32cd4 100644
--- a/qt/aqt/preferences.py
+++ b/qt/aqt/preferences.py
@@ -5,6 +5,7 @@ from __future__ import annotations
import functools
import re
+from collections.abc import Callable
import anki.lang
import aqt
diff --git a/qt/aqt/profiles.py b/qt/aqt/profiles.py
index 185fe4fd3..7da170408 100644
--- a/qt/aqt/profiles.py
+++ b/qt/aqt/profiles.py
@@ -10,7 +10,7 @@ import shutil
import traceback
from enum import Enum
from pathlib import Path
-from typing import TYPE_CHECKING, Any, Optional
+from typing import TYPE_CHECKING, Any
import anki.lang
import aqt.forms
@@ -544,7 +544,7 @@ create table if not exists profiles
def set_spacebar_rates_card(self, on: bool) -> None:
self.meta["spacebar_rates_card"] = on
- def get_answer_key(self, ease: int) -> Optional[str]:
+ def get_answer_key(self, ease: int) -> str | None:
return self.meta.setdefault("answer_keys", self.default_answer_keys).get(ease)
def set_answer_key(self, ease: int, key: str):
diff --git a/qt/aqt/progress.py b/qt/aqt/progress.py
index 692268a79..f4f17fb6c 100644
--- a/qt/aqt/progress.py
+++ b/qt/aqt/progress.py
@@ -3,6 +3,7 @@
from __future__ import annotations
import time
+from collections.abc import Callable
from concurrent.futures import Future
from dataclasses import dataclass
diff --git a/qt/aqt/qt/qt5_audio.py b/qt/aqt/qt/qt5_audio.py
index ffab7477e..cc8426a6e 100644
--- a/qt/aqt/qt/qt5_audio.py
+++ b/qt/aqt/qt/qt5_audio.py
@@ -8,6 +8,7 @@ PyQt5-only audio code
"""
import wave
+from collections.abc import Callable
from concurrent.futures import Future
from typing import cast
diff --git a/qt/aqt/reviewer.py b/qt/aqt/reviewer.py
index 70afc54cf..5058e5177 100644
--- a/qt/aqt/reviewer.py
+++ b/qt/aqt/reviewer.py
@@ -7,9 +7,10 @@ import functools
import json
import random
import re
+from collections.abc import Callable, Sequence
from dataclasses import dataclass
from enum import Enum, auto
-from typing import Any, Literal, Match, Sequence, cast
+from typing import Any, Literal, Match, Union, cast
import aqt
import aqt.browser
@@ -568,7 +569,7 @@ class Reviewer:
def korean_shortcuts(
self,
- ) -> Sequence[Union[tuple[str, Callable], tuple[Qt.Key, Callable]]]:
+ ) -> Sequence[tuple[str, Callable] | tuple[Qt.Key, Callable]]:
return [
("ㄷ", self.mw.onEditCurrent),
("ㅡ", self.showContextMenu),
@@ -588,7 +589,7 @@ class Reviewer:
def _shortcutKeys(
self,
- ) -> Sequence[Union[tuple[str, Callable], tuple[Qt.Key, Callable]]]:
+ ) -> Sequence[tuple[str, Callable] | tuple[Qt.Key, Callable]]:
return [
("e", self.mw.onEditCurrent),
(" ", self.onEnterKey),
@@ -839,7 +840,7 @@ timerStopped = false;
if not self.mw.col.conf["dueCounts"]:
return ""
- counts: list[Union[int, str]]
+ counts: list[int | str]
idx, counts_ = self._v3.counts()
counts = cast(list[Union[int, str]], counts_)
counts[idx] = f"{counts[idx]}"
diff --git a/qt/aqt/stats.py b/qt/aqt/stats.py
index 388f2d89e..8116f1b2c 100644
--- a/qt/aqt/stats.py
+++ b/qt/aqt/stats.py
@@ -3,6 +3,7 @@
from __future__ import annotations
import time
+from collections.abc import Callable
from typing import Any
import aqt
diff --git a/qt/aqt/tagedit.py b/qt/aqt/tagedit.py
index 90cbf56ed..c88fc1845 100644
--- a/qt/aqt/tagedit.py
+++ b/qt/aqt/tagedit.py
@@ -4,7 +4,7 @@
from __future__ import annotations
import re
-from typing import Iterable
+from collections.abc import Iterable
from anki.collection import Collection
from aqt import gui_hooks
diff --git a/qt/aqt/taglimit.py b/qt/aqt/taglimit.py
index 5cc172a66..4132deafa 100644
--- a/qt/aqt/taglimit.py
+++ b/qt/aqt/taglimit.py
@@ -3,7 +3,7 @@
from __future__ import annotations
-from typing import Sequence
+from collections.abc import Callable, Sequence
import aqt
import aqt.customstudy
diff --git a/qt/aqt/theme.py b/qt/aqt/theme.py
index 6acd5463d..fbbbcda96 100644
--- a/qt/aqt/theme.py
+++ b/qt/aqt/theme.py
@@ -8,7 +8,7 @@ import os
import re
import subprocess
from dataclasses import dataclass
-from typing import Callable, List, Tuple
+from typing import Callable
import anki.lang
import aqt
@@ -386,7 +386,7 @@ def get_linux_dark_mode() -> bool:
return dbus_response[-1] == PREFER_DARK
- dark_mode_detection_strategies: List[Tuple[str, Callable[[str], bool]]] = [
+ dark_mode_detection_strategies: list[tuple[str, Callable[[str], bool]]] = [
(
"dbus-send --session --print-reply=literal --reply-timeout=1000 "
"--dest=org.freedesktop.portal.Desktop /org/freedesktop/portal/desktop "
diff --git a/qt/aqt/toolbar.py b/qt/aqt/toolbar.py
index 34b7fa2d3..7313f2a39 100644
--- a/qt/aqt/toolbar.py
+++ b/qt/aqt/toolbar.py
@@ -4,7 +4,8 @@ from __future__ import annotations
import enum
import re
-from typing import Any, Callable, Optional, cast
+from collections.abc import Callable
+from typing import Any, cast
import aqt
from anki.sync import SyncStatus
@@ -86,7 +87,7 @@ class TopWebView(ToolbarWebView):
self.show()
- def _onHeight(self, qvar: Optional[int]) -> None:
+ def _onHeight(self, qvar: int | None) -> None:
super()._onHeight(qvar)
if qvar:
self.web_height = int(qvar)
diff --git a/qt/aqt/utils.py b/qt/aqt/utils.py
index d89c9fbf6..500306c6e 100644
--- a/qt/aqt/utils.py
+++ b/qt/aqt/utils.py
@@ -9,9 +9,10 @@ import re
import shutil
import subprocess
import sys
+from collections.abc import Sequence
from functools import partial, wraps
from pathlib import Path
-from typing import TYPE_CHECKING, Any, Callable, Literal, Sequence, Union
+from typing import TYPE_CHECKING, Any, Callable, Literal, Union
from send2trash import send2trash
diff --git a/qt/aqt/webview.py b/qt/aqt/webview.py
index 71cb585bc..e711ec01a 100644
--- a/qt/aqt/webview.py
+++ b/qt/aqt/webview.py
@@ -7,8 +7,9 @@ import dataclasses
import json
import re
import sys
+from collections.abc import Sequence
from enum import Enum
-from typing import TYPE_CHECKING, Any, Callable, Optional, Sequence, cast
+from typing import TYPE_CHECKING, Any, Callable, cast
import anki
import anki.lang
@@ -524,10 +525,10 @@ html {{ {font} }}
def stdHtml(
self,
body: str,
- css: Optional[list[str]] = None,
- js: Optional[list[str]] = None,
+ css: list[str] | None = None,
+ js: list[str] | None = None,
head: str = "",
- context: Optional[Any] = None,
+ context: Any | None = None,
default_css: bool = True,
) -> None:
css = (["css/webview.css"] if default_css else []) + (
@@ -705,7 +706,7 @@ html {{ {font} }}
def adjustHeightToFit(self) -> None:
self.evalWithCallback("document.documentElement.offsetHeight", self._onHeight)
- def _onHeight(self, qvar: Optional[int]) -> None:
+ def _onHeight(self, qvar: int | None) -> None:
from aqt import mw
if qvar is None:
@@ -842,5 +843,5 @@ html {{ {font} }}
)
@deprecated(info="use theme_manager.qcolor() instead")
- def get_window_bg_color(self, night_mode: Optional[bool] = None) -> QColor:
+ def get_window_bg_color(self, night_mode: bool | None = None) -> QColor:
return theme_manager.qcolor(colors.CANVAS)