diff --git a/pylib/anki/cards.py b/pylib/anki/cards.py index 7d9a4a20a..b71ccaf74 100644 --- a/pylib/anki/cards.py +++ b/pylib/anki/cards.py @@ -32,6 +32,7 @@ class Card: timerStarted: Optional[float] lastIvl: int ord: int + nid: anki.notes.NoteID def __init__( self, col: anki.collection.Collection, id: Optional[int] = None @@ -56,7 +57,7 @@ class Card: self._render_output = None self._note = None self.id = c.id - self.nid = c.note_id + self.nid = anki.notes.NoteID(c.note_id) self.did = c.deck_id self.ord = c.template_idx self.mod = c.mtime_secs diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 41258e67c..67272d50f 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -43,7 +43,7 @@ from anki.errors import AnkiError, DBError from anki.lang import TR, FormatTimeSpan from anki.media import MediaManager, media_paths_from_col_path from anki.models import ModelManager, NoteType -from anki.notes import Note +from anki.notes import Note, NoteID from anki.scheduler.v1 import Scheduler as V1Scheduler from anki.scheduler.v2 import Scheduler as V2Scheduler from anki.scheduler.v3 import Scheduler as V3TestScheduler @@ -327,7 +327,7 @@ class Collection: Unlike card.flush(), this will invalidate any current checkpoint.""" self._backend.update_card(card=card._to_backend_card(), skip_undo_entry=False) - def get_note(self, id: int) -> Note: + def get_note(self, id: NoteID) -> Note: return Note(self, id=id) def update_note(self, note: Note) -> OpChanges: @@ -358,7 +358,7 @@ class Collection: # Deletion logging ########################################################################## - def _logRem(self, ids: List[int], type: int) -> None: + def _logRem(self, ids: List[Union[int, NoteID]], type: int) -> None: self.db.executemany( "insert into graves values (%d, ?, %d)" % (self.usn(), type), ([x] for x in ids), @@ -372,10 +372,10 @@ class Collection: def add_note(self, note: Note, deck_id: int) -> OpChanges: out = self._backend.add_note(note=note._to_backend_note(), deck_id=deck_id) - note.id = out.note_id + note.id = NoteID(out.note_id) return out.changes - def remove_notes(self, note_ids: Sequence[int]) -> OpChanges: + def remove_notes(self, note_ids: Sequence[NoteID]) -> OpChanges: hooks.notes_will_be_deleted(self, note_ids) return self._backend.remove_notes(note_ids=note_ids, card_ids=[]) @@ -387,7 +387,7 @@ class Collection: hooks.notes_will_be_deleted(self, nids) self._backend.remove_notes(note_ids=[], card_ids=card_ids) - def card_ids_of_note(self, note_id: int) -> Sequence[int]: + def card_ids_of_note(self, note_id: NoteID) -> Sequence[int]: return self._backend.cards_of_note(note_id) def defaults_for_adding( @@ -406,7 +406,7 @@ class Collection: home_deck_of_current_review_card=home_deck, ) - def default_deck_for_notetype(self, notetype_id: int) -> Optional[int]: + def default_deck_for_notetype(self, notetype_id: NoteID) -> Optional[int]: """If 'change deck depending on notetype' is enabled in the preferences, return the last deck used with the provided notetype, if any..""" if self.get_config_bool(Config.Bool.ADDING_DEFAULTS_TO_CURRENT_DECK): @@ -432,10 +432,10 @@ class Collection: self.add_note(note, note.model()["did"]) return len(note.cards()) - def remNotes(self, ids: Sequence[int]) -> None: + def remNotes(self, ids: Sequence[NoteID]) -> None: self.remove_notes(ids) - def _remNotes(self, ids: List[int]) -> None: + def _remNotes(self, ids: List[NoteID]) -> None: pass # Cards @@ -470,7 +470,7 @@ class Collection: ########################################################################## def after_note_updates( - self, nids: List[int], mark_modified: bool, generate_cards: bool = True + self, nids: List[NoteID], mark_modified: bool, generate_cards: bool = True ) -> None: self._backend.after_note_updates( nids=nids, generate_cards=generate_cards, mark_notes_modified=mark_modified @@ -478,11 +478,11 @@ class Collection: # legacy - def updateFieldCache(self, nids: List[int]) -> None: + def updateFieldCache(self, nids: List[NoteID]) -> None: self.after_note_updates(nids, mark_modified=False, generate_cards=False) # this also updates field cache - def genCards(self, nids: List[int]) -> List[int]: + def genCards(self, nids: List[NoteID]) -> List[int]: self.after_note_updates(nids, mark_modified=False, generate_cards=True) # previously returned empty cards, no longer does return [] @@ -527,7 +527,7 @@ class Collection: ) return self._backend.search_cards(search=query, order=mode) - def find_notes(self, *terms: Union[str, SearchNode]) -> Sequence[int]: + def find_notes(self, *terms: Union[str, SearchNode]) -> Sequence[NoteID]: """Return note ids matching the provided search or searches. If more than one search is provided, they will be ANDed together. @@ -538,12 +538,15 @@ class Collection: Eg: col.find_notes(SearchNode(deck="test"), "foo") will return notes that have a card in deck called "test", and have the text "foo". """ - return self._backend.search_notes(self.build_search_string(*terms)) + return [ + NoteID(did) + for did in self._backend.search_notes(self.build_search_string(*terms)) + ] def find_and_replace( self, *, - note_ids: Sequence[int], + note_ids: Sequence[NoteID], search: str, replacement: str, regex: bool = False, diff --git a/pylib/anki/find.py b/pylib/anki/find.py index 14692531a..659797554 100644 --- a/pylib/anki/find.py +++ b/pylib/anki/find.py @@ -6,6 +6,7 @@ from __future__ import annotations from typing import TYPE_CHECKING, Optional, Set from anki.hooks import * +from anki.notes import NoteID if TYPE_CHECKING: from anki.collection import Collection @@ -29,7 +30,7 @@ class Finder: def findReplace( col: Collection, - nids: List[int], + nids: List[NoteID], src: str, dst: str, regex: bool = False, @@ -48,7 +49,7 @@ def findReplace( ).count -def fieldNamesForNotes(col: Collection, nids: List[int]) -> List[str]: +def fieldNamesForNotes(col: Collection, nids: List[NoteID]) -> List[str]: return list(col.field_names_for_note_ids(nids)) diff --git a/pylib/anki/importing/anki2.py b/pylib/anki/importing/anki2.py index b29137396..126a6bf3a 100644 --- a/pylib/anki/importing/anki2.py +++ b/pylib/anki/importing/anki2.py @@ -10,6 +10,7 @@ from anki.consts import * from anki.decks import DeckManager from anki.importing.base import Importer from anki.lang import TR +from anki.notes import NoteID from anki.utils import intTime, joinFields, splitFields, stripHTMLMedia GUID = 1 @@ -79,7 +80,7 @@ class Anki2Importer(Importer): def _importNotes(self) -> None: # build guid -> (id,mod,mid) hash & map of existing note ids - self._notes: Dict[str, Tuple[int, int, int]] = {} + self._notes: Dict[str, Tuple[NoteID, int, int]] = {} existing = {} for id, guid, mod, mid in self.dst.db.execute( "select id, guid, mod, mid from notes" diff --git a/pylib/anki/importing/noteimp.py b/pylib/anki/importing/noteimp.py index 8fe5ae3b7..5bb713fbe 100644 --- a/pylib/anki/importing/noteimp.py +++ b/pylib/anki/importing/noteimp.py @@ -10,6 +10,7 @@ from anki.config import Config from anki.consts import NEW_CARDS_RANDOM, STARTING_FACTOR from anki.importing.base import Importer from anki.lang import TR +from anki.notes import NoteID from anki.utils import ( fieldChecksum, guid64, @@ -19,9 +20,9 @@ from anki.utils import ( timestampID, ) -type_tagsMapped = Tuple[int, int, str, str, int, str, str] -type_tagsModified = Tuple[int, int, str, str, int, str] -type_tagsElse = Tuple[int, int, str, int, str] +type_tagsMapped = Tuple[int, int, str, str, NoteID, str, str] +type_tagsModified = Tuple[int, int, str, str, NoteID, str] +type_tagsElse = Tuple[int, int, str, NoteID, str] type_udpates = Union[type_tagsMapped, type_tagsModified, type_tagsElse] # Stores a list of fields, tags and deck @@ -127,7 +128,7 @@ class NoteImporter(Importer): if f == "_tags": self._tagsMapped = True # gather checks for duplicate comparison - csums: Dict[str, List[int]] = {} + csums: Dict[str, List[NoteID]] = {} for csum, id in self.col.db.execute( "select csum, id from notes where mid = ?", self.model["id"] ): @@ -138,12 +139,12 @@ class NoteImporter(Importer): firsts: Dict[str, bool] = {} fld0idx = self.mapping.index(self.model["flds"][0]["name"]) self._fmap = self.col.models.fieldMap(self.model) - self._nextID = timestampID(self.col.db, "notes") + self._nextID = NoteID(timestampID(self.col.db, "notes")) # loop through the notes updates: List[type_udpates] = [] updateLog = [] new = [] - self._ids: List[int] = [] + self._ids: List[NoteID] = [] self._cards: List[Tuple] = [] dupeCount = 0 dupes: List[str] = [] @@ -242,9 +243,9 @@ class NoteImporter(Importer): def newData( self, n: ForeignNote - ) -> Tuple[int, str, int, int, int, str, str, str, int, int, str]: + ) -> Tuple[NoteID, str, int, int, int, str, str, str, int, int, str]: id = self._nextID - self._nextID += 1 + self._nextID = NoteID(self._nextID + 1) self._ids.append(id) self.processFields(n) # note id for card updates later @@ -266,14 +267,14 @@ class NoteImporter(Importer): def addNew( self, - rows: List[Tuple[int, str, int, int, int, str, str, str, int, int, str]], + rows: List[Tuple[NoteID, str, int, int, int, str, str, str, int, int, str]], ) -> None: self.col.db.executemany( "insert or replace into notes values (?,?,?,?,?,?,?,?,?,?,?)", rows ) def updateData( - self, n: ForeignNote, id: int, sflds: List[str] + self, n: ForeignNote, id: NoteID, sflds: List[str] ) -> Optional[type_udpates]: self._ids.append(id) self.processFields(n, sflds) diff --git a/pylib/anki/models.py b/pylib/anki/models.py index c585c1b61..735b1ec57 100644 --- a/pylib/anki/models.py +++ b/pylib/anki/models.py @@ -257,7 +257,7 @@ class ModelManager: # Tools ################################################## - def nids(self, ntid: int) -> List[int]: + def nids(self, ntid: int) -> List[anki.notes.NoteID]: "Note ids for M." if isinstance(ntid, dict): # legacy callers passed in note type @@ -420,7 +420,7 @@ and notes.mid = ? and cards.ord = ?""", def change( self, m: NoteType, - nids: List[int], + nids: List[anki.notes.NoteID], newModel: NoteType, fmap: Optional[Dict[int, Union[None, int]]], cmap: Optional[Dict[int, Union[None, int]]], @@ -434,7 +434,10 @@ and notes.mid = ? and cards.ord = ?""", self.col.after_note_updates(nids, mark_modified=True) def _changeNotes( - self, nids: List[int], newModel: NoteType, map: Dict[int, Union[None, int]] + self, + nids: List[anki.notes.NoteID], + newModel: NoteType, + map: Dict[int, Union[None, int]], ) -> None: d = [] nfields = len(newModel["flds"]) @@ -464,7 +467,7 @@ and notes.mid = ? and cards.ord = ?""", def _changeCards( self, - nids: List[int], + nids: List[anki.notes.NoteID], oldModel: NoteType, newModel: NoteType, map: Dict[int, Union[None, int]], diff --git a/pylib/anki/notes.py b/pylib/anki/notes.py index b438937db..b0b477858 100644 --- a/pylib/anki/notes.py +++ b/pylib/anki/notes.py @@ -5,7 +5,7 @@ from __future__ import annotations import copy import pprint -from typing import Any, List, Optional, Sequence, Tuple +from typing import Any, List, NewType, Optional, Sequence, Tuple import anki # pylint: disable=unused-import import anki._backend.backend_pb2 as _pb @@ -16,6 +16,9 @@ from anki.utils import joinFields DuplicateOrEmptyResult = _pb.NoteIsDuplicateOrEmptyOut.State +# types +NoteID = NewType("NoteID", int) + class Note: # not currently exposed @@ -26,7 +29,7 @@ class Note: self, col: anki.collection.Collection, model: Optional[NoteType] = None, - id: Optional[int] = None, + id: Optional[NoteID] = None, ) -> None: assert not (model and id) self.col = col.weakref() @@ -46,7 +49,7 @@ class Note: self._load_from_backend_note(n) def _load_from_backend_note(self, n: _pb.Note) -> None: - self.id = n.id + self.id = NoteID(n.id) self.guid = n.guid self.mid = n.notetype_id self.mod = n.mtime_secs diff --git a/pylib/anki/scheduler/legacy.py b/pylib/anki/scheduler/legacy.py index db13bd715..f75c30294 100644 --- a/pylib/anki/scheduler/legacy.py +++ b/pylib/anki/scheduler/legacy.py @@ -6,6 +6,7 @@ from typing import List, Optional, Tuple from anki.cards import Card from anki.consts import CARD_TYPE_RELEARNING, QUEUE_TYPE_DAY_LEARN_RELEARN from anki.decks import DeckConfigDict +from anki.notes import NoteID from anki.scheduler.base import SchedulerBase, UnburyCurrentDeck from anki.utils import from_json_bytes, ids2str @@ -18,7 +19,7 @@ class SchedulerBaseWithLegacy(SchedulerBase): ) -> None: self.set_due_date(card_ids, f"{min_interval}-{max_interval}!") - def buryNote(self, nid: int) -> None: + def buryNote(self, nid: NoteID) -> None: note = self.col.get_note(nid) self.bury_cards(note.card_ids()) diff --git a/pylib/anki/tags.py b/pylib/anki/tags.py index d139d9f23..fa57d7502 100644 --- a/pylib/anki/tags.py +++ b/pylib/anki/tags.py @@ -19,6 +19,7 @@ import anki # pylint: disable=unused-import import anki._backend.backend_pb2 as _pb import anki.collection from anki.collection import OpChangesWithCount +from anki.notes import NoteID from anki.utils import ids2str # public exports @@ -68,11 +69,11 @@ class TagManager: # Bulk addition/removal from specific notes ############################################################# - def bulk_add(self, note_ids: Sequence[int], tags: str) -> OpChangesWithCount: + def bulk_add(self, note_ids: Sequence[NoteID], tags: str) -> OpChangesWithCount: """Add space-separate tags to provided notes, returning changed count.""" return self.col._backend.add_note_tags(note_ids=note_ids, tags=tags) - def bulk_remove(self, note_ids: Sequence[int], tags: str) -> OpChangesWithCount: + def bulk_remove(self, note_ids: Sequence[NoteID], tags: str) -> OpChangesWithCount: return self.col._backend.remove_note_tags(note_ids=note_ids, tags=tags) # Find&replace @@ -175,12 +176,12 @@ class TagManager: ) -> None: print("tags.register() is deprecated and no longer works") - def bulkAdd(self, ids: List[int], tags: str, add: bool = True) -> None: + def bulkAdd(self, ids: List[NoteID], tags: str, add: bool = True) -> None: "Add tags in bulk. TAGS is space-separated." if add: self.bulk_add(ids, tags) else: self.bulk_remove(ids, tags) - def bulkRem(self, ids: List[int], tags: str) -> None: + def bulkRem(self, ids: List[NoteID], tags: str) -> None: self.bulkAdd(ids, tags, False) diff --git a/pylib/tools/genhooks.py b/pylib/tools/genhooks.py index a4d32c2ec..e47da9cef 100644 --- a/pylib/tools/genhooks.py +++ b/pylib/tools/genhooks.py @@ -23,7 +23,7 @@ hooks = [ Hook(name="schema_will_change", args=["proceed: bool"], return_type="bool"), Hook( name="notes_will_be_deleted", - args=["col: anki.collection.Collection", "ids: Sequence[int]"], + args=["col: anki.collection.Collection", "ids: Sequence[anki.notes.NoteID]"], legacy_hook="remNotes", ), Hook(name="media_files_did_export", args=["count: int"]), diff --git a/qt/aqt/addcards.py b/qt/aqt/addcards.py index 0b1c7f3c2..fc57e8831 100644 --- a/qt/aqt/addcards.py +++ b/qt/aqt/addcards.py @@ -8,7 +8,7 @@ import aqt.editor import aqt.forms from anki.collection import OpChanges, SearchNode from anki.consts import MODEL_CLOZE -from anki.notes import DuplicateOrEmptyResult, Note +from anki.notes import DuplicateOrEmptyResult, Note, NoteID from anki.utils import htmlToTextLine, isMac from aqt import AnkiQt, gui_hooks from aqt.note_ops import add_note @@ -47,7 +47,7 @@ class AddCards(QDialog): self.setupEditor() self.setupButtons() self._load_new_note() - self.history: List[int] = [] + self.history: List[NoteID] = [] self._last_added_note: Optional[Note] = None restoreGeom(self, "add") addCloseShortcut(self) @@ -109,7 +109,7 @@ class AddCards(QDialog): def show_notetype_selector(self) -> None: self.editor.call_after_note_saved(self.notetype_chooser.choose_notetype) - def on_notetype_change(self, notetype_id: int) -> None: + def on_notetype_change(self, notetype_id: NoteID) -> None: # need to adjust current deck? if deck_id := self.mw.col.default_deck_for_notetype(notetype_id): self.deck_chooser.selected_deck_id = deck_id @@ -178,7 +178,7 @@ class AddCards(QDialog): gui_hooks.add_cards_will_show_history_menu(self, m) m.exec_(self.historyButton.mapToGlobal(QPoint(0, 0))) - def editHistory(self, nid: int) -> None: + def editHistory(self, nid: NoteID) -> None: aqt.dialogs.open("Browser", self.mw, search=(SearchNode(nid=nid),)) def add_current_note(self) -> None: diff --git a/qt/aqt/browser.py b/qt/aqt/browser.py index abba7cd51..1e0cd98fe 100644 --- a/qt/aqt/browser.py +++ b/qt/aqt/browser.py @@ -28,6 +28,7 @@ from anki.consts import * from anki.errors import NotFoundError from anki.lang import without_unicode_isolation from anki.models import NoteType +from anki.notes import NoteID from anki.stats import CardStats from anki.tags import MARKED_TAG from anki.utils import ids2str, isMac, isWin @@ -1054,7 +1055,7 @@ QTableView {{ gridline-color: {grid} }} for idx in self.form.tableView.selectionModel().selectedRows() ] - def selected_notes(self) -> List[int]: + def selected_notes(self) -> List[NoteID]: return self.col.db.list( """ select distinct nid from cards @@ -1073,7 +1074,7 @@ where id in %s""" % ",".join([str(s) for s in self.selected_notes()]) ) - def oneModelNotes(self) -> List[int]: + def oneModelNotes(self) -> List[NoteID]: sf = self.selected_notes() if not sf: return [] @@ -1603,7 +1604,7 @@ where id in %s""" class ChangeModel(QDialog): - def __init__(self, browser: Browser, nids: List[int]) -> None: + def __init__(self, browser: Browser, nids: List[NoteID]) -> None: QDialog.__init__(self, browser) self.browser = browser self.nids = nids diff --git a/qt/aqt/find_and_replace.py b/qt/aqt/find_and_replace.py index 52d4c0b5d..1c7fc3207 100644 --- a/qt/aqt/find_and_replace.py +++ b/qt/aqt/find_and_replace.py @@ -7,6 +7,7 @@ from typing import List, Optional, Sequence import aqt from anki.lang import TR +from anki.notes import NoteID from aqt import AnkiQt, QWidget from aqt.qt import QDialog, Qt from aqt.utils import ( @@ -31,7 +32,7 @@ def find_and_replace( *, mw: AnkiQt, parent: QWidget, - note_ids: Sequence[int], + note_ids: Sequence[NoteID], search: str, replacement: str, regex: bool, @@ -82,7 +83,9 @@ def find_and_replace_tag( class FindAndReplaceDialog(QDialog): COMBO_NAME = "BrowserFindAndReplace" - def __init__(self, parent: QWidget, *, mw: AnkiQt, note_ids: Sequence[int]) -> None: + def __init__( + self, parent: QWidget, *, mw: AnkiQt, note_ids: Sequence[NoteID] + ) -> None: super().__init__(parent) self.mw = mw self.note_ids = note_ids diff --git a/qt/aqt/main.py b/qt/aqt/main.py index f9e97fd74..e8e8fcd59 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -54,6 +54,7 @@ from anki.collection import ( ) from anki.decks import DeckDict from anki.hooks import runHook +from anki.notes import NoteID from anki.sound import AVTag, SoundOrVideoTag from anki.types import assert_exhaustive from anki.utils import devMode, ids2str, intTime, isMac, isWin, splitFields @@ -1534,7 +1535,7 @@ title="%s" %s>%s""" % ( # Log note deletion ########################################################################## - def onRemNotes(self, col: Collection, nids: Sequence[int]) -> None: + def onRemNotes(self, col: Collection, nids: Sequence[NoteID]) -> None: path = os.path.join(self.pm.profileFolder(), "deleted.txt") existed = os.path.exists(path) with open(path, "ab") as f: diff --git a/qt/aqt/note_ops.py b/qt/aqt/note_ops.py index 582a380b4..042490988 100644 --- a/qt/aqt/note_ops.py +++ b/qt/aqt/note_ops.py @@ -5,7 +5,7 @@ from __future__ import annotations from typing import Callable, Sequence -from anki.notes import Note +from anki.notes import Note, NoteID from aqt import AnkiQt from aqt.main import PerformOpOptionalSuccessCallback @@ -30,7 +30,7 @@ def update_note(*, mw: AnkiQt, note: Note, after_hooks: Callable[[], None]) -> N def remove_notes( *, mw: AnkiQt, - note_ids: Sequence[int], + note_ids: Sequence[NoteID], success: PerformOpOptionalSuccessCallback = None, ) -> None: mw.perform_op(lambda: mw.col.remove_notes(note_ids), success=success) diff --git a/qt/aqt/notetypechooser.py b/qt/aqt/notetypechooser.py index dfddd5bd1..870a696e4 100644 --- a/qt/aqt/notetypechooser.py +++ b/qt/aqt/notetypechooser.py @@ -2,6 +2,7 @@ # License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html from typing import List, Optional +from anki.notes import NoteID from aqt import AnkiQt, gui_hooks from aqt.qt import * from aqt.utils import TR, HelpPage, shortcut, tr @@ -29,7 +30,7 @@ class NoteTypeChooser(QHBoxLayout): widget: QWidget, starting_notetype_id: int, on_button_activated: Optional[Callable[[], None]] = None, - on_notetype_changed: Optional[Callable[[int], None]] = None, + on_notetype_changed: Optional[Callable[[NoteID], None]] = None, show_prefix_label: bool = True, ) -> None: QHBoxLayout.__init__(self) diff --git a/qt/aqt/scheduling_ops.py b/qt/aqt/scheduling_ops.py index 93828ffc0..43c594c30 100644 --- a/qt/aqt/scheduling_ops.py +++ b/qt/aqt/scheduling_ops.py @@ -9,6 +9,7 @@ import aqt from anki.collection import CARD_TYPE_NEW, Config from anki.decks import DeckID from anki.lang import TR +from anki.notes import NoteID from anki.scheduler import FilteredDeckForUpdate from aqt import AnkiQt from aqt.main import PerformOpOptionalSuccessCallback @@ -143,7 +144,7 @@ def suspend_cards( def suspend_note( *, mw: AnkiQt, - note_id: int, + note_id: NoteID, success: PerformOpOptionalSuccessCallback = None, ) -> None: mw.taskman.run_in_background( @@ -168,7 +169,7 @@ def bury_cards( def bury_note( *, mw: AnkiQt, - note_id: int, + note_id: NoteID, success: PerformOpOptionalSuccessCallback = None, ) -> None: mw.taskman.run_in_background( diff --git a/qt/aqt/tag_ops.py b/qt/aqt/tag_ops.py index f5f68abf4..d99ab8d65 100644 --- a/qt/aqt/tag_ops.py +++ b/qt/aqt/tag_ops.py @@ -7,6 +7,7 @@ from typing import Callable, Sequence from anki.collection import OpChangesWithCount from anki.lang import TR +from anki.notes import NoteID from aqt import AnkiQt, QWidget from aqt.main import PerformOpOptionalSuccessCallback from aqt.utils import showInfo, tooltip, tr @@ -15,7 +16,7 @@ from aqt.utils import showInfo, tooltip, tr def add_tags( *, mw: AnkiQt, - note_ids: Sequence[int], + note_ids: Sequence[NoteID], space_separated_tags: str, success: PerformOpOptionalSuccessCallback = None, ) -> None: @@ -27,7 +28,7 @@ def add_tags( def remove_tags_for_notes( *, mw: AnkiQt, - note_ids: Sequence[int], + note_ids: Sequence[NoteID], space_separated_tags: str, success: PerformOpOptionalSuccessCallback = None, ) -> None: