Merge pull request #732 from Arthur-Milchior/typing

Typing
This commit is contained in:
Damien Elmes 2020-08-13 20:19:30 +10:00 committed by GitHub
commit c3f8f7dd94
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 228 additions and 167 deletions

View File

@ -19,6 +19,21 @@ from anki.utils import ids2str, intTime
defaultDeck = 0 defaultDeck = 0
defaultDynamicDeck = 1 defaultDynamicDeck = 1
NonFilteredDeck = Dict[str, Any]
FilteredDeck = Dict[str, Any]
"""Any kind of deck """
Deck = Union[NonFilteredDeck, FilteredDeck]
"""Configuration of standard deck, as seen from the deck picker's gear."""
Config = Dict[str, Any]
"""Configurationf of some deck, filtered deck for filtered deck, config for standard deck"""
DeckConfig = Union[FilteredDeck, Config]
""" New/lrn/rev conf, from deck config"""
QueueConfig = Dict[str, Any]
class DecksDictProxy: class DecksDictProxy:
def __init__(self, col: anki.collection.Collection): def __init__(self, col: anki.collection.Collection):
@ -64,7 +79,7 @@ class DeckManager:
self.col = col.weakref() self.col = col.weakref()
self.decks = DecksDictProxy(col) self.decks = DecksDictProxy(col)
def save(self, g: Dict = None) -> None: def save(self, g: Union[Deck, Config] = None) -> None:
"Can be called with either a deck or a deck configuration." "Can be called with either a deck or a deck configuration."
if not g: if not g:
print("col.decks.save() should be passed the changed deck") print("col.decks.save() should be passed the changed deck")
@ -124,7 +139,7 @@ class DeckManager:
except NotFoundError: except NotFoundError:
return None return None
def get_legacy(self, did: int) -> Optional[Dict]: def get_legacy(self, did: int) -> Optional[Deck]:
try: try:
return from_json_bytes(self.col.backend.get_deck_legacy(did)) return from_json_bytes(self.col.backend.get_deck_legacy(did))
except NotFoundError: except NotFoundError:
@ -133,10 +148,10 @@ class DeckManager:
def have(self, id: int) -> bool: def have(self, id: int) -> bool:
return not self.get_legacy(int(id)) return not self.get_legacy(int(id))
def get_all_legacy(self) -> List[Dict]: def get_all_legacy(self) -> List[Deck]:
return list(from_json_bytes(self.col.backend.get_all_decks_legacy()).values()) return list(from_json_bytes(self.col.backend.get_all_decks_legacy()).values())
def new_deck_legacy(self, filtered: bool) -> Dict: def new_deck_legacy(self, filtered: bool) -> Deck:
return from_json_bytes(self.col.backend.new_deck_legacy(filtered)) return from_json_bytes(self.col.backend.new_deck_legacy(filtered))
def deck_tree(self) -> pb.DeckTreeNode: def deck_tree(self) -> pb.DeckTreeNode:
@ -154,7 +169,7 @@ class DeckManager:
return match return match
return None return None
def all(self) -> List: def all(self) -> List[Deck]:
"All decks. Expensive; prefer all_names_and_ids()" "All decks. Expensive; prefer all_names_and_ids()"
return self.get_all_legacy() return self.get_all_legacy()
@ -162,7 +177,7 @@ class DeckManager:
print("decks.allIds() is deprecated, use .all_names_and_ids()") print("decks.allIds() is deprecated, use .all_names_and_ids()")
return [str(x.id) for x in self.all_names_and_ids()] return [str(x.id) for x in self.all_names_and_ids()]
def allNames(self, dyn: bool = True, force_default: bool = True) -> List: def allNames(self, dyn: bool = True, force_default: bool = True) -> List[str]:
print("decks.allNames() is deprecated, use .all_names_and_ids()") print("decks.allNames() is deprecated, use .all_names_and_ids()")
return [ return [
x.name x.name
@ -185,7 +200,7 @@ class DeckManager:
def count(self) -> int: def count(self) -> int:
return len(self.all_names_and_ids()) return len(self.all_names_and_ids())
def get(self, did: Union[int, str], default: bool = True) -> Optional[Dict]: def get(self, did: Union[int, str], default: bool = True) -> Optional[Deck]:
if not did: if not did:
if default: if default:
return self.get_legacy(1) return self.get_legacy(1)
@ -200,14 +215,14 @@ class DeckManager:
else: else:
return None return None
def byName(self, name: str) -> Optional[Dict]: def byName(self, name: str) -> Optional[Deck]:
"""Get deck with NAME, ignoring case.""" """Get deck with NAME, ignoring case."""
id = self.id_for_name(name) id = self.id_for_name(name)
if id: if id:
return self.get_legacy(id) return self.get_legacy(id)
return None return None
def update(self, g: Dict[str, Any], preserve_usn=True) -> None: def update(self, g: Deck, preserve_usn=True) -> None:
"Add or update an existing deck. Used for syncing and merging." "Add or update an existing deck. Used for syncing and merging."
try: try:
g["id"] = self.col.backend.add_or_update_deck_legacy( g["id"] = self.col.backend.add_or_update_deck_legacy(
@ -216,7 +231,7 @@ class DeckManager:
except anki.rsbackend.DeckIsFilteredError: except anki.rsbackend.DeckIsFilteredError:
raise DeckRenameError("deck was filtered") raise DeckRenameError("deck was filtered")
def rename(self, g: Dict[str, Any], newName: str) -> None: def rename(self, g: Deck, newName: str) -> None:
"Rename deck prefix to NAME if not exists. Updates children." "Rename deck prefix to NAME if not exists. Updates children."
g["name"] = newName g["name"] = newName
self.update(g, preserve_usn=False) self.update(g, preserve_usn=False)
@ -225,7 +240,9 @@ class DeckManager:
# Drag/drop # Drag/drop
############################################################# #############################################################
def renameForDragAndDrop(self, draggedDeckDid: int, ontoDeckDid: Any) -> None: def renameForDragAndDrop(
self, draggedDeckDid: int, ontoDeckDid: Optional[Union[int, str]]
) -> None:
draggedDeck = self.get(draggedDeckDid) draggedDeck = self.get(draggedDeckDid)
draggedDeckName = draggedDeck["name"] draggedDeckName = draggedDeck["name"]
ontoDeckName = self.get(ontoDeckDid)["name"] ontoDeckName = self.get(ontoDeckDid)["name"]
@ -252,23 +269,23 @@ class DeckManager:
else: else:
return True return True
def _isParent(self, parentDeckName: str, childDeckName: str) -> Any: def _isParent(self, parentDeckName: str, childDeckName: str) -> bool:
return self.path(childDeckName) == self.path(parentDeckName) + [ return self.path(childDeckName) == self.path(parentDeckName) + [
self.basename(childDeckName) self.basename(childDeckName)
] ]
def _isAncestor(self, ancestorDeckName: str, descendantDeckName: str) -> Any: def _isAncestor(self, ancestorDeckName: str, descendantDeckName: str) -> bool:
ancestorPath = self.path(ancestorDeckName) ancestorPath = self.path(ancestorDeckName)
return ancestorPath == self.path(descendantDeckName)[0 : len(ancestorPath)] return ancestorPath == self.path(descendantDeckName)[0 : len(ancestorPath)]
# Deck configurations # Deck configurations
############################################################# #############################################################
def all_config(self) -> List: def all_config(self) -> List[Config]:
"A list of all deck config." "A list of all deck config."
return list(from_json_bytes(self.col.backend.all_deck_config_legacy())) return list(from_json_bytes(self.col.backend.all_deck_config_legacy()))
def confForDid(self, did: int) -> Any: def confForDid(self, did: int) -> DeckConfig:
deck = self.get(did, default=False) deck = self.get(did, default=False)
assert deck assert deck
if "conf" in deck: if "conf" in deck:
@ -282,20 +299,20 @@ class DeckManager:
# dynamic decks have embedded conf # dynamic decks have embedded conf
return deck return deck
def get_config(self, conf_id: int) -> Any: def get_config(self, conf_id: int) -> Optional[DeckConfig]:
try: try:
return from_json_bytes(self.col.backend.get_deck_config_legacy(conf_id)) return from_json_bytes(self.col.backend.get_deck_config_legacy(conf_id))
except NotFoundError: except NotFoundError:
return None return None
def update_config(self, conf: Dict[str, Any], preserve_usn=False) -> None: def update_config(self, conf: DeckConfig, preserve_usn=False) -> None:
conf["id"] = self.col.backend.add_or_update_deck_config_legacy( conf["id"] = self.col.backend.add_or_update_deck_config_legacy(
config=to_json_bytes(conf), preserve_usn_and_mtime=preserve_usn config=to_json_bytes(conf), preserve_usn_and_mtime=preserve_usn
) )
def add_config( def add_config(
self, name: str, clone_from: Optional[Dict[str, Any]] = None self, name: str, clone_from: Optional[DeckConfig] = None
) -> Dict[str, Any]: ) -> DeckConfig:
if clone_from is not None: if clone_from is not None:
conf = copy.deepcopy(clone_from) conf = copy.deepcopy(clone_from)
conf["id"] = 0 conf["id"] = 0
@ -306,7 +323,7 @@ class DeckManager:
return conf return conf
def add_config_returning_id( def add_config_returning_id(
self, name: str, clone_from: Optional[Dict[str, Any]] = None self, name: str, clone_from: Optional[DeckConfig] = None
) -> int: ) -> int:
return self.add_config(name, clone_from)["id"] return self.add_config(name, clone_from)["id"]
@ -322,11 +339,11 @@ class DeckManager:
self.save(g) self.save(g)
self.col.backend.remove_deck_config(id) self.col.backend.remove_deck_config(id)
def setConf(self, grp: Dict[str, Any], id: int) -> None: def setConf(self, grp: DeckConfig, id: int) -> None:
grp["conf"] = id grp["conf"] = id
self.save(grp) self.save(grp)
def didsForConf(self, conf) -> List: def didsForConf(self, conf) -> List[int]:
dids = [] dids = []
for deck in self.all(): for deck in self.all():
if "conf" in deck and deck["conf"] == conf["id"]: if "conf" in deck and deck["conf"] == conf["id"]:
@ -353,13 +370,13 @@ class DeckManager:
# Deck utils # Deck utils
############################################################# #############################################################
def name(self, did: int, default: bool = False) -> Any: def name(self, did: int, default: bool = False) -> str:
deck = self.get(did, default=default) deck = self.get(did, default=default)
if deck: if deck:
return deck["name"] return deck["name"]
return _("[no deck]") return _("[no deck]")
def nameOrNone(self, did: int) -> Any: def nameOrNone(self, did: int) -> Optional[str]:
deck = self.get(did, default=False) deck = self.get(did, default=False)
if deck: if deck:
return deck["name"] return deck["name"]
@ -373,7 +390,7 @@ class DeckManager:
intTime(), intTime(),
) )
def cids(self, did: int, children: bool = False) -> Any: def cids(self, did: int, children: bool = False) -> List[int]:
if not children: if not children:
return self.col.db.list("select id from cards where did=?", did) return self.col.db.list("select id from cards where did=?", did)
dids = [did] dids = [did]
@ -387,15 +404,15 @@ class DeckManager:
# Deck selection # Deck selection
############################################################# #############################################################
def active(self) -> Any: def active(self) -> List[int]:
"The currrently active dids." "The currrently active dids."
return self.col.get_config("activeDecks", [1]) return self.col.get_config("activeDecks", [1])
def selected(self) -> Any: def selected(self) -> int:
"The currently selected did." "The currently selected did."
return self.col.conf["curDeck"] return self.col.conf["curDeck"]
def current(self) -> Any: def current(self) -> Deck:
return self.get(self.selected()) return self.get(self.selected())
def select(self, did: int) -> None: def select(self, did: int) -> None:
@ -416,32 +433,33 @@ class DeckManager:
############################################################# #############################################################
@staticmethod @staticmethod
def path(name: str) -> Any: def path(name: str) -> List[str]:
return name.split("::") return name.split("::")
_path = path _path = path
@classmethod @classmethod
def basename(cls, name: str) -> Any: def basename(cls, name: str) -> str:
return cls.path(name)[-1] return cls.path(name)[-1]
_basename = basename _basename = basename
@classmethod @classmethod
def immediate_parent_path(cls, name: str) -> Any: def immediate_parent_path(cls, name: str) -> List[str]:
return cls._path(name)[:-1] return cls._path(name)[:-1]
@classmethod @classmethod
def immediate_parent(cls, name: str) -> Any: def immediate_parent(cls, name: str) -> Optional[str]:
pp = cls.immediate_parent_path(name) pp = cls.immediate_parent_path(name)
if pp: if pp:
return "::".join(pp) return "::".join(pp)
return None
@classmethod @classmethod
def key(cls, deck: Dict[str, Any]) -> List[str]: def key(cls, deck: Deck) -> List[str]:
return cls.path(deck["name"]) return cls.path(deck["name"])
def children(self, did: int) -> List[Tuple[Any, Any]]: def children(self, did: int) -> List[Tuple[str, int]]:
"All children of did, as (name, id)." "All children of did, as (name, id)."
name = self.get(did)["name"] name = self.get(did)["name"]
actv = [] actv = []
@ -460,19 +478,22 @@ class DeckManager:
out.extend(self.child_ids(parent_name)) out.extend(self.child_ids(parent_name))
return out return out
def childDids(self, did: int, childMap: Dict[int, Any]) -> List: childMapNode = Dict[int, Any]
def gather(node, arr): # Change to Dict[int, "DeckManager.childMapNode"] when MyPy allow recursive type
def childDids(self, did: int, childMap: DeckManager.childMapNode) -> List:
def gather(node: DeckManager.childMapNode, arr):
for did, child in node.items(): for did, child in node.items():
arr.append(did) arr.append(did)
gather(child, arr) gather(child, arr)
arr: List = [] arr: List[int] = []
gather(childMap[did], arr) gather(childMap[did], arr)
return arr return arr
def childMap(self) -> Dict[Any, Dict[Any, dict]]: def childMap(self) -> DeckManager.childMapNode:
nameMap = self.nameMap() nameMap = self.nameMap()
childMap = {} childMap: DeckManager.childMapNode = {}
# go through all decks, sorted by name # go through all decks, sorted by name
for deck in sorted(self.all(), key=self.key): for deck in sorted(self.all(), key=self.key):
@ -487,31 +508,34 @@ class DeckManager:
return childMap return childMap
def parents(self, did: int, nameMap: Optional[Any] = None) -> List: def parents(
self, did: int, nameMap: Optional[Dict[str, Deck]] = None
) -> List[Deck]:
"All parents of did." "All parents of did."
# get parent and grandparent names # get parent and grandparent names
parents: List[str] = [] parents_names: List[str] = []
for part in self.immediate_parent_path(self.get(did)["name"]): for part in self.immediate_parent_path(self.get(did)["name"]):
if not parents: if not parents_names:
parents.append(part) parents_names.append(part)
else: else:
parents.append(parents[-1] + "::" + part) parents_names.append(parents_names[-1] + "::" + part)
parents: List[Deck] = []
# convert to objects # convert to objects
for c, p in enumerate(parents): for parent_name in parents_names:
if nameMap: if nameMap:
deck = nameMap[p] deck = nameMap[parent_name]
else: else:
deck = self.get(self.id(p)) deck = self.get(self.id(parent_name))
parents[c] = deck parents.append(deck)
return parents return parents
def parentsByName(self, name: str) -> List: def parentsByName(self, name: str) -> List[Deck]:
"All existing parents of name" "All existing parents of name"
if "::" not in name: if "::" not in name:
return [] return []
names = self.immediate_parent_path(name) names = self.immediate_parent_path(name)
head = [] head = []
parents = [] parents: List[Deck] = []
while names: while names:
head.append(names.pop(0)) head.append(names.pop(0))
@ -521,7 +545,7 @@ class DeckManager:
return parents return parents
def nameMap(self) -> dict: def nameMap(self) -> Dict[str, Deck]:
return dict((d["name"], d) for d in self.all()) return dict((d["name"], d) for d in self.all())
# Dynamic decks # Dynamic decks
@ -533,5 +557,6 @@ class DeckManager:
self.select(did) self.select(did)
return did return did
def isDyn(self, did: Union[int, str]) -> Any: # 1 for dyn, 0 for standard
def isDyn(self, did: Union[int, str]) -> int:
return self.get(did)["dyn"] return self.get(did)["dyn"]

View File

@ -175,20 +175,20 @@ card_will_flush = _CardWillFlushHook()
class _DeckAddedHook: class _DeckAddedHook:
"""Obsolete, do not use.""" """Obsolete, do not use."""
_hooks: List[Callable[[Dict[str, Any]], None]] = [] _hooks: List[Callable[["anki.decks.Deck"], None]] = []
def append(self, cb: Callable[[Dict[str, Any]], None]) -> None: def append(self, cb: Callable[["anki.decks.Deck"], None]) -> None:
"""(deck: Dict[str, Any])""" """(deck: anki.decks.Deck)"""
self._hooks.append(cb) self._hooks.append(cb)
def remove(self, cb: Callable[[Dict[str, Any]], None]) -> None: def remove(self, cb: Callable[["anki.decks.Deck"], None]) -> None:
if cb in self._hooks: if cb in self._hooks:
self._hooks.remove(cb) self._hooks.remove(cb)
def count(self) -> int: def count(self) -> int:
return len(self._hooks) return len(self._hooks)
def __call__(self, deck: Dict[str, Any]) -> None: def __call__(self, deck: anki.decks.Deck) -> None:
for hook in self._hooks: for hook in self._hooks:
try: try:
hook(deck) hook(deck)
@ -305,20 +305,20 @@ media_files_did_export = _MediaFilesDidExportHook()
class _NoteTypeAddedHook: class _NoteTypeAddedHook:
"""Obsolete, do not use.""" """Obsolete, do not use."""
_hooks: List[Callable[[Dict[str, Any]], None]] = [] _hooks: List[Callable[["anki.models.NoteType"], None]] = []
def append(self, cb: Callable[[Dict[str, Any]], None]) -> None: def append(self, cb: Callable[["anki.models.NoteType"], None]) -> None:
"""(notetype: Dict[str, Any])""" """(notetype: anki.models.NoteType)"""
self._hooks.append(cb) self._hooks.append(cb)
def remove(self, cb: Callable[[Dict[str, Any]], None]) -> None: def remove(self, cb: Callable[["anki.models.NoteType"], None]) -> None:
if cb in self._hooks: if cb in self._hooks:
self._hooks.remove(cb) self._hooks.remove(cb)
def count(self) -> int: def count(self) -> int:
return len(self._hooks) return len(self._hooks)
def __call__(self, notetype: Dict[str, Any]) -> None: def __call__(self, notetype: anki.models.NoteType) -> None:
for hook in self._hooks: for hook in self._hooks:
try: try:
hook(notetype) hook(notetype)
@ -397,20 +397,20 @@ class _SchedulerNewLimitForSingleDeckFilter:
"""Allows changing the number of new card for this deck (without """Allows changing the number of new card for this deck (without
considering descendants).""" considering descendants)."""
_hooks: List[Callable[[int, Dict[str, Any]], int]] = [] _hooks: List[Callable[[int, "anki.decks.Deck"], int]] = []
def append(self, cb: Callable[[int, Dict[str, Any]], int]) -> None: def append(self, cb: Callable[[int, "anki.decks.Deck"], int]) -> None:
"""(count: int, deck: Dict[str, Any])""" """(count: int, deck: anki.decks.Deck)"""
self._hooks.append(cb) self._hooks.append(cb)
def remove(self, cb: Callable[[int, Dict[str, Any]], int]) -> None: def remove(self, cb: Callable[[int, "anki.decks.Deck"], int]) -> None:
if cb in self._hooks: if cb in self._hooks:
self._hooks.remove(cb) self._hooks.remove(cb)
def count(self) -> int: def count(self) -> int:
return len(self._hooks) return len(self._hooks)
def __call__(self, count: int, deck: Dict[str, Any]) -> int: def __call__(self, count: int, deck: anki.decks.Deck) -> int:
for filter in self._hooks: for filter in self._hooks:
try: try:
count = filter(count, deck) count = filter(count, deck)
@ -428,20 +428,20 @@ class _SchedulerReviewLimitForSingleDeckFilter:
"""Allows changing the number of rev card for this deck (without """Allows changing the number of rev card for this deck (without
considering descendants).""" considering descendants)."""
_hooks: List[Callable[[int, Dict[str, Any]], int]] = [] _hooks: List[Callable[[int, "anki.decks.Deck"], int]] = []
def append(self, cb: Callable[[int, Dict[str, Any]], int]) -> None: def append(self, cb: Callable[[int, "anki.decks.Deck"], int]) -> None:
"""(count: int, deck: Dict[str, Any])""" """(count: int, deck: anki.decks.Deck)"""
self._hooks.append(cb) self._hooks.append(cb)
def remove(self, cb: Callable[[int, Dict[str, Any]], int]) -> None: def remove(self, cb: Callable[[int, "anki.decks.Deck"], int]) -> None:
if cb in self._hooks: if cb in self._hooks:
self._hooks.remove(cb) self._hooks.remove(cb)
def count(self) -> int: def count(self) -> int:
return len(self._hooks) return len(self._hooks)
def __call__(self, count: int, deck: Dict[str, Any]) -> int: def __call__(self, count: int, deck: anki.decks.Deck) -> int:
for filter in self._hooks: for filter in self._hooks:
try: try:
count = filter(count, deck) count = filter(count, deck)

View File

@ -138,7 +138,7 @@ class ModelManager:
# Current note type # Current note type
############################################################# #############################################################
def current(self, forDeck: bool = True) -> Any: def current(self, forDeck: bool = True) -> NoteType:
"Get current model." "Get current model."
m = self.get(self.col.decks.current().get("mid")) m = self.get(self.col.decks.current().get("mid"))
if not forDeck or not m: if not forDeck or not m:
@ -243,21 +243,21 @@ class ModelManager:
# Tools # Tools
################################################## ##################################################
def nids(self, ntid: int) -> Any: def nids(self, ntid: int) -> List[int]:
"Note ids for M." "Note ids for M."
if isinstance(ntid, dict): if isinstance(ntid, dict):
# legacy callers passed in note type # legacy callers passed in note type
ntid = ntid["id"] ntid = ntid["id"]
return self.col.db.list("select id from notes where mid = ?", ntid) return self.col.db.list("select id from notes where mid = ?", ntid)
def useCount(self, m: NoteType) -> Any: def useCount(self, m: NoteType) -> int:
"Number of note using M." "Number of note using M."
return self.col.db.scalar("select count() from notes where mid = ?", m["id"]) return self.col.db.scalar("select count() from notes where mid = ?", m["id"])
# Copying # Copying
################################################## ##################################################
def copy(self, m: NoteType) -> Any: def copy(self, m: NoteType) -> NoteType:
"Copy, save and return." "Copy, save and return."
m2 = copy.deepcopy(m) m2 = copy.deepcopy(m)
m2["name"] = _("%s copy") % m2["name"] m2["name"] = _("%s copy") % m2["name"]
@ -275,7 +275,7 @@ class ModelManager:
def fieldNames(self, m: NoteType) -> List[str]: def fieldNames(self, m: NoteType) -> List[str]:
return [f["name"] for f in m["flds"]] return [f["name"] for f in m["flds"]]
def sortIdx(self, m: NoteType) -> Any: def sortIdx(self, m: NoteType) -> int:
return m["sortf"] return m["sortf"]
# Adding & changing fields # Adding & changing fields
@ -406,7 +406,12 @@ and notes.mid = ? and cards.ord = ?""",
# - newModel should be self if model is not changing # - newModel should be self if model is not changing
def change( def change(
self, m: NoteType, nids: List[int], newModel: NoteType, fmap: Any, cmap: Any self,
m: NoteType,
nids: List[int],
newModel: NoteType,
fmap: Optional[Dict[int, Union[None, int]]],
cmap: Optional[Dict[int, Union[None, int]]],
) -> None: ) -> None:
self.col.modSchema(check=True) self.col.modSchema(check=True)
assert newModel["id"] == m["id"] or (fmap and cmap) assert newModel["id"] == m["id"] or (fmap and cmap)
@ -483,7 +488,9 @@ and notes.mid = ? and cards.ord = ?""",
# Cloze # Cloze
########################################################################## ##########################################################################
def _availClozeOrds(self, m: NoteType, flds: str, allowEmpty: bool = True) -> List: def _availClozeOrds(
self, m: NoteType, flds: str, allowEmpty: bool = True
) -> List[int]:
print("_availClozeOrds() is deprecated; use note.cloze_numbers_in_fields()") print("_availClozeOrds() is deprecated; use note.cloze_numbers_in_fields()")
note = anki.rsbackend.BackendNote(fields=[flds]) note = anki.rsbackend.BackendNote(fields=[flds])
return list(self.col.backend.cloze_numbers_in_note(note)) return list(self.col.backend.cloze_numbers_in_note(note))

View File

@ -6,12 +6,13 @@ from __future__ import annotations
import random import random
import time import time
from heapq import * from heapq import *
from typing import Any, Dict, List, Optional, Sequence, Tuple, Union from typing import Any, List, Optional, Sequence, Tuple, Union
import anki import anki
from anki import hooks from anki import hooks
from anki.cards import Card from anki.cards import Card
from anki.consts import * from anki.consts import *
from anki.decks import Deck, QueueConfig
from anki.schedv2 import Scheduler as V2 from anki.schedv2 import Scheduler as V2
from anki.utils import ids2str, intTime from anki.utils import ids2str, intTime
@ -299,13 +300,13 @@ limit %d"""
card.queue = QUEUE_TYPE_DAY_LEARN_RELEARN card.queue = QUEUE_TYPE_DAY_LEARN_RELEARN
self._logLrn(card, ease, conf, leaving, type, lastLeft) self._logLrn(card, ease, conf, leaving, type, lastLeft)
def _lrnConf(self, card: Card) -> Dict[str, Any]: def _lrnConf(self, card: Card) -> QueueConfig:
if card.type == CARD_TYPE_REV: if card.type == CARD_TYPE_REV:
return self._lapseConf(card) return self._lapseConf(card)
else: else:
return self._newConf(card) return self._newConf(card)
def _rescheduleAsRev(self, card: Card, conf: Dict[str, Any], early: bool) -> None: def _rescheduleAsRev(self, card: Card, conf: QueueConfig, early: bool) -> None:
lapse = card.type == CARD_TYPE_REV lapse = card.type == CARD_TYPE_REV
if lapse: if lapse:
if self._resched(card): if self._resched(card):
@ -338,7 +339,7 @@ limit %d"""
return tot + tod * 1000 return tot + tod * 1000
def _graduatingIvl( def _graduatingIvl(
self, card: Card, conf: Dict[str, Any], early: bool, adj: bool = True self, card: Card, conf: QueueConfig, early: bool, adj: bool = True
) -> int: ) -> int:
if card.type == CARD_TYPE_REV: if card.type == CARD_TYPE_REV:
# lapsed card being relearnt # lapsed card being relearnt
@ -357,7 +358,7 @@ limit %d"""
else: else:
return ideal return ideal
def _rescheduleNew(self, card: Card, conf: Dict[str, Any], early: bool) -> None: def _rescheduleNew(self, card: Card, conf: QueueConfig, early: bool) -> None:
"Reschedule a new card that's graduated for the first time." "Reschedule a new card that's graduated for the first time."
card.ivl = self._graduatingIvl(card, conf, early) card.ivl = self._graduatingIvl(card, conf, early)
card.due = self.today + card.ivl card.due = self.today + card.ivl
@ -367,7 +368,7 @@ limit %d"""
self, self,
card: Card, card: Card,
ease: int, ease: int,
conf: Dict[str, Any], conf: QueueConfig,
leaving: bool, leaving: bool,
type: int, type: int,
lastLeft: int, lastLeft: int,
@ -452,7 +453,7 @@ and due <= ? limit ?)""",
def _deckRevLimit(self, did: int) -> int: def _deckRevLimit(self, did: int) -> int:
return self._deckNewLimit(did, self._deckRevLimitSingle) return self._deckNewLimit(did, self._deckRevLimitSingle)
def _deckRevLimitSingle(self, d: Dict[str, Any]) -> int: # type: ignore[override] def _deckRevLimitSingle(self, d: Deck) -> int: # type: ignore[override]
if d["dyn"]: if d["dyn"]:
return self.reportLimit return self.reportLimit
c = self.col.decks.confForDid(d["id"]) c = self.col.decks.confForDid(d["id"])
@ -567,7 +568,7 @@ did = ? and queue = {QUEUE_TYPE_REV} and due <= ? limit ?""",
card.queue = QUEUE_TYPE_DAY_LEARN_RELEARN card.queue = QUEUE_TYPE_DAY_LEARN_RELEARN
return delay return delay
def _nextLapseIvl(self, card: Card, conf: Dict[str, Any]) -> int: def _nextLapseIvl(self, card: Card, conf: QueueConfig) -> int:
return max(conf["minInt"], int(card.ivl * conf["mult"])) return max(conf["minInt"], int(card.ivl * conf["mult"]))
def _rescheduleRev(self, card: Card, ease: int) -> None: # type: ignore[override] def _rescheduleRev(self, card: Card, ease: int) -> None: # type: ignore[override]
@ -607,7 +608,7 @@ did = ? and queue = {QUEUE_TYPE_REV} and due <= ? limit ?""",
# interval capped? # interval capped?
return min(interval, conf["maxIvl"]) return min(interval, conf["maxIvl"])
def _constrainedIvl(self, ivl: float, conf: Dict[str, Any], prev: int) -> int: # type: ignore[override] def _constrainedIvl(self, ivl: float, conf: QueueConfig, prev: int) -> int: # type: ignore[override]
"Integer interval after interval factor and prev+1 constraints applied." "Integer interval after interval factor and prev+1 constraints applied."
new = ivl * conf.get("ivlFct", 1) new = ivl * conf.get("ivlFct", 1)
return int(max(new, prev + 1)) return int(max(new, prev + 1))
@ -641,7 +642,7 @@ did = ? and queue = {QUEUE_TYPE_REV} and due <= ? limit ?""",
self.col.decks.select(did) self.col.decks.select(did)
return ids return ids
def _fillDyn(self, deck: Dict[str, Any]) -> Sequence[int]: # type: ignore[override] def _fillDyn(self, deck: Deck) -> Sequence[int]: # type: ignore[override]
search, limit, order = deck["terms"][0] search, limit, order = deck["terms"][0]
orderlimit = self._dynOrder(order, limit) orderlimit = self._dynOrder(order, limit)
if search.strip(): if search.strip():
@ -707,7 +708,7 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?"""
# Leeches # Leeches
########################################################################## ##########################################################################
def _checkLeech(self, card: Card, conf: Dict[str, Any]) -> bool: def _checkLeech(self, card: Card, conf: QueueConfig) -> bool:
"Leech handler. True if card was a leech." "Leech handler. True if card was a leech."
lf = conf["leechFails"] lf = conf["leechFails"]
if not lf: if not lf:
@ -737,7 +738,7 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?"""
# Tools # Tools
########################################################################## ##########################################################################
def _newConf(self, card: Card) -> Dict[str, Any]: def _newConf(self, card: Card) -> QueueConfig:
conf = self._cardConf(card) conf = self._cardConf(card)
# normal deck # normal deck
if not card.odid: if not card.odid:
@ -756,7 +757,7 @@ did = ?, queue = %s, due = ?, usn = ? where id = ?"""
perDay=self.reportLimit, perDay=self.reportLimit,
) )
def _lapseConf(self, card: Card) -> Dict[str, Any]: def _lapseConf(self, card: Card) -> QueueConfig:
conf = self._cardConf(card) conf = self._cardConf(card)
# normal deck # normal deck
if not card.odid: if not card.odid:

View File

@ -13,6 +13,7 @@ import anki # pylint: disable=unused-import
from anki import hooks from anki import hooks
from anki.cards import Card from anki.cards import Card
from anki.consts import * from anki.consts import *
from anki.decks import Deck, DeckConfig, DeckManager, FilteredDeck, QueueConfig
from anki.lang import _ from anki.lang import _
from anki.rsbackend import ( from anki.rsbackend import (
CountsForDeckToday, CountsForDeckToday,
@ -355,7 +356,7 @@ order by due"""
return None return None
def _deckNewLimit( def _deckNewLimit(
self, did: int, fn: Optional[Callable[[Dict[str, Any]], int]] = None self, did: int, fn: Optional[Callable[[Deck], int]] = None
) -> int: ) -> int:
if not fn: if not fn:
fn = self._deckNewLimitSingle fn = self._deckNewLimitSingle
@ -383,7 +384,7 @@ select count() from
lim, lim,
) )
def _deckNewLimitSingle(self, g: Dict[str, Any]) -> int: def _deckNewLimitSingle(self, g: DeckConfig) -> int:
"Limit for deck without parent limits." "Limit for deck without parent limits."
if g["dyn"]: if g["dyn"]:
return self.dynReportLimit return self.dynReportLimit
@ -552,11 +553,11 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""",
self._logLrn(card, ease, conf, leaving, type, lastLeft) self._logLrn(card, ease, conf, leaving, type, lastLeft)
def _updateRevIvlOnFail(self, card: Card, conf: Dict[str, Any]) -> None: def _updateRevIvlOnFail(self, card: Card, conf: QueueConfig) -> None:
card.lastIvl = card.ivl card.lastIvl = card.ivl
card.ivl = self._lapseIvl(card, conf) card.ivl = self._lapseIvl(card, conf)
def _moveToFirstStep(self, card: Card, conf: Dict[str, Any]) -> Any: def _moveToFirstStep(self, card: Card, conf: QueueConfig) -> Any:
card.left = self._startingLeft(card) card.left = self._startingLeft(card)
# relearning card? # relearning card?
@ -565,19 +566,19 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""",
return self._rescheduleLrnCard(card, conf) return self._rescheduleLrnCard(card, conf)
def _moveToNextStep(self, card: Card, conf: Dict[str, Any]) -> None: def _moveToNextStep(self, card: Card, conf: QueueConfig) -> None:
# decrement real left count and recalculate left today # decrement real left count and recalculate left today
left = (card.left % 1000) - 1 left = (card.left % 1000) - 1
card.left = self._leftToday(conf["delays"], left) * 1000 + left card.left = self._leftToday(conf["delays"], left) * 1000 + left
self._rescheduleLrnCard(card, conf) self._rescheduleLrnCard(card, conf)
def _repeatStep(self, card: Card, conf: Dict[str, Any]) -> None: def _repeatStep(self, card: Card, conf: QueueConfig) -> None:
delay = self._delayForRepeatingGrade(conf, card.left) delay = self._delayForRepeatingGrade(conf, card.left)
self._rescheduleLrnCard(card, conf, delay=delay) self._rescheduleLrnCard(card, conf, delay=delay)
def _rescheduleLrnCard( def _rescheduleLrnCard(
self, card: Card, conf: Dict[str, Any], delay: Optional[int] = None self, card: Card, conf: QueueConfig, delay: Optional[int] = None
) -> Any: ) -> Any:
# normal delay for the current step? # normal delay for the current step?
if delay is None: if delay is None:
@ -608,7 +609,7 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""",
card.queue = QUEUE_TYPE_DAY_LEARN_RELEARN card.queue = QUEUE_TYPE_DAY_LEARN_RELEARN
return delay return delay
def _delayForGrade(self, conf: Dict[str, Any], left: int) -> int: def _delayForGrade(self, conf: QueueConfig, left: int) -> int:
left = left % 1000 left = left % 1000
try: try:
delay = conf["delays"][-left] delay = conf["delays"][-left]
@ -620,7 +621,7 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""",
delay = 1 delay = 1
return delay * 60 return delay * 60
def _delayForRepeatingGrade(self, conf: Dict[str, Any], left: int) -> Any: def _delayForRepeatingGrade(self, conf: QueueConfig, left: int) -> Any:
# halfway between last and next # halfway between last and next
delay1 = self._delayForGrade(conf, left) delay1 = self._delayForGrade(conf, left)
if len(conf["delays"]) > 1: if len(conf["delays"]) > 1:
@ -636,7 +637,7 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""",
else: else:
return self._newConf(card) return self._newConf(card)
def _rescheduleAsRev(self, card: Card, conf: Dict[str, Any], early: bool) -> None: def _rescheduleAsRev(self, card: Card, conf: QueueConfig, early: bool) -> None:
lapse = card.type in (CARD_TYPE_REV, CARD_TYPE_RELEARNING) lapse = card.type in (CARD_TYPE_REV, CARD_TYPE_RELEARNING)
if lapse: if lapse:
@ -680,7 +681,7 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""",
return ok + 1 return ok + 1
def _graduatingIvl( def _graduatingIvl(
self, card: Card, conf: Dict[str, Any], early: bool, fuzz: bool = True self, card: Card, conf: QueueConfig, early: bool, fuzz: bool = True
) -> Any: ) -> Any:
if card.type in (CARD_TYPE_REV, CARD_TYPE_RELEARNING): if card.type in (CARD_TYPE_REV, CARD_TYPE_RELEARNING):
bonus = early and 1 or 0 bonus = early and 1 or 0
@ -695,7 +696,7 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""",
ideal = self._fuzzedIvl(ideal) ideal = self._fuzzedIvl(ideal)
return ideal return ideal
def _rescheduleNew(self, card: Card, conf: Dict[str, Any], early: bool) -> None: def _rescheduleNew(self, card: Card, conf: QueueConfig, early: bool) -> None:
"Reschedule a new card that's graduated for the first time." "Reschedule a new card that's graduated for the first time."
card.ivl = self._graduatingIvl(card, conf, early) card.ivl = self._graduatingIvl(card, conf, early)
card.due = self.today + card.ivl card.due = self.today + card.ivl
@ -706,7 +707,7 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""",
self, self,
card: Card, card: Card,
ease: int, ease: int,
conf: Dict[str, Any], conf: QueueConfig,
leaving: bool, leaving: bool,
type: int, type: int,
lastLeft: int, lastLeft: int,
@ -791,7 +792,9 @@ and due <= ? limit ?)""",
lim = min(lim, self._deckRevLimitSingle(parent, parentLimit=lim)) lim = min(lim, self._deckRevLimitSingle(parent, parentLimit=lim))
return hooks.scheduler_review_limit_for_single_deck(lim, d) return hooks.scheduler_review_limit_for_single_deck(lim, d)
def _revForDeck(self, did: int, lim: int, childMap: Dict[int, Any]) -> Any: def _revForDeck(
self, did: int, lim: int, childMap: DeckManager.childMapNode
) -> Any:
dids = [did] + self.col.decks.childDids(did, childMap) dids = [did] + self.col.decks.childDids(did, childMap)
lim = min(lim, self.reportLimit) lim = min(lim, self.reportLimit)
return self.col.db.scalar( return self.col.db.scalar(
@ -888,7 +891,7 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l
return delay return delay
def _lapseIvl(self, card: Card, conf: Dict[str, Any]) -> Any: def _lapseIvl(self, card: Card, conf: QueueConfig) -> Any:
ivl = max(1, conf["minInt"], int(card.ivl * conf["mult"])) ivl = max(1, conf["minInt"], int(card.ivl * conf["mult"]))
return ivl return ivl
@ -975,7 +978,7 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l
return [ivl - fuzz, ivl + fuzz] return [ivl - fuzz, ivl + fuzz]
def _constrainedIvl( def _constrainedIvl(
self, ivl: float, conf: Dict[str, Any], prev: int, fuzz: bool self, ivl: float, conf: QueueConfig, prev: int, fuzz: bool
) -> int: ) -> int:
ivl = int(ivl * conf.get("ivlFct", 1)) ivl = int(ivl * conf.get("ivlFct", 1))
if fuzz: if fuzz:
@ -1058,7 +1061,7 @@ end)
self.col.decks.select(did) self.col.decks.select(did)
return cnt return cnt
def _fillDyn(self, deck: Dict[str, Any]) -> int: def _fillDyn(self, deck: FilteredDeck) -> int:
start = -100000 start = -100000
total = 0 total = 0
for search, limit, order in deck["terms"]: for search, limit, order in deck["terms"]:
@ -1167,7 +1170,7 @@ where id = ?
# Leeches # Leeches
########################################################################## ##########################################################################
def _checkLeech(self, card: Card, conf: Dict[str, Any]) -> bool: def _checkLeech(self, card: Card, conf: QueueConfig) -> bool:
"Leech handler. True if card was a leech." "Leech handler. True if card was a leech."
lf = conf["leechFails"] lf = conf["leechFails"]
if not lf: if not lf:
@ -1190,7 +1193,7 @@ where id = ?
# Tools # Tools
########################################################################## ##########################################################################
def _cardConf(self, card: Card) -> Dict[str, Any]: def _cardConf(self, card: Card) -> DeckConfig:
return self.col.decks.confForDid(card.did) return self.col.decks.confForDid(card.did)
def _newConf(self, card: Card) -> Any: def _newConf(self, card: Card) -> Any:
@ -1229,7 +1232,7 @@ where id = ?
resched=conf["resched"], resched=conf["resched"],
) )
def _revConf(self, card: Card) -> Dict[str, Any]: def _revConf(self, card: Card) -> QueueConfig:
conf = self._cardConf(card) conf = self._cardConf(card)
# normal deck # normal deck
if not card.odid: if not card.odid:

View File

@ -71,23 +71,25 @@ hooks = [
), ),
Hook( Hook(
name="scheduler_new_limit_for_single_deck", name="scheduler_new_limit_for_single_deck",
args=["count: int", "deck: Dict[str, Any]"], args=["count: int", "deck: anki.decks.Deck"],
return_type="int", return_type="int",
doc="""Allows changing the number of new card for this deck (without doc="""Allows changing the number of new card for this deck (without
considering descendants).""", considering descendants).""",
), ),
Hook( Hook(
name="scheduler_review_limit_for_single_deck", name="scheduler_review_limit_for_single_deck",
args=["count: int", "deck: Dict[str, Any]"], args=["count: int", "deck: anki.decks.Deck"],
return_type="int", return_type="int",
doc="""Allows changing the number of rev card for this deck (without doc="""Allows changing the number of rev card for this deck (without
considering descendants).""", considering descendants).""",
), ),
# obsolete # obsolete
Hook(name="deck_added", args=["deck: Dict[str, Any]"], doc="Obsolete, do not use."), Hook(
name="deck_added", args=["deck: anki.decks.Deck"], doc="Obsolete, do not use."
),
Hook( Hook(
name="note_type_added", name="note_type_added",
args=["notetype: Dict[str, Any]"], args=["notetype: anki.models.NoteType"],
doc="Obsolete, do not use.", doc="Obsolete, do not use.",
), ),
Hook( Hook(

View File

@ -254,7 +254,7 @@ class Editor:
id: Optional[str] = None, id: Optional[str] = None,
toggleable: bool = False, toggleable: bool = False,
disables: bool = True, disables: bool = True,
): ) -> str:
if icon: if icon:
if icon.startswith("qrc:/"): if icon.startswith("qrc:/"):
iconstr = icon iconstr = icon

View File

@ -119,8 +119,8 @@ class FieldDialog(QDialog):
def onDelete(self): def onDelete(self):
if len(self.model["flds"]) < 2: if len(self.model["flds"]) < 2:
return showWarning(_("Notes require at least one field.")) return showWarning(_("Notes require at least one field."))
c = self.mm.useCount(self.model) count = self.mm.useCount(self.model)
c = ngettext("%d note", "%d notes", c) % c c = ngettext("%d note", "%d notes", count) % count
if not askUser(_("Delete field from %s?") % c): if not askUser(_("Delete field from %s?") % c):
return return
if not self.change_tracker.mark_schema(): if not self.change_tracker.mark_schema():

View File

@ -7,12 +7,14 @@ See pylib/anki/hooks.py
from __future__ import annotations from __future__ import annotations
from typing import Any, Callable, Dict, List, Optional, Tuple from typing import Any, Callable, List, Optional, Tuple
import anki import anki
import aqt import aqt
from anki.cards import Card from anki.cards import Card
from anki.decks import Deck, DeckConfig
from anki.hooks import runFilter, runHook from anki.hooks import runFilter, runHook
from anki.models import NoteType
from aqt.qt import QDialog, QEvent, QMenu from aqt.qt import QDialog, QEvent, QMenu
from aqt.tagedit import TagEdit from aqt.tagedit import TagEdit
@ -827,20 +829,20 @@ collection_did_load = _CollectionDidLoadHook()
class _CurrentNoteTypeDidChangeHook: class _CurrentNoteTypeDidChangeHook:
_hooks: List[Callable[[Dict[str, Any]], None]] = [] _hooks: List[Callable[[NoteType], None]] = []
def append(self, cb: Callable[[Dict[str, Any]], None]) -> None: def append(self, cb: Callable[[NoteType], None]) -> None:
"""(notetype: Dict[str, Any])""" """(notetype: NoteType)"""
self._hooks.append(cb) self._hooks.append(cb)
def remove(self, cb: Callable[[Dict[str, Any]], None]) -> None: def remove(self, cb: Callable[[NoteType], None]) -> None:
if cb in self._hooks: if cb in self._hooks:
self._hooks.remove(cb) self._hooks.remove(cb)
def count(self) -> int: def count(self) -> int:
return len(self._hooks) return len(self._hooks)
def __call__(self, notetype: Dict[str, Any]) -> None: def __call__(self, notetype: NoteType) -> None:
for hook in self._hooks: for hook in self._hooks:
try: try:
hook(notetype) hook(notetype)
@ -1046,16 +1048,18 @@ class _DeckConfDidAddConfigHook:
Config groups are created as clones of the current one. Config groups are created as clones of the current one.
""" """
_hooks: List[Callable[["aqt.deckconf.DeckConf", Any, Any, str, int], None]] = [] _hooks: List[
Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig, str, int], None]
] = []
def append( def append(
self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any, str, int], None] self, cb: Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig, str, int], None]
) -> None: ) -> None:
"""(deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any, new_name: str, new_conf_id: int)""" """(deck_conf: aqt.deckconf.DeckConf, deck: Deck, config: DeckConfig, new_name: str, new_conf_id: int)"""
self._hooks.append(cb) self._hooks.append(cb)
def remove( def remove(
self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any, str, int], None] self, cb: Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig, str, int], None]
) -> None: ) -> None:
if cb in self._hooks: if cb in self._hooks:
self._hooks.remove(cb) self._hooks.remove(cb)
@ -1066,8 +1070,8 @@ class _DeckConfDidAddConfigHook:
def __call__( def __call__(
self, self,
deck_conf: aqt.deckconf.DeckConf, deck_conf: aqt.deckconf.DeckConf,
deck: Any, deck: Deck,
config: Any, config: DeckConfig,
new_name: str, new_name: str,
new_conf_id: int, new_conf_id: int,
) -> None: ) -> None:
@ -1086,13 +1090,17 @@ deck_conf_did_add_config = _DeckConfDidAddConfigHook()
class _DeckConfDidLoadConfigHook: class _DeckConfDidLoadConfigHook:
"""Called once widget state has been set from deck config""" """Called once widget state has been set from deck config"""
_hooks: List[Callable[["aqt.deckconf.DeckConf", Any, Any], None]] = [] _hooks: List[Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig], None]] = []
def append(self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any], None]) -> None: def append(
"""(deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any)""" self, cb: Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig], None]
) -> None:
"""(deck_conf: aqt.deckconf.DeckConf, deck: Deck, config: DeckConfig)"""
self._hooks.append(cb) self._hooks.append(cb)
def remove(self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any], None]) -> None: def remove(
self, cb: Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig], None]
) -> None:
if cb in self._hooks: if cb in self._hooks:
self._hooks.remove(cb) self._hooks.remove(cb)
@ -1100,7 +1108,7 @@ class _DeckConfDidLoadConfigHook:
return len(self._hooks) return len(self._hooks)
def __call__( def __call__(
self, deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any self, deck_conf: aqt.deckconf.DeckConf, deck: Deck, config: DeckConfig
) -> None: ) -> None:
for hook in self._hooks: for hook in self._hooks:
try: try:
@ -1146,13 +1154,17 @@ deck_conf_did_setup_ui_form = _DeckConfDidSetupUiFormHook()
class _DeckConfWillRemoveConfigHook: class _DeckConfWillRemoveConfigHook:
"""Called before current config group is removed""" """Called before current config group is removed"""
_hooks: List[Callable[["aqt.deckconf.DeckConf", Any, Any], None]] = [] _hooks: List[Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig], None]] = []
def append(self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any], None]) -> None: def append(
"""(deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any)""" self, cb: Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig], None]
) -> None:
"""(deck_conf: aqt.deckconf.DeckConf, deck: Deck, config: DeckConfig)"""
self._hooks.append(cb) self._hooks.append(cb)
def remove(self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any], None]) -> None: def remove(
self, cb: Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig], None]
) -> None:
if cb in self._hooks: if cb in self._hooks:
self._hooks.remove(cb) self._hooks.remove(cb)
@ -1160,7 +1172,7 @@ class _DeckConfWillRemoveConfigHook:
return len(self._hooks) return len(self._hooks)
def __call__( def __call__(
self, deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any self, deck_conf: aqt.deckconf.DeckConf, deck: Deck, config: DeckConfig
) -> None: ) -> None:
for hook in self._hooks: for hook in self._hooks:
try: try:
@ -1177,16 +1189,16 @@ deck_conf_will_remove_config = _DeckConfWillRemoveConfigHook()
class _DeckConfWillRenameConfigHook: class _DeckConfWillRenameConfigHook:
"""Called before config group is renamed""" """Called before config group is renamed"""
_hooks: List[Callable[["aqt.deckconf.DeckConf", Any, Any, str], None]] = [] _hooks: List[Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig, str], None]] = []
def append( def append(
self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any, str], None] self, cb: Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig, str], None]
) -> None: ) -> None:
"""(deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any, new_name: str)""" """(deck_conf: aqt.deckconf.DeckConf, deck: Deck, config: DeckConfig, new_name: str)"""
self._hooks.append(cb) self._hooks.append(cb)
def remove( def remove(
self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any, str], None] self, cb: Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig, str], None]
) -> None: ) -> None:
if cb in self._hooks: if cb in self._hooks:
self._hooks.remove(cb) self._hooks.remove(cb)
@ -1195,7 +1207,11 @@ class _DeckConfWillRenameConfigHook:
return len(self._hooks) return len(self._hooks)
def __call__( def __call__(
self, deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any, new_name: str self,
deck_conf: aqt.deckconf.DeckConf,
deck: Deck,
config: DeckConfig,
new_name: str,
) -> None: ) -> None:
for hook in self._hooks: for hook in self._hooks:
try: try:
@ -1212,13 +1228,17 @@ deck_conf_will_rename_config = _DeckConfWillRenameConfigHook()
class _DeckConfWillSaveConfigHook: class _DeckConfWillSaveConfigHook:
"""Called before widget state is saved to config""" """Called before widget state is saved to config"""
_hooks: List[Callable[["aqt.deckconf.DeckConf", Any, Any], None]] = [] _hooks: List[Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig], None]] = []
def append(self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any], None]) -> None: def append(
"""(deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any)""" self, cb: Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig], None]
) -> None:
"""(deck_conf: aqt.deckconf.DeckConf, deck: Deck, config: DeckConfig)"""
self._hooks.append(cb) self._hooks.append(cb)
def remove(self, cb: Callable[["aqt.deckconf.DeckConf", Any, Any], None]) -> None: def remove(
self, cb: Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig], None]
) -> None:
if cb in self._hooks: if cb in self._hooks:
self._hooks.remove(cb) self._hooks.remove(cb)
@ -1226,7 +1246,7 @@ class _DeckConfWillSaveConfigHook:
return len(self._hooks) return len(self._hooks)
def __call__( def __call__(
self, deck_conf: aqt.deckconf.DeckConf, deck: Any, config: Any self, deck_conf: aqt.deckconf.DeckConf, deck: Deck, config: DeckConfig
) -> None: ) -> None:
for hook in self._hooks: for hook in self._hooks:
try: try:
@ -1355,20 +1375,20 @@ editor_did_init = _EditorDidInitHook()
class _EditorDidInitButtonsHook: class _EditorDidInitButtonsHook:
_hooks: List[Callable[[List, "aqt.editor.Editor"], None]] = [] _hooks: List[Callable[[List[str], "aqt.editor.Editor"], None]] = []
def append(self, cb: Callable[[List, "aqt.editor.Editor"], None]) -> None: def append(self, cb: Callable[[List[str], "aqt.editor.Editor"], None]) -> None:
"""(buttons: List, editor: aqt.editor.Editor)""" """(buttons: List[str], editor: aqt.editor.Editor)"""
self._hooks.append(cb) self._hooks.append(cb)
def remove(self, cb: Callable[[List, "aqt.editor.Editor"], None]) -> None: def remove(self, cb: Callable[[List[str], "aqt.editor.Editor"], None]) -> None:
if cb in self._hooks: if cb in self._hooks:
self._hooks.remove(cb) self._hooks.remove(cb)
def count(self) -> int: def count(self) -> int:
return len(self._hooks) return len(self._hooks)
def __call__(self, buttons: List, editor: aqt.editor.Editor) -> None: def __call__(self, buttons: List[str], editor: aqt.editor.Editor) -> None:
for hook in self._hooks: for hook in self._hooks:
try: try:
hook(buttons, editor) hook(buttons, editor)

View File

@ -14,7 +14,7 @@ import weakref
import zipfile import zipfile
from argparse import Namespace from argparse import Namespace
from threading import Thread from threading import Thread
from typing import Any, Callable, Dict, List, Optional, Sequence, TextIO, Tuple, cast from typing import Any, Callable, List, Optional, Sequence, TextIO, Tuple, cast
import anki import anki
import aqt import aqt
@ -27,6 +27,7 @@ import aqt.toolbar
import aqt.webview import aqt.webview
from anki import hooks from anki import hooks
from anki.collection import Collection from anki.collection import Collection
from anki.decks import Deck
from anki.hooks import runHook from anki.hooks import runHook
from anki.lang import _, ngettext from anki.lang import _, ngettext
from anki.rsbackend import RustBackend from anki.rsbackend import RustBackend
@ -652,7 +653,7 @@ from the profile screen."
self.maybe_check_for_addon_updates() self.maybe_check_for_addon_updates()
self.deckBrowser.show() self.deckBrowser.show()
def _selectedDeck(self) -> Optional[Dict[str, Any]]: def _selectedDeck(self) -> Optional[Deck]:
did = self.col.decks.selected() did = self.col.decks.selected()
if not self.col.decks.nameOrNone(did): if not self.col.decks.nameOrNone(did):
showInfo(_("Please select a deck.")) showInfo(_("Please select a deck."))

View File

@ -185,6 +185,8 @@ class Models(QDialog):
class AddModel(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):
self.parent_ = parent or mw self.parent_ = parent or mw
self.mw = mw self.mw = mw

View File

@ -194,20 +194,20 @@ hooks = [
), ),
Hook( Hook(
name="deck_conf_did_load_config", name="deck_conf_did_load_config",
args=["deck_conf: aqt.deckconf.DeckConf", "deck: Any", "config: Any"], args=["deck_conf: aqt.deckconf.DeckConf", "deck: Deck", "config: DeckConfig"],
doc="Called once widget state has been set from deck config", doc="Called once widget state has been set from deck config",
), ),
Hook( Hook(
name="deck_conf_will_save_config", name="deck_conf_will_save_config",
args=["deck_conf: aqt.deckconf.DeckConf", "deck: Any", "config: Any"], args=["deck_conf: aqt.deckconf.DeckConf", "deck: Deck", "config: DeckConfig"],
doc="Called before widget state is saved to config", doc="Called before widget state is saved to config",
), ),
Hook( Hook(
name="deck_conf_did_add_config", name="deck_conf_did_add_config",
args=[ args=[
"deck_conf: aqt.deckconf.DeckConf", "deck_conf: aqt.deckconf.DeckConf",
"deck: Any", "deck: Deck",
"config: Any", "config: DeckConfig",
"new_name: str", "new_name: str",
"new_conf_id: int", "new_conf_id: int",
], ],
@ -224,15 +224,15 @@ hooks = [
), ),
Hook( Hook(
name="deck_conf_will_remove_config", name="deck_conf_will_remove_config",
args=["deck_conf: aqt.deckconf.DeckConf", "deck: Any", "config: Any"], args=["deck_conf: aqt.deckconf.DeckConf", "deck: Deck", "config: DeckConfig"],
doc="Called before current config group is removed", doc="Called before current config group is removed",
), ),
Hook( Hook(
name="deck_conf_will_rename_config", name="deck_conf_will_rename_config",
args=[ args=[
"deck_conf: aqt.deckconf.DeckConf", "deck_conf: aqt.deckconf.DeckConf",
"deck: Any", "deck: Deck",
"config: Any", "config: DeckConfig",
"new_name: str", "new_name: str",
], ],
doc="Called before config group is renamed", doc="Called before config group is renamed",
@ -530,7 +530,7 @@ hooks = [
################### ###################
Hook( Hook(
name="editor_did_init_buttons", name="editor_did_init_buttons",
args=["buttons: List", "editor: aqt.editor.Editor"], args=["buttons: List[str]", "editor: aqt.editor.Editor"],
), ),
Hook( Hook(
name="editor_did_init_shortcuts", name="editor_did_init_shortcuts",
@ -653,7 +653,7 @@ hooks = [
################### ###################
Hook( Hook(
name="current_note_type_did_change", name="current_note_type_did_change",
args=["notetype: Dict[str, Any]"], args=["notetype: NoteType"],
legacy_hook="currentModelChanged", legacy_hook="currentModelChanged",
legacy_no_args=True, legacy_no_args=True,
), ),