diff --git a/pylib/anki/cards.py b/pylib/anki/cards.py index b71ccaf74..844c4f048 100644 --- a/pylib/anki/cards.py +++ b/pylib/anki/cards.py @@ -5,7 +5,7 @@ from __future__ import annotations import pprint import time -from typing import List, Optional +from typing import List, NewType, Optional import anki # pylint: disable=unused-import import anki._backend.backend_pb2 as _pb @@ -26,6 +26,9 @@ from anki.sound import AVTag # - rev queue: integer day # - lrn queue: integer timestamp +# types +CardID = NewType("CardID", int) + class Card: _note: Optional[Note] @@ -33,9 +36,10 @@ class Card: lastIvl: int ord: int nid: anki.notes.NoteID + id: CardID def __init__( - self, col: anki.collection.Collection, id: Optional[int] = None + self, col: anki.collection.Collection, id: Optional[CardID] = None ) -> None: self.col = col.weakref() self.timerStarted = None @@ -56,7 +60,7 @@ class Card: def _load_from_backend_card(self, c: _pb.Card) -> None: self._render_output = None self._note = None - self.id = c.id + self.id = CardID(c.id) self.nid = anki.notes.NoteID(c.note_id) self.did = c.deck_id self.ord = c.template_idx diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 67272d50f..fa0c8ddfe 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -34,7 +34,7 @@ from dataclasses import dataclass, field import anki.latex from anki import hooks from anki._backend import RustBackend -from anki.cards import Card +from anki.cards import Card, CardID from anki.config import Config, ConfigManager from anki.consts import * from anki.dbproxy import DBProxy @@ -319,7 +319,7 @@ class Collection: # Object creation helpers ########################################################################## - def get_card(self, id: int) -> Card: + def get_card(self, id: CardID) -> Card: return Card(self, id) def update_card(self, card: Card) -> None: @@ -379,7 +379,7 @@ class Collection: hooks.notes_will_be_deleted(self, note_ids) return self._backend.remove_notes(note_ids=note_ids, card_ids=[]) - def remove_notes_by_card(self, card_ids: List[int]) -> None: + def remove_notes_by_card(self, card_ids: List[CardID]) -> None: if hooks.notes_will_be_deleted.count(): nids = self.db.list( f"select nid from cards where id in {ids2str(card_ids)}" @@ -387,8 +387,8 @@ 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: NoteID) -> Sequence[int]: - return self._backend.cards_of_note(note_id) + def card_ids_of_note(self, note_id: NoteID) -> Sequence[CardID]: + return [CardID(id) for id in self._backend.cards_of_note(note_id)] def defaults_for_adding( self, *, current_review_card: Optional[Card] @@ -447,11 +447,11 @@ class Collection: def cardCount(self) -> Any: return self.db.scalar("select count() from cards") - def remove_cards_and_orphaned_notes(self, card_ids: Sequence[int]) -> None: + def remove_cards_and_orphaned_notes(self, card_ids: Sequence[CardID]) -> None: "You probably want .remove_notes_by_card() instead." self._backend.remove_cards(card_ids=card_ids) - def set_deck(self, card_ids: Sequence[int], deck_id: int) -> OpChanges: + def set_deck(self, card_ids: Sequence[CardID], deck_id: int) -> OpChanges: return self._backend.set_deck(card_ids=card_ids, deck_id=deck_id) def get_empty_cards(self) -> EmptyCardsReport: @@ -459,10 +459,10 @@ class Collection: # legacy - def remCards(self, ids: List[int], notes: bool = True) -> None: + def remCards(self, ids: List[CardID], notes: bool = True) -> None: self.remove_cards_and_orphaned_notes(ids) - def emptyCids(self) -> List[int]: + def emptyCids(self) -> List[CardID]: print("emptyCids() will go away") return [] @@ -495,7 +495,7 @@ class Collection: query: str, order: Union[bool, str, BuiltinSort.Kind.V] = False, reverse: bool = False, - ) -> Sequence[int]: + ) -> Sequence[CardID]: """Return card ids matching the provided search. To programmatically construct a search string, see .build_search_string(). @@ -525,7 +525,9 @@ class Collection: mode = _pb.SortOrder( builtin=_pb.SortOrder.Builtin(kind=order, reverse=reverse) ) - return self._backend.search_cards(search=query, order=mode) + return [ + CardID(id) for id in self._backend.search_cards(search=query, order=mode) + ] def find_notes(self, *terms: Union[str, SearchNode]) -> Sequence[NoteID]: """Return note ids matching the provided search or searches. @@ -740,7 +742,7 @@ class Collection: return CollectionStats(self) - def card_stats(self, card_id: int, include_revlog: bool) -> str: + def card_stats(self, card_id: CardID, include_revlog: bool) -> str: import anki.stats as st if include_revlog: @@ -1033,7 +1035,7 @@ table.review-log {{ {revlog_style} }} ########################################################################## - def set_user_flag_for_cards(self, flag: int, cids: Sequence[int]) -> OpChanges: + def set_user_flag_for_cards(self, flag: int, cids: Sequence[CardID]) -> OpChanges: return self._backend.set_flag(card_ids=cids, flag=flag) def set_wants_abort(self) -> None: diff --git a/pylib/anki/decks.py b/pylib/anki/decks.py index c3a1ec56b..3c38df99c 100644 --- a/pylib/anki/decks.py +++ b/pylib/anki/decks.py @@ -11,6 +11,7 @@ from typing import Any, Dict, Iterable, List, NewType, Optional, Sequence, Tuple import anki # pylint: disable=unused-import import anki._backend.backend_pb2 as _pb +from anki.cards import CardID from anki.collection import OpChanges, OpChangesWithCount, OpChangesWithID from anki.consts import * from anki.errors import NotFoundError @@ -406,7 +407,7 @@ class DeckManager: return deck["name"] return None - def setDeck(self, cids: List[int], did: int) -> None: + def setDeck(self, cids: List[CardID], did: int) -> None: self.col.db.execute( f"update cards set did=?,usn=?,mod=? where id in {ids2str(cids)}", did, @@ -414,7 +415,7 @@ class DeckManager: intTime(), ) - def cids(self, did: int, children: bool = False) -> List[int]: + def cids(self, did: int, children: bool = False) -> List[CardID]: if not children: return self.col.db.list("select id from cards where did=?", did) dids = [did] @@ -422,7 +423,7 @@ class DeckManager: dids.append(id) return self.col.db.list(f"select id from cards where did in {ids2str(dids)}") - def for_card_ids(self, cids: List[int]) -> List[int]: + def for_card_ids(self, cids: List[CardID]) -> List[int]: return self.col.db.list(f"select did from cards where id in {ids2str(cids)}") # Deck selection diff --git a/pylib/anki/exporting.py b/pylib/anki/exporting.py index 6a9d7fc9a..1a559c448 100644 --- a/pylib/anki/exporting.py +++ b/pylib/anki/exporting.py @@ -12,6 +12,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union from zipfile import ZipFile from anki import hooks +from anki.cards import CardID from anki.collection import Collection from anki.lang import TR from anki.utils import ids2str, namedtmp, splitFields, stripHTML @@ -28,7 +29,7 @@ class Exporter: self, col: Collection, did: Optional[int] = None, - cids: Optional[List[int]] = None, + cids: Optional[List[CardID]] = None, ) -> None: self.col = col.weakref() self.did = did diff --git a/pylib/anki/importing/anki2.py b/pylib/anki/importing/anki2.py index 126a6bf3a..15eb3b606 100644 --- a/pylib/anki/importing/anki2.py +++ b/pylib/anki/importing/anki2.py @@ -5,6 +5,7 @@ import os import unicodedata from typing import Any, Dict, List, Optional, Tuple +from anki.cards import CardID from anki.collection import Collection from anki.consts import * from anki.decks import DeckManager @@ -306,7 +307,7 @@ class Anki2Importer(Importer): if self.source_needs_upgrade: self.src.upgrade_to_v2_scheduler() # build map of (guid, ord) -> cid and used id cache - self._cards: Dict[Tuple[str, int], int] = {} + self._cards: Dict[Tuple[str, int], CardID] = {} existing = {} for guid, ord, cid in self.dst.db.execute( "select f.guid, c.ord, c.id from cards c, notes f " "where c.nid = f.id" diff --git a/pylib/anki/notes.py b/pylib/anki/notes.py index b0b477858..1f2895e08 100644 --- a/pylib/anki/notes.py +++ b/pylib/anki/notes.py @@ -24,6 +24,7 @@ class Note: # not currently exposed flags = 0 data = "" + id: NoteID def __init__( self, @@ -122,7 +123,7 @@ class Note: def cards(self) -> List[anki.cards.Card]: return [self.col.getCard(id) for id in self.card_ids()] - def card_ids(self) -> Sequence[int]: + def card_ids(self) -> Sequence[anki.cards.CardID]: return self.col.card_ids_of_note(self.id) def model(self) -> Optional[NoteType]: diff --git a/pylib/anki/scheduler/base.py b/pylib/anki/scheduler/base.py index 81e7c3954..e61bae378 100644 --- a/pylib/anki/scheduler/base.py +++ b/pylib/anki/scheduler/base.py @@ -13,6 +13,7 @@ SchedTimingToday = _pb.SchedTimingTodayOut from typing import List, Optional, Sequence +from anki.cards import CardID from anki.consts import CARD_TYPE_NEW, NEW_CARDS_RANDOM, QUEUE_TYPE_NEW, QUEUE_TYPE_REV from anki.decks import DeckConfigDict, DeckID, DeckTreeNode from anki.notes import Note @@ -107,10 +108,10 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l # Suspending & burying ########################################################################## - def unsuspend_cards(self, ids: Sequence[int]) -> OpChanges: + def unsuspend_cards(self, ids: Sequence[CardID]) -> OpChanges: return self.col._backend.restore_buried_and_suspended_cards(ids) - def unbury_cards(self, ids: List[int]) -> OpChanges: + def unbury_cards(self, ids: List[CardID]) -> OpChanges: return self.col._backend.restore_buried_and_suspended_cards(ids) def unbury_cards_in_current_deck( @@ -119,12 +120,12 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l ) -> None: self.col._backend.unbury_cards_in_current_deck(mode) - def suspend_cards(self, ids: Sequence[int]) -> OpChanges: + def suspend_cards(self, ids: Sequence[CardID]) -> OpChanges: return self.col._backend.bury_or_suspend_cards( card_ids=ids, mode=BuryOrSuspend.SUSPEND ) - def bury_cards(self, ids: Sequence[int], manual: bool = True) -> OpChanges: + def bury_cards(self, ids: Sequence[CardID], manual: bool = True) -> OpChanges: if manual: mode = BuryOrSuspend.BURY_USER else: @@ -137,13 +138,13 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l # Resetting/rescheduling ########################################################################## - def schedule_cards_as_new(self, card_ids: List[int]) -> OpChanges: + def schedule_cards_as_new(self, card_ids: List[CardID]) -> OpChanges: "Put cards at the end of the new queue." return self.col._backend.schedule_cards_as_new(card_ids=card_ids, log=True) def set_due_date( self, - card_ids: List[int], + card_ids: List[CardID], days: str, config_key: Optional[Config.String.Key.V] = None, ) -> OpChanges: @@ -162,7 +163,7 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l config_key=key, # type: ignore ) - def resetCards(self, ids: List[int]) -> None: + def resetCards(self, ids: List[CardID]) -> None: "Completely reset cards for export." sids = ids2str(ids) assert self.col.db @@ -184,7 +185,7 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l def reposition_new_cards( self, - card_ids: Sequence[int], + card_ids: Sequence[CardID], starting_from: int, step_size: int, randomize: bool, @@ -223,7 +224,7 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l # legacy def sortCards( self, - cids: List[int], + cids: List[CardID], start: int = 1, step: int = 1, shuffle: bool = False, diff --git a/pylib/anki/scheduler/legacy.py b/pylib/anki/scheduler/legacy.py index f75c30294..6eb2e1023 100644 --- a/pylib/anki/scheduler/legacy.py +++ b/pylib/anki/scheduler/legacy.py @@ -3,7 +3,7 @@ from typing import List, Optional, Tuple -from anki.cards import Card +from anki.cards import Card, CardID from anki.consts import CARD_TYPE_RELEARNING, QUEUE_TYPE_DAY_LEARN_RELEARN from anki.decks import DeckConfigDict from anki.notes import NoteID @@ -15,7 +15,7 @@ class SchedulerBaseWithLegacy(SchedulerBase): "Legacy aliases and helpers. These will go away in the future." def reschedCards( - self, card_ids: List[int], min_interval: int, max_interval: int + self, card_ids: List[CardID], min_interval: int, max_interval: int ) -> None: self.set_due_date(card_ids, f"{min_interval}-{max_interval}!") @@ -80,7 +80,7 @@ due = (case when odue>0 then odue else due end), odue = 0, odid = 0, usn = ? whe self.col.usn(), ) - def remFromDyn(self, cids: List[int]) -> None: + def remFromDyn(self, cids: List[CardID]) -> None: self.emptyDyn(None, f"id in {ids2str(cids)} and odid") # used by v2 scheduler and some add-ons diff --git a/pylib/anki/scheduler/v2.py b/pylib/anki/scheduler/v2.py index b0ec0467c..a6d1aa8c5 100644 --- a/pylib/anki/scheduler/v2.py +++ b/pylib/anki/scheduler/v2.py @@ -11,7 +11,7 @@ from typing import Any, Callable, Dict, List, Optional, Tuple, Union import anki # pylint: disable=unused-import import anki._backend.backend_pb2 as _pb from anki import hooks -from anki.cards import Card +from anki.cards import Card, CardID from anki.consts import * from anki.decks import DeckConfigDict, DeckDict from anki.lang import FormatTimeSpan @@ -144,7 +144,7 @@ class Scheduler(SchedulerBaseWithLegacy): def _resetNew(self) -> None: self._newDids = self.col.decks.active()[:] - self._newQueue: List[int] = [] + self._newQueue: List[CardID] = [] self._updateNewCardRatio() def _fillNew(self, recursing: bool = False) -> bool: @@ -301,8 +301,8 @@ select count() from cards where did in %s and queue = {QUEUE_TYPE_PREVIEW} def _resetLrn(self) -> None: self._updateLrnCutoff(force=True) self._resetLrnCount() - self._lrnQueue: List[Tuple[int, int]] = [] - self._lrnDayQueue: List[int] = [] + self._lrnQueue: List[Tuple[int, CardID]] = [] + self._lrnDayQueue: List[CardID] = [] self._lrnDids = self.col.decks.active()[:] # sub-day learning @@ -397,7 +397,7 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""", return hooks.scheduler_review_limit_for_single_deck(lim, d) def _resetRev(self) -> None: - self._revQueue: List[int] = [] + self._revQueue: List[CardID] = [] def _fillRev(self, recursing: bool = False) -> bool: "True if a review card can be fetched." @@ -1072,7 +1072,7 @@ limit ?""" ########################################################################## def _burySiblings(self, card: Card) -> None: - toBury: List[int] = [] + toBury: List[CardID] = [] nconf = self._newConf(card) buryNew = nconf.get("bury", True) rconf = self._revConf(card) diff --git a/qt/aqt/browser.py b/qt/aqt/browser.py index 1e0cd98fe..b000976fe 100644 --- a/qt/aqt/browser.py +++ b/qt/aqt/browser.py @@ -22,7 +22,7 @@ from typing import ( import aqt import aqt.forms -from anki.cards import Card +from anki.cards import Card, CardID from anki.collection import BrowserRow, Collection, Config, OpChanges, SearchNode from anki.consts import * from anki.errors import NotFoundError @@ -97,7 +97,7 @@ class SearchContext: browser: Browser order: Union[bool, str] = True # if set, provided card ids will be used instead of the regular search - card_ids: Optional[Sequence[int]] = None + card_ids: Optional[Sequence[CardID]] = None # Data model @@ -170,13 +170,13 @@ class DataModel(QAbstractTableModel): self.activeCols: List[str] = self.col.get_config( "activeCols", ["noteFld", "template", "cardDue", "deck"] ) - self.cards: Sequence[int] = [] + self.cards: Sequence[CardID] = [] self._rows: Dict[int, CellRow] = {} self._last_refresh = 0.0 # serve stale content to avoid hitting the DB? self.block_updates = False - def get_id(self, index: QModelIndex) -> int: + def get_id(self, index: QModelIndex) -> CardID: return self.cards[index.row()] def get_cell(self, index: QModelIndex) -> Cell: @@ -198,7 +198,7 @@ class DataModel(QAbstractTableModel): self._rows[cid] = self._fetch_row_from_backend(cid) return self._rows[cid] - def _fetch_row_from_backend(self, cid: int) -> CellRow: + def _fetch_row_from_backend(self, cid: CardID) -> CellRow: try: row = CellRow(*self.col.browser_row_for_card(cid)) except NotFoundError: @@ -1049,7 +1049,7 @@ QTableView {{ gridline-color: {grid} }} # Menu helpers ###################################################################### - def selected_cards(self) -> List[int]: + def selected_cards(self) -> List[CardID]: return [ self.model.cards[idx.row()] for idx in self.form.tableView.selectionModel().selectedRows() @@ -1068,7 +1068,7 @@ where id in %s""" ) ) - def selectedNotesAsCards(self) -> List[int]: + def selectedNotesAsCards(self) -> List[CardID]: return self.col.db.list( "select id from cards where nid in (%s)" % ",".join([str(s) for s in self.selected_notes()]) @@ -1590,7 +1590,7 @@ where id in %s""" def onCardList(self) -> None: self.form.tableView.setFocus() - def focusCid(self, cid: int) -> None: + def focusCid(self, cid: CardID) -> None: try: row = list(self.model.cards).index(cid) except ValueError: diff --git a/qt/aqt/card_ops.py b/qt/aqt/card_ops.py index d7553527a..a4c22e6f5 100644 --- a/qt/aqt/card_ops.py +++ b/qt/aqt/card_ops.py @@ -5,12 +5,14 @@ from __future__ import annotations from typing import Sequence +from anki.cards import CardID +from anki.decks import DeckID from aqt import AnkiQt -def set_card_deck(*, mw: AnkiQt, card_ids: Sequence[int], deck_id: int) -> None: +def set_card_deck(*, mw: AnkiQt, card_ids: Sequence[CardID], deck_id: DeckID) -> None: mw.perform_op(lambda: mw.col.set_deck(card_ids, deck_id)) -def set_card_flag(*, mw: AnkiQt, card_ids: Sequence[int], flag: int) -> None: +def set_card_flag(*, mw: AnkiQt, card_ids: Sequence[CardID], flag: int) -> None: mw.perform_op(lambda: mw.col.set_user_flag_for_cards(flag, card_ids)) diff --git a/qt/aqt/emptycards.py b/qt/aqt/emptycards.py index eb8099cbf..9f8ff67f8 100644 --- a/qt/aqt/emptycards.py +++ b/qt/aqt/emptycards.py @@ -5,9 +5,10 @@ from __future__ import annotations import re from concurrent.futures import Future -from typing import Any +from typing import Any, List import aqt +from anki.cards import CardID from anki.collection import EmptyCardsReport from aqt import gui_hooks from aqt.qt import QDialog, QDialogButtonBox, qconnect @@ -88,14 +89,14 @@ class EmptyCardsDialog(QDialog): self.mw.taskman.run_in_background(delete, on_done) def _delete_cards(self, keep_notes: bool) -> int: - to_delete = [] + to_delete: List[CardID] = [] note: EmptyCardsReport.NoteWithEmptyCards for note in self.report.notes: if keep_notes and note.will_delete_note: # leave first card - to_delete.extend(note.card_ids[1:]) + to_delete.extend([CardID(id) for id in note.card_ids[1:]]) else: - to_delete.extend(note.card_ids) + to_delete.extend([CardID(id) for id in note.card_ids]) self.mw.col.remove_cards_and_orphaned_notes(to_delete) return len(to_delete) diff --git a/qt/aqt/exporting.py b/qt/aqt/exporting.py index e1990f789..bb5ec145c 100644 --- a/qt/aqt/exporting.py +++ b/qt/aqt/exporting.py @@ -11,6 +11,7 @@ from typing import List, Optional import aqt from anki import hooks +from anki.cards import CardID from anki.exporting import Exporter, exporters from aqt.qt import * from aqt.utils import ( @@ -29,7 +30,7 @@ class ExportDialog(QDialog): self, mw: aqt.main.AnkiQt, did: Optional[int] = None, - cids: Optional[List[int]] = None, + cids: Optional[List[CardID]] = None, ): QDialog.__init__(self, mw, Qt.Window) self.mw = mw diff --git a/qt/aqt/reviewer.py b/qt/aqt/reviewer.py index 169b4d216..e36062c0a 100644 --- a/qt/aqt/reviewer.py +++ b/qt/aqt/reviewer.py @@ -13,7 +13,7 @@ from typing import Any, Callable, List, Match, Optional, Sequence, Tuple, Union from PyQt5.QtCore import Qt from anki import hooks -from anki.cards import Card +from anki.cards import Card, CardID from anki.collection import Config, OpChanges from anki.tags import MARKED_TAG from anki.utils import stripHTML @@ -74,7 +74,7 @@ class Reviewer: self.card: Optional[Card] = None self.cardQueue: List[Card] = [] self.hadCardQueue = False - self._answeredIds: List[int] = [] + self._answeredIds: List[CardID] = [] self._recordedAudio: Optional[str] = None self.typeCorrect: str = None # web init happens before this is set self.state: Optional[str] = None diff --git a/qt/aqt/scheduling_ops.py b/qt/aqt/scheduling_ops.py index 43c594c30..c0c345d2a 100644 --- a/qt/aqt/scheduling_ops.py +++ b/qt/aqt/scheduling_ops.py @@ -6,6 +6,7 @@ from __future__ import annotations from typing import List, Optional, Sequence import aqt +from anki.cards import CardID from anki.collection import CARD_TYPE_NEW, Config from anki.decks import DeckID from anki.lang import TR @@ -21,7 +22,7 @@ def set_due_date_dialog( *, mw: aqt.AnkiQt, parent: QWidget, - card_ids: List[int], + card_ids: List[CardID], config_key: Optional[Config.String.Key.V], ) -> None: if not card_ids: @@ -54,7 +55,7 @@ def set_due_date_dialog( ) -def forget_cards(*, mw: aqt.AnkiQt, parent: QWidget, card_ids: List[int]) -> None: +def forget_cards(*, mw: aqt.AnkiQt, parent: QWidget, card_ids: List[CardID]) -> None: if not card_ids: return @@ -67,7 +68,7 @@ def forget_cards(*, mw: aqt.AnkiQt, parent: QWidget, card_ids: List[int]) -> Non def reposition_new_cards_dialog( - *, mw: AnkiQt, parent: QWidget, card_ids: Sequence[int] + *, mw: AnkiQt, parent: QWidget, card_ids: Sequence[CardID] ) -> None: assert mw.col.db row = mw.col.db.first( @@ -112,7 +113,7 @@ def reposition_new_cards( *, mw: AnkiQt, parent: QWidget, - card_ids: Sequence[int], + card_ids: Sequence[CardID], starting_from: int, step_size: int, randomize: bool, @@ -135,7 +136,7 @@ def reposition_new_cards( def suspend_cards( *, mw: AnkiQt, - card_ids: Sequence[int], + card_ids: Sequence[CardID], success: PerformOpOptionalSuccessCallback = None, ) -> None: mw.perform_op(lambda: mw.col.sched.suspend_cards(card_ids), success=success) @@ -153,14 +154,14 @@ def suspend_note( ) -def unsuspend_cards(*, mw: AnkiQt, card_ids: Sequence[int]) -> None: +def unsuspend_cards(*, mw: AnkiQt, card_ids: Sequence[CardID]) -> None: mw.perform_op(lambda: mw.col.sched.unsuspend_cards(card_ids)) def bury_cards( *, mw: AnkiQt, - card_ids: Sequence[int], + card_ids: Sequence[CardID], success: PerformOpOptionalSuccessCallback = None, ) -> None: mw.perform_op(lambda: mw.col.sched.bury_cards(card_ids), success=success)