Version checks
Add-ons that were manually deconstructing Anki’s version string and making assumptions about the number of components it has should instead switch to
Anki’s built-in tools for getting the version number.
For example:
from anki import buildinfo
if int(buildinfo.version.split(".")[2]) < 45: # < 2.1.45
…should become:
from anki.utils import pointVersion
if pointVersion() < 45: ...
For Anki 2.1.x releases, it will return the x.
For Anki 23.10+, it packs the version into 6 digits for YY-MM-patch, so if you wished to check if the user was on 23.10+, you’d do:
if pointVersion() >= 231000:
And if you wanted to check 23.10.2, it would be 231002.
pointVersion()
has been around since 2.1.20, and will be kept around in the future despite being camel case. If you don’t need to support older Anki versions:
point_version()
has been around since 2.1.49.- 23.10 introduces a clearer-named
int_version()
alias.
QueryOp is serialized
QueryOp()s are now serialized by default, as only one operation should be accessing the collection at once. If you use QueryOp() for non-collection tasks like network requests and want multiple ops to run in parallel, you can opt-out of this with .without_collection()
save(), checkpoint() and flush() are deprecated
Old Anki versions held a long-running transaction, and would commit it every
5 minutes or when col.checkpoint() was called, which offered a crude 1-step undo,
but meant you could lose a few minutes of work if Anki crashed. Anki 2.1.45 introduced a new undo system that supports multi-step undo/redo. As of 23.10,
.checkpoint() is now a no-op.
If your add-on calls .checkpoint() before some operation, you can remove the checkpoint call. As long as you make sure to perform changes in a CollectionOp and use undoable actions, you can take advantage of the undo/redo system.
Similarly, col.save() and col.autosave() no longer do anything, as changes are now saved as they’re made.
If you currently use card.flush() or note.flush(), please transition to col.update_note() / col.update_card() in a CollectionOp, so that the user can undo
the changes.
Qt5 compatibility
The compatibility code that has been in place for the last 2 years is now off by default, as it increases startup time and can lead to some confusing edge cases.
Imports
To support both Qt5 and Qt6, you will need to change lines like:
import sip
from PyQt5.QtWidgets import QDialog
…to the recommended approach:
from aqt.qt import sip, QDialog
Enumerations
Qt6 requires enums to be prefixed with their type name. For example:
self.setWizardStyle(QWizard.ClassicStyle)
becomes
self.setWizardStyle(QWizard.WizardStyle.ClassicStyle)
and
f.setStyleHint(QFont.Monospace)
becomes
f.setStyleHint(QFont.StyleHint.Monospace)
You can find the various enum names in the docs.
Temporary workarounds
Users with add-ons that the add-on author has not updated yet can either switch to the Qt5 version of Anki, or temporarily set an environment variable ENABLE_QT5_COMPAT to 1 to have Anki install the previous compatibility code. In an early 2024 update, ENABLE_QT5_COMPAT will be removed, so this is not a long term solution.
Removal of the v1/v2 schedulers
Code that imports from anki.sched will break, as the old schedulers have been removed. Typically you don’t need to do this unless you’re monkey patching the scheduler, which won’t work with v3. If you just need to call some scheduler method, you can do that via col.sched.foo()
Check the console
Make sure you monitor the console for deprecation warnings:
https://addon-docs.ankiweb.net/console-output.html
Take advantage of mypy
A reminder that Anki’s codebase is fully typed. By investing some time setting up mypy, you can catch many of these changes at build time.