convert invariant assertions to if statements

The packaged builds of 2.1.50 use python -OO, which means our assertion
statements won't be run. This is not an issue for unit tests (as we
don't run them from a packaged build), or for type assertions (which are
added for mypy's benefit), but we do need to ensure that invariant checks
are still run.
This commit is contained in:
Damien Elmes 2021-11-25 17:47:50 +10:00
parent 88392634a8
commit 9ed13eee80
15 changed files with 53 additions and 30 deletions

View file

@ -40,7 +40,8 @@ from .fluent import GeneratedTranslations, LegacyTranslationEnum
# the following comment is required to suppress a warning that only shows up # the following comment is required to suppress a warning that only shows up
# when there are other pylint failures # when there are other pylint failures
# pylint: disable=c-extension-no-member # 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): class RustBackend(RustBackendGenerated):

View file

@ -201,7 +201,8 @@ class Card(DeprecatedNamesMixin):
def set_user_flag(self, flag: int) -> None: def set_user_flag(self, flag: int) -> None:
print("use col.set_user_flag_for_cards() instead") 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 self.flags = (self.flags & ~0b111) | flag
@deprecated(info="use card.render_output() directly") @deprecated(info="use card.render_output() directly")

View file

@ -264,8 +264,8 @@ class Collection(DeprecatedNamesMixin):
self.models._clear_cache() self.models._clear_cache()
def reopen(self, after_full_sync: bool = False) -> None: def reopen(self, after_full_sync: bool = False) -> None:
assert not self.db if self.db:
assert self.path.endswith(".anki2") raise Exception("reopen() called with open db")
self._last_checkpoint_at = time.time() self._last_checkpoint_at = time.time()
self._undo: _UndoInfo = None self._undo: _UndoInfo = None

View file

@ -109,7 +109,8 @@ class DeckManager(DeprecatedNamesMixin):
def add_deck_legacy(self, deck: DeckDict) -> OpChangesWithId: def add_deck_legacy(self, deck: DeckDict) -> OpChangesWithId:
"Add a deck created with new_deck_legacy(). Must have id of 0." "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)) return self.col._backend.add_deck_legacy(to_json_bytes(deck))
def id( def id(

View file

@ -117,7 +117,8 @@ class NoteImporter(Importer):
def importNotes(self, notes: list[ForeignNote]) -> None: def importNotes(self, notes: list[ForeignNote]) -> None:
"Convert each card into a note, apply attributes and add to col." "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 # note whether tags are mapped
self._tagsMapped = False self._tagsMapped = False
for f in self.mapping: for f in self.mapping:

View file

@ -304,12 +304,14 @@ class ModelManager(DeprecatedNamesMixin):
def rename_field( def rename_field(
self, notetype: NotetypeDict, field: FieldDict, new_name: str self, notetype: NotetypeDict, field: FieldDict, new_name: str
) -> None: ) -> None:
assert field in notetype["flds"] if not field in notetype["flds"]:
raise Exception("invalid field")
field["name"] = new_name field["name"] = new_name
def set_sort_index(self, notetype: NotetypeDict, idx: int) -> None: def set_sort_index(self, notetype: NotetypeDict, idx: int) -> None:
"Modifies schema." "Modifies schema."
assert 0 <= idx < len(notetype["flds"]) if not 0 <= idx < len(notetype["flds"]):
raise Exception("invalid sort index")
notetype["sortf"] = idx notetype["sortf"] = idx
# Adding & changing templates # Adding & changing templates
@ -332,7 +334,8 @@ class ModelManager(DeprecatedNamesMixin):
def remove_template(self, notetype: NotetypeDict, template: TemplateDict) -> None: def remove_template(self, notetype: NotetypeDict, template: TemplateDict) -> None:
"Modifies schema." "Modifies schema."
assert len(notetype["tmpls"]) > 1 if not len(notetype["tmpls"]) > 1:
raise Exception("must have 1 template")
notetype["tmpls"].remove(template) notetype["tmpls"].remove(template)
def reposition_template( def reposition_template(

View file

@ -34,7 +34,8 @@ class Note(DeprecatedNamesMixin):
model: NotetypeDict | NotetypeId | None = None, model: NotetypeDict | NotetypeId | None = None,
id: NoteId | None = None, id: NoteId | None = 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 notetype_id = model["id"] if isinstance(model, dict) else model
self.col = col.weakref() self.col = col.weakref()
@ -76,7 +77,8 @@ class Note(DeprecatedNamesMixin):
def flush(self) -> None: def flush(self) -> None:
"""This preserves any current checkpoint. """This preserves any current checkpoint.
For an undo entry, use col.update_note() instead.""" 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( self.col._backend.update_notes(
notes=[self._to_backend_note()], skip_undo_entry=True notes=[self._to_backend_note()], skip_undo_entry=True
) )

View file

@ -449,8 +449,8 @@ limit ?"""
########################################################################## ##########################################################################
def answerCard(self, card: Card, ease: int) -> None: def answerCard(self, card: Card, ease: int) -> None:
assert 1 <= ease <= 4 if (not 1 <= ease <= 4) or (not 0 <= card.queue <= 4):
assert 0 <= card.queue <= 4 raise Exception("invalid ease or queue")
self.col.save_card_review_undo_info(card) self.col.save_card_review_undo_info(card)
if self._burySiblingsOnAnswer: if self._burySiblingsOnAnswer:
self._burySiblings(card) self._burySiblings(card)
@ -772,7 +772,8 @@ limit ?"""
# note: when adding revlog entries in the future, make sure undo # note: when adding revlog entries in the future, make sure undo
# code deletes the entries # code deletes the entries
def _answerCardPreview(self, card: Card, ease: int) -> None: 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: if ease == BUTTON_ONE:
# repeat after delay # repeat after delay
@ -799,7 +800,8 @@ limit ?"""
card.odid = DeckId(0) card.odid = DeckId(0)
def _restorePreviewCard(self, card: Card) -> None: def _restorePreviewCard(self, card: Card) -> None:
assert card.odid if not card.odid:
raise Exception("card should have odid set")
card.due = card.odue card.due = card.odue
@ -965,9 +967,12 @@ limit ?"""
# next interval for card when answered early+correctly # next interval for card when answered early+correctly
def _earlyReviewIvl(self, card: Card, ease: int) -> int: def _earlyReviewIvl(self, card: Card, ease: int) -> int:
assert card.odid and card.type == CARD_TYPE_REV if (
assert card.factor not (card.odid and card.type == CARD_TYPE_REV)
assert ease > 1 or not card.factor
or not ease > 1
):
raise Exception("invalid input to earlyReviewIvl")
elapsed = card.ivl - (card.odue - self.today) elapsed = card.ivl - (card.odue - self.today)

View file

@ -72,7 +72,7 @@ class Scheduler(SchedulerBaseWithLegacy):
elif rating == CardAnswer.EASY: elif rating == CardAnswer.EASY:
new_state = states.easy new_state = states.easy
else: else:
assert False, "invalid rating" raise Exception("invalid rating")
return CardAnswer( return CardAnswer(
card_id=card.id, card_id=card.id,
@ -157,7 +157,7 @@ class Scheduler(SchedulerBaseWithLegacy):
elif ease == BUTTON_FOUR: elif ease == BUTTON_FOUR:
rating = CardAnswer.EASY rating = CardAnswer.EASY
else: else:
assert False, "invalid ease" raise Exception("invalid ease")
states = self.col._backend.get_next_card_states(card.id) states = self.col._backend.get_next_card_states(card.id)
changes = self.answer_card( changes = self.answer_card(
@ -223,7 +223,7 @@ class Scheduler(SchedulerBaseWithLegacy):
elif ease == BUTTON_FOUR: elif ease == BUTTON_FOUR:
new_state = states.easy new_state = states.easy
else: else:
assert False, "invalid ease" raise Exception("invalid ease")
return self._interval_for_state(new_state) return self._interval_for_state(new_state)

View file

@ -267,7 +267,8 @@ class DeckConf(QDialog):
continue continue
try: try:
i = float(item) i = float(item)
assert i > 0 if not i > 0:
raise Exception("0 invalid")
if i == int(i): if i == int(i):
i = int(i) i = int(i)
ret.append(i) ret.append(i)

View file

@ -333,7 +333,8 @@ class FilteredDeckConfigDialog(QDialog):
continue continue
try: try:
i = float(item) i = float(item)
assert i > 0 if not i > 0:
raise Exception("0 invalid")
ret.append(i) ret.append(i)
except: except:
# invalid, don't update # invalid, don't update

View file

@ -608,7 +608,8 @@ class AnkiQt(QMainWindow):
def backup(self) -> None: def backup(self) -> None:
"Read data into memory, and complete backup on a background thread." "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"] nbacks = self.pm.profile["numBackups"]
if not nbacks or dev_mode: if not nbacks or dev_mode:
@ -706,7 +707,8 @@ class AnkiQt(QMainWindow):
self._background_op_count -= 1 self._background_op_count -= 1
if not self._background_op_count: if not self._background_op_count:
gui_hooks.backend_did_block() 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: def _synthesize_op_did_execute_from_reset(self) -> None:
"""Fire the `operation_did_execute` hook with everything marked as changed, """Fire the `operation_did_execute` hook with everything marked as changed,
@ -1357,7 +1359,8 @@ title="{}" {}>{}</button>""".format(
# this will gradually be phased out # this will gradually be phased out
def onSchemaMod(self, arg: bool) -> bool: def onSchemaMod(self, arg: bool) -> bool:
assert self.inMainThread() if not self.inMainThread():
raise Exception("not in main thread")
progress_shown = self.progress.busy() progress_shown = self.progress.busy()
if progress_shown: if progress_shown:
self.progress.finish() self.progress.finish()

View file

@ -199,7 +199,8 @@ class ProfileManager:
return pickle.dumps(obj, protocol=4) return pickle.dumps(obj, protocol=4)
def load(self, name: str) -> bool: def load(self, name: str) -> bool:
assert name != "_global" if name == "_global":
raise Exception("_global is not a valid name")
data = self.db.scalar( data = self.db.scalar(
"select cast(data as blob) from profiles where name = ?", name "select cast(data as blob) from profiles where name = ?", name
) )
@ -381,7 +382,8 @@ class ProfileManager:
# open DB file and read data # open DB file and read data
try: try:
self.db = DB(path) 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( self.db.execute(
""" """
create table if not exists profiles create table if not exists profiles

View file

@ -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: def sync_collection(mw: aqt.main.AnkiQt, on_done: Callable[[], None]) -> None:
auth = mw.pm.sync_auth() auth = mw.pm.sync_auth()
assert auth if not auth:
raise Exception("expected auth")
def on_timer() -> None: def on_timer() -> None:
on_normal_sync_timer(mw) on_normal_sync_timer(mw)

View file

@ -431,7 +431,8 @@ def getFile(
multi: bool = False, # controls whether a single or multiple files is returned multi: bool = False, # controls whether a single or multiple files is returned
) -> Sequence[str] | str | None: ) -> Sequence[str] | str | None:
"Ask the user for a file." "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: if not dir:
dirkey = f"{key}Directory" dirkey = f"{key}Directory"
dir = aqt.mw.pm.profile.get(dirkey, "") dir = aqt.mw.pm.profile.get(dirkey, "")