diff --git a/pylib/anki/_backend/__init__.py b/pylib/anki/_backend/__init__.py index d486d9ab3..dc443c853 100644 --- a/pylib/anki/_backend/__init__.py +++ b/pylib/anki/_backend/__init__.py @@ -40,7 +40,8 @@ from .fluent import GeneratedTranslations, LegacyTranslationEnum # the following comment is required to suppress a warning that only shows up # when there are other pylint failures # pylint: disable=c-extension-no-member -assert rsbridge.buildhash() == anki.buildinfo.buildhash +if rsbridge.buildhash() != anki.buildinfo.buildhash: + raise Exception("rsbridge and anki build hashes do not match") class RustBackend(RustBackendGenerated): diff --git a/pylib/anki/cards.py b/pylib/anki/cards.py index f79f94d76..aa5225f7d 100644 --- a/pylib/anki/cards.py +++ b/pylib/anki/cards.py @@ -201,7 +201,8 @@ class Card(DeprecatedNamesMixin): def set_user_flag(self, flag: int) -> None: print("use col.set_user_flag_for_cards() instead") - assert 0 <= flag <= 7 + if not 0 <= flag <= 7: + raise Exception("invalid flag") self.flags = (self.flags & ~0b111) | flag @deprecated(info="use card.render_output() directly") diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index 8d38229d5..7a9f31096 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -264,8 +264,8 @@ class Collection(DeprecatedNamesMixin): self.models._clear_cache() def reopen(self, after_full_sync: bool = False) -> None: - assert not self.db - assert self.path.endswith(".anki2") + if self.db: + raise Exception("reopen() called with open db") self._last_checkpoint_at = time.time() self._undo: _UndoInfo = None diff --git a/pylib/anki/decks.py b/pylib/anki/decks.py index cf7e08c8a..1baabbe71 100644 --- a/pylib/anki/decks.py +++ b/pylib/anki/decks.py @@ -109,7 +109,8 @@ class DeckManager(DeprecatedNamesMixin): def add_deck_legacy(self, deck: DeckDict) -> OpChangesWithId: "Add a deck created with new_deck_legacy(). Must have id of 0." - assert deck["id"] == 0 + if not deck["id"] == 0: + raise Exception("id should be 0") return self.col._backend.add_deck_legacy(to_json_bytes(deck)) def id( diff --git a/pylib/anki/importing/noteimp.py b/pylib/anki/importing/noteimp.py index 7792de96e..3c57318e3 100644 --- a/pylib/anki/importing/noteimp.py +++ b/pylib/anki/importing/noteimp.py @@ -117,7 +117,8 @@ class NoteImporter(Importer): def importNotes(self, notes: list[ForeignNote]) -> None: "Convert each card into a note, apply attributes and add to col." - assert self.mappingOk() + if not self.mappingOk(): + raise Exception("mapping not ok") # note whether tags are mapped self._tagsMapped = False for f in self.mapping: diff --git a/pylib/anki/models.py b/pylib/anki/models.py index 71c2c75af..96ca68888 100644 --- a/pylib/anki/models.py +++ b/pylib/anki/models.py @@ -304,12 +304,14 @@ class ModelManager(DeprecatedNamesMixin): def rename_field( self, notetype: NotetypeDict, field: FieldDict, new_name: str ) -> None: - assert field in notetype["flds"] + if not field in notetype["flds"]: + raise Exception("invalid field") field["name"] = new_name def set_sort_index(self, notetype: NotetypeDict, idx: int) -> None: "Modifies schema." - assert 0 <= idx < len(notetype["flds"]) + if not 0 <= idx < len(notetype["flds"]): + raise Exception("invalid sort index") notetype["sortf"] = idx # Adding & changing templates @@ -332,7 +334,8 @@ class ModelManager(DeprecatedNamesMixin): def remove_template(self, notetype: NotetypeDict, template: TemplateDict) -> None: "Modifies schema." - assert len(notetype["tmpls"]) > 1 + if not len(notetype["tmpls"]) > 1: + raise Exception("must have 1 template") notetype["tmpls"].remove(template) def reposition_template( diff --git a/pylib/anki/notes.py b/pylib/anki/notes.py index b78106475..caffedcae 100644 --- a/pylib/anki/notes.py +++ b/pylib/anki/notes.py @@ -34,7 +34,8 @@ class Note(DeprecatedNamesMixin): model: NotetypeDict | NotetypeId | None = None, id: NoteId | None = None, ) -> None: - assert not (model and id) + if model and id: + raise Exception("only model or id should be provided") notetype_id = model["id"] if isinstance(model, dict) else model self.col = col.weakref() @@ -76,7 +77,8 @@ class Note(DeprecatedNamesMixin): def flush(self) -> None: """This preserves any current checkpoint. For an undo entry, use col.update_note() instead.""" - assert self.id != 0 + if self.id == 0: + raise Exception("can't flush a new note") self.col._backend.update_notes( notes=[self._to_backend_note()], skip_undo_entry=True ) diff --git a/pylib/anki/scheduler/v2.py b/pylib/anki/scheduler/v2.py index aa330c5ab..924f235c9 100644 --- a/pylib/anki/scheduler/v2.py +++ b/pylib/anki/scheduler/v2.py @@ -449,8 +449,8 @@ limit ?""" ########################################################################## def answerCard(self, card: Card, ease: int) -> None: - assert 1 <= ease <= 4 - assert 0 <= card.queue <= 4 + if (not 1 <= ease <= 4) or (not 0 <= card.queue <= 4): + raise Exception("invalid ease or queue") self.col.save_card_review_undo_info(card) if self._burySiblingsOnAnswer: self._burySiblings(card) @@ -772,7 +772,8 @@ limit ?""" # note: when adding revlog entries in the future, make sure undo # code deletes the entries def _answerCardPreview(self, card: Card, ease: int) -> None: - assert 1 <= ease <= 2 + if not 1 <= ease <= 2: + raise Exception("invalid ease") if ease == BUTTON_ONE: # repeat after delay @@ -799,7 +800,8 @@ limit ?""" card.odid = DeckId(0) def _restorePreviewCard(self, card: Card) -> None: - assert card.odid + if not card.odid: + raise Exception("card should have odid set") card.due = card.odue @@ -965,9 +967,12 @@ limit ?""" # next interval for card when answered early+correctly def _earlyReviewIvl(self, card: Card, ease: int) -> int: - assert card.odid and card.type == CARD_TYPE_REV - assert card.factor - assert ease > 1 + if ( + not (card.odid and card.type == CARD_TYPE_REV) + or not card.factor + or not ease > 1 + ): + raise Exception("invalid input to earlyReviewIvl") elapsed = card.ivl - (card.odue - self.today) diff --git a/pylib/anki/scheduler/v3.py b/pylib/anki/scheduler/v3.py index a77704c8d..0c959f968 100644 --- a/pylib/anki/scheduler/v3.py +++ b/pylib/anki/scheduler/v3.py @@ -72,7 +72,7 @@ class Scheduler(SchedulerBaseWithLegacy): elif rating == CardAnswer.EASY: new_state = states.easy else: - assert False, "invalid rating" + raise Exception("invalid rating") return CardAnswer( card_id=card.id, @@ -157,7 +157,7 @@ class Scheduler(SchedulerBaseWithLegacy): elif ease == BUTTON_FOUR: rating = CardAnswer.EASY else: - assert False, "invalid ease" + raise Exception("invalid ease") states = self.col._backend.get_next_card_states(card.id) changes = self.answer_card( @@ -223,7 +223,7 @@ class Scheduler(SchedulerBaseWithLegacy): elif ease == BUTTON_FOUR: new_state = states.easy else: - assert False, "invalid ease" + raise Exception("invalid ease") return self._interval_for_state(new_state) diff --git a/qt/aqt/deckconf.py b/qt/aqt/deckconf.py index b4bba4e6a..5388c214e 100644 --- a/qt/aqt/deckconf.py +++ b/qt/aqt/deckconf.py @@ -267,7 +267,8 @@ class DeckConf(QDialog): continue try: i = float(item) - assert i > 0 + if not i > 0: + raise Exception("0 invalid") if i == int(i): i = int(i) ret.append(i) diff --git a/qt/aqt/filtered_deck.py b/qt/aqt/filtered_deck.py index e225cb0e7..098756348 100644 --- a/qt/aqt/filtered_deck.py +++ b/qt/aqt/filtered_deck.py @@ -333,7 +333,8 @@ class FilteredDeckConfigDialog(QDialog): continue try: i = float(item) - assert i > 0 + if not i > 0: + raise Exception("0 invalid") ret.append(i) except: # invalid, don't update diff --git a/qt/aqt/main.py b/qt/aqt/main.py index f1c50e321..63fc3f131 100644 --- a/qt/aqt/main.py +++ b/qt/aqt/main.py @@ -608,7 +608,8 @@ class AnkiQt(QMainWindow): def backup(self) -> None: "Read data into memory, and complete backup on a background thread." - assert not self.col or not self.col.db + if self.col and self.col.db: + raise Exception("collection must be closed") nbacks = self.pm.profile["numBackups"] if not nbacks or dev_mode: @@ -706,7 +707,8 @@ class AnkiQt(QMainWindow): self._background_op_count -= 1 if not self._background_op_count: gui_hooks.backend_did_block() - assert self._background_op_count >= 0 + if not self._background_op_count >= 0: + raise Exception("no background ops active") def _synthesize_op_did_execute_from_reset(self) -> None: """Fire the `operation_did_execute` hook with everything marked as changed, @@ -1357,7 +1359,8 @@ title="{}" {}>{}""".format( # this will gradually be phased out def onSchemaMod(self, arg: bool) -> bool: - assert self.inMainThread() + if not self.inMainThread(): + raise Exception("not in main thread") progress_shown = self.progress.busy() if progress_shown: self.progress.finish() diff --git a/qt/aqt/profiles.py b/qt/aqt/profiles.py index 5e596d8bf..07c86ec6e 100644 --- a/qt/aqt/profiles.py +++ b/qt/aqt/profiles.py @@ -199,7 +199,8 @@ class ProfileManager: return pickle.dumps(obj, protocol=4) def load(self, name: str) -> bool: - assert name != "_global" + if name == "_global": + raise Exception("_global is not a valid name") data = self.db.scalar( "select cast(data as blob) from profiles where name = ?", name ) @@ -381,7 +382,8 @@ class ProfileManager: # open DB file and read data try: self.db = DB(path) - assert self.db.scalar("pragma integrity_check") == "ok" + if not self.db.scalar("pragma integrity_check") == "ok": + raise Exception("corrupt db") self.db.execute( """ create table if not exists profiles diff --git a/qt/aqt/sync.py b/qt/aqt/sync.py index 3dcd3a9c2..7e8eb83ed 100644 --- a/qt/aqt/sync.py +++ b/qt/aqt/sync.py @@ -88,7 +88,8 @@ def on_normal_sync_timer(mw: aqt.main.AnkiQt) -> None: def sync_collection(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None: auth = mw.pm.sync_auth() - assert auth + if not auth: + raise Exception("expected auth") def on_timer() -> None: on_normal_sync_timer(mw) diff --git a/qt/aqt/utils.py b/qt/aqt/utils.py index 01cef2160..0d43b2065 100644 --- a/qt/aqt/utils.py +++ b/qt/aqt/utils.py @@ -431,7 +431,8 @@ def getFile( multi: bool = False, # controls whether a single or multiple files is returned ) -> Sequence[str] | str | None: "Ask the user for a file." - assert not dir or not key + if dir and key: + raise Exception("expected dir or key") if not dir: dirkey = f"{key}Directory" dir = aqt.mw.pm.profile.get(dirkey, "")