From a517accee358a495a31336c68b6596c9b897fb9d Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Mon, 31 Aug 2020 13:29:28 +1000 Subject: [PATCH] update to latest black --- pylib/anki/cards.py | 8 +- pylib/anki/decks.py | 7 +- pylib/anki/hooks.py | 10 +- pylib/anki/models.py | 10 +- pylib/anki/rsbackend.py | 4 +- pylib/anki/schedv2.py | 16 +- pylib/anki/stats.py | 8 +- pylib/requirements.dev | 2 +- pylib/tools/genhooks.py | 8 +- qt/aqt/__init__.py | 10 +- qt/aqt/addons.py | 10 +- qt/aqt/clayout.py | 21 ++- qt/aqt/deckbrowser.py | 5 +- qt/aqt/editor.py | 4 +- qt/aqt/exporting.py | 15 +- qt/aqt/gui_hooks.py | 396 +++++++++++++++++++-------------------- qt/aqt/mediasrv.py | 15 +- qt/aqt/mpv.py | 111 +++++------ qt/aqt/previewer.py | 5 +- qt/aqt/profiles.py | 4 +- qt/aqt/sync.py | 3 +- qt/aqt/toolbar.py | 6 +- qt/aqt/webview.py | 14 +- qt/tools/genhooks_gui.py | 23 ++- 24 files changed, 395 insertions(+), 320 deletions(-) diff --git a/pylib/anki/cards.py b/pylib/anki/cards.py index 4f185857d..3f1bf212f 100644 --- a/pylib/anki/cards.py +++ b/pylib/anki/cards.py @@ -129,9 +129,11 @@ class Card: self, reload: bool = False, browser: bool = False ) -> anki.template.TemplateRenderOutput: if not self._render_output or reload: - self._render_output = anki.template.TemplateRenderContext.from_existing_card( - self, browser - ).render() + self._render_output = ( + anki.template.TemplateRenderContext.from_existing_card( + self, browser + ).render() + ) return self._render_output def set_render_output(self, output: anki.template.TemplateRenderOutput) -> None: diff --git a/pylib/anki/decks.py b/pylib/anki/decks.py index bdcdfde33..2da3cbe9c 100644 --- a/pylib/anki/decks.py +++ b/pylib/anki/decks.py @@ -104,7 +104,12 @@ class DeckManager: # Deck save/load ############################################################# - def id(self, name: str, create: bool = True, type: int = 0,) -> Optional[int]: + def id( + self, + name: str, + create: bool = True, + type: int = 0, + ) -> Optional[int]: "Add a deck with NAME. Reuse deck if already exists. Return id as int." id = self.id_for_name(name) if id: diff --git a/pylib/anki/hooks.py b/pylib/anki/hooks.py index 27977dc52..adca62f7a 100644 --- a/pylib/anki/hooks.py +++ b/pylib/anki/hooks.py @@ -232,9 +232,9 @@ exporters_list_created = _ExportersListCreatedHook() class _FieldFilterFilter: """Allows you to define custom {{filters:..}} - - Your add-on can check filter_name to decide whether it should modify - field_text or not before returning it.""" + + Your add-on can check filter_name to decide whether it should modify + field_text or not before returning it.""" _hooks: List[ Callable[[str, str, str, "anki.template.TemplateRenderContext"], str] @@ -395,7 +395,7 @@ notes_will_be_deleted = _NotesWillBeDeletedHook() class _SchedulerNewLimitForSingleDeckFilter: """Allows changing the number of new card for this deck (without - considering descendants).""" + considering descendants).""" _hooks: List[Callable[[int, "anki.decks.Deck"], int]] = [] @@ -426,7 +426,7 @@ scheduler_new_limit_for_single_deck = _SchedulerNewLimitForSingleDeckFilter() class _SchedulerReviewLimitForSingleDeckFilter: """Allows changing the number of rev card for this deck (without - considering descendants).""" + considering descendants).""" _hooks: List[Callable[[int, "anki.decks.Deck"], int]] = [] diff --git a/pylib/anki/models.py b/pylib/anki/models.py index ab1a11988..746555663 100644 --- a/pylib/anki/models.py +++ b/pylib/anki/models.py @@ -437,7 +437,15 @@ and notes.mid = ? and cards.ord = ?""", for c in range(nfields): flds.append(newflds.get(c, "")) flds = joinFields(flds) - d.append((flds, newModel["id"], intTime(), self.col.usn(), nid,)) + d.append( + ( + flds, + newModel["id"], + intTime(), + self.col.usn(), + nid, + ) + ) self.col.db.executemany( "update notes set flds=?,mid=?,mod=?,usn=? where id = ?", d ) diff --git a/pylib/anki/rsbackend.py b/pylib/anki/rsbackend.py index 047571486..58ecb9669 100644 --- a/pylib/anki/rsbackend.py +++ b/pylib/anki/rsbackend.py @@ -209,7 +209,9 @@ class RustBackend(RustBackendGenerated): langs = [anki.lang.currentLang] init_msg = pb.BackendInit( - locale_folder_path=ftl_folder, preferred_langs=langs, server=server, + locale_folder_path=ftl_folder, + preferred_langs=langs, + server=server, ) self._backend = ankirspy.open_backend(init_msg.SerializeToString()) diff --git a/pylib/anki/schedv2.py b/pylib/anki/schedv2.py index 053728151..162efbb24 100644 --- a/pylib/anki/schedv2.py +++ b/pylib/anki/schedv2.py @@ -668,7 +668,10 @@ did = ? and queue = {QUEUE_TYPE_DAY_LEARN_RELEARN} and due <= ? limit ?""", return tot + tod * 1000 def _leftToday( - self, delays: List[int], left: int, now: Optional[int] = None, + self, + delays: List[int], + left: int, + now: Optional[int] = None, ) -> int: "The number of steps that can be completed by the day cutoff." if not now: @@ -1603,7 +1606,16 @@ and (queue={QUEUE_TYPE_NEW} or (queue={QUEUE_TYPE_REV} and due<=?))""", mod = intTime() for id in ids: r = random.randint(imin, imax) - d.append((max(1, r), r + t, self.col.usn(), mod, STARTING_FACTOR, id,)) + d.append( + ( + max(1, r), + r + t, + self.col.usn(), + mod, + STARTING_FACTOR, + id, + ) + ) self.remFromDyn(ids) self.col.db.executemany( f""" diff --git a/pylib/anki/stats.py b/pylib/anki/stats.py index 3b2e2981a..abfdd0099 100644 --- a/pylib/anki/stats.py +++ b/pylib/anki/stats.py @@ -243,7 +243,9 @@ from revlog where id > ? """ def _dueInfo(self, tot: int, num: int) -> str: i: List[str] = [] self._line( - i, _("Total"), self.col.tr(TR.STATISTICS_REVIEWS, reviews=tot), + i, + _("Total"), + self.col.tr(TR.STATISTICS_REVIEWS, reviews=tot), ) self._line(i, _("Average"), self._avgDay(tot, num, _("reviews"))) tomorrow = self.col.db.scalar( @@ -436,7 +438,9 @@ group by day order by day""" return self._lineTbl(i), int(tot) def _splitRepData( - self, data: List[Tuple[Any, ...]], spec: Sequence[Tuple[int, str, str]], + self, + data: List[Tuple[Any, ...]], + spec: Sequence[Tuple[int, str, str]], ) -> Tuple[List[Dict[str, Any]], List[Tuple[Any, Any]]]: sep: Dict[int, Any] = {} totcnt = {} diff --git a/pylib/requirements.dev b/pylib/requirements.dev index 1005941f4..abb50d581 100644 --- a/pylib/requirements.dev +++ b/pylib/requirements.dev @@ -1,6 +1,6 @@ wheel mypy mypy_protobuf>=1.21 -black +black>=20.1b0 pytest>=6.0.1 stringcase==1.2.0 diff --git a/pylib/tools/genhooks.py b/pylib/tools/genhooks.py index 825880dd4..97f783516 100644 --- a/pylib/tools/genhooks.py +++ b/pylib/tools/genhooks.py @@ -93,10 +93,14 @@ hooks = [ doc="Obsolete, do not use.", ), Hook( - name="sync_stage_did_change", args=["stage: str"], doc="Obsolete, do not use.", + name="sync_stage_did_change", + args=["stage: str"], + doc="Obsolete, do not use.", ), Hook( - name="sync_progress_did_change", args=["msg: str"], doc="Obsolete, do not use.", + name="sync_progress_did_change", + args=["msg: str"], + doc="Obsolete, do not use.", ), ] diff --git a/qt/aqt/__init__.py b/qt/aqt/__init__.py index 9727cfcbf..ca8d833fb 100644 --- a/qt/aqt/__init__.py +++ b/qt/aqt/__init__.py @@ -144,22 +144,22 @@ class DialogManager: """Allows add-ons to register a custom dialog to be managed by Anki's dialog manager, which ensures that only one copy of the window is open at once, and that the dialog cleans up asynchronously when the collection closes - + Please note that dialogs added in this manner need to define a close behavior by either: - + - setting `dialog.silentlyClose = True` to have it close immediately - define a `dialog.closeWithCallback()` method that is called when closed by the dialog manager - + TODO?: Implement more restrictive type check to ensure these requirements are met - + Arguments: name {str} -- Name/identifier of the dialog in question creator {Union[Callable, type]} -- A class or function to create new dialog instances with - + Keyword Arguments: instance {Optional[Any]} -- An optional existing instance of the dialog (default: {None}) diff --git a/qt/aqt/addons.py b/qt/aqt/addons.py index 9f8bfc4e9..c36b1d8f4 100644 --- a/qt/aqt/addons.py +++ b/qt/aqt/addons.py @@ -760,7 +760,9 @@ class AddonsDialog(QDialog): def should_grey(self, addon: AddonMeta) -> bool: return not addon.enabled or not addon.compatible() - def redrawAddons(self,) -> None: + def redrawAddons( + self, + ) -> None: addonList = self.form.addonList mgr = self.mgr @@ -1321,7 +1323,11 @@ class ConfigEditor(QDialog): def updateText(self, conf: Dict[str, Any]) -> None: text = json.dumps( - conf, ensure_ascii=False, sort_keys=True, indent=4, separators=(",", ": "), + conf, + ensure_ascii=False, + sort_keys=True, + indent=4, + separators=(",", ": "), ) text = gui_hooks.addon_config_editor_will_display_json(text) self.form.editor.setPlainText(text) diff --git a/qt/aqt/clayout.py b/qt/aqt/clayout.py index 2398065bb..a89f9d365 100644 --- a/qt/aqt/clayout.py +++ b/qt/aqt/clayout.py @@ -166,13 +166,19 @@ class CardLayout(QDialog): self.tform.back_button.setToolTip(shortcut("Ctrl+2")) self.tform.style_button.setToolTip(shortcut("Ctrl+3")) QShortcut( # type: ignore - QKeySequence("Ctrl+1"), self, activated=self.tform.front_button.click, + QKeySequence("Ctrl+1"), + self, + activated=self.tform.front_button.click, ) QShortcut( # type: ignore - QKeySequence("Ctrl+2"), self, activated=self.tform.back_button.click, + QKeySequence("Ctrl+2"), + self, + activated=self.tform.back_button.click, ) QShortcut( # type: ignore - QKeySequence("Ctrl+3"), self, activated=self.tform.style_button.click, + QKeySequence("Ctrl+3"), + self, + activated=self.tform.style_button.click, ) # Main area setup @@ -303,7 +309,10 @@ class CardLayout(QDialog): "reviewer.js", ] self.preview_web.stdHtml( - self.mw.reviewer.revHtml(), css=["reviewer.css"], js=jsinc, context=self, + self.mw.reviewer.revHtml(), + css=["reviewer.css"], + js=jsinc, + context=self, ) self.preview_web.set_bridge_command(self._on_bridge_cmd, self) @@ -764,7 +773,9 @@ Enter deck to place new %s cards in, or leave blank:""" row = form.fields.currentIndex().row() if row >= 0: self._addField( - fields[row], form.font.currentFont().family(), form.size.value(), + fields[row], + form.font.currentFont().family(), + form.size.value(), ) def _addField(self, field, font, size): diff --git a/qt/aqt/deckbrowser.py b/qt/aqt/deckbrowser.py index fa0509f25..b08eff116 100644 --- a/qt/aqt/deckbrowser.py +++ b/qt/aqt/deckbrowser.py @@ -28,7 +28,7 @@ class DeckBrowserBottomBar: class DeckBrowserContent: """Stores sections of HTML content that the deck browser will be populated with. - + Attributes: tree {str} -- HTML of the deck tree section stats {str} -- HTML of the stats section @@ -118,7 +118,8 @@ class DeckBrowser: def __renderPage(self, offset): content = DeckBrowserContent( - tree=self._renderDeckTree(self._dueTree), stats=self._renderStats(), + tree=self._renderDeckTree(self._dueTree), + stats=self._renderStats(), ) gui_hooks.deck_browser_will_render_content(self, content) self.web.stdHtml( diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index 306fa3095..2a980d037 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -233,7 +233,9 @@ class Editor: self._links[cmd] = func if keys: QShortcut( # type: ignore - QKeySequence(keys), self.widget, activated=lambda s=self: func(s), + QKeySequence(keys), + self.widget, + activated=lambda s=self: func(s), ) btn = self._addButton( icon, diff --git a/qt/aqt/exporting.py b/qt/aqt/exporting.py index 486706820..fc27b2d68 100644 --- a/qt/aqt/exporting.py +++ b/qt/aqt/exporting.py @@ -120,7 +120,12 @@ class ExportDialog(QDialog): key_str = self.exporter.key while 1: file = getSaveFile( - self, _("Export"), "export", key_str, self.exporter.ext, fname=filename, + self, + _("Export"), + "export", + key_str, + self.exporter.ext, + fname=filename, ) if not file: return @@ -175,14 +180,18 @@ class ExportDialog(QDialog): if self.isTextNote: msg = ( ngettext( - "%d note exported.", "%d notes exported.", self.exporter.count, + "%d note exported.", + "%d notes exported.", + self.exporter.count, ) % self.exporter.count ) else: msg = ( ngettext( - "%d card exported.", "%d cards exported.", self.exporter.count, + "%d card exported.", + "%d cards exported.", + self.exporter.count, ) % self.exporter.count ) diff --git a/qt/aqt/gui_hooks.py b/qt/aqt/gui_hooks.py index 980742c29..fd4b93f4b 100644 --- a/qt/aqt/gui_hooks.py +++ b/qt/aqt/gui_hooks.py @@ -84,12 +84,12 @@ add_cards_did_init = _AddCardsDidInitHook() class _AddCardsWillAddNoteFilter: """Decides whether the note should be added to the collection or - not. It is assumed to come from the addCards window. + not. It is assumed to come from the addCards window. - reason_to_already_reject is the first reason to reject that - was found, or None. If your filter wants to reject, it should - replace return the reason to reject. Otherwise return the - input.""" + reason_to_already_reject is the first reason to reject that + was found, or None. If your filter wants to reject, it should + replace return the reason to reject. Otherwise return the + input.""" _hooks: List[Callable[[Optional[str], "anki.notes.Note"], Optional[str]]] = [] @@ -183,10 +183,10 @@ addcards_will_add_history_entry = _AddcardsWillAddHistoryEntryFilter() class _AddonConfigEditorWillDisplayJsonFilter: """Allows changing the text of the json configuration before actually - displaying it to the user. For example, you can replace "\n" by - some actual new line. Then you can replace the new lines by "\n" - while reading the file and let the user uses real new line in - string instead of its encoding.""" + displaying it to the user. For example, you can replace "\n" by + some actual new line. Then you can replace the new lines by "\n" + while reading the file and let the user uses real new line in + string instead of its encoding.""" _hooks: List[Callable[[str], str]] = [] @@ -217,8 +217,8 @@ addon_config_editor_will_display_json = _AddonConfigEditorWillDisplayJsonFilter( class _AddonConfigEditorWillSaveJsonFilter: """Allows changing the text of the json configuration that was - received from the user before actually reading it. For - example, you can replace new line in strings by some "\n".""" + received from the user before actually reading it. For + example, you can replace new line in strings by some "\n".""" _hooks: List[Callable[[str], str]] = [] @@ -286,7 +286,7 @@ addons_dialog_did_change_selected_addon = _AddonsDialogDidChangeSelectedAddonHoo class _AddonsDialogWillShowHook: """Allows changing the add-on dialog before it is shown. E.g. add - buttons.""" + buttons.""" _hooks: List[Callable[["aqt.addons.AddonsDialog"], None]] = [] @@ -542,40 +542,40 @@ browser_menus_did_init = _BrowserMenusDidInitHook() class _BrowserWillBuildTreeFilter: """Used to add or replace items in the browser sidebar tree - - 'tree' is the root SidebarItem that all other items are added to. - - 'stage' is an enum describing the different construction stages of - the sidebar tree at which you can interject your changes. - The different values can be inspected by looking at - aqt.browser.SidebarStage. - - If you want Anki to proceed with the construction of the tree stage - in question after your have performed your changes or additions, - return the 'handled' boolean unchanged. - - On the other hand, if you want to prevent Anki from adding its own - items at a particular construction stage (e.g. in case your add-on - implements its own version of that particular stage), return 'True'. - - If you return 'True' at SidebarStage.ROOT, the sidebar will not be - populated by any of the other construction stages. For any other stage - the tree construction will just continue as usual. - - For example, if your code wishes to replace the tag tree, you could do: - - def on_browser_will_build_tree(handled, root, stage, browser): - if stage != SidebarStage.TAGS: - # not at tag tree building stage, pass on - return handled - - # your tag tree construction code - # root.addChild(...) - - # your code handled tag tree construction, no need for Anki - # or other add-ons to build the tag tree - return True - """ + + 'tree' is the root SidebarItem that all other items are added to. + + 'stage' is an enum describing the different construction stages of + the sidebar tree at which you can interject your changes. + The different values can be inspected by looking at + aqt.browser.SidebarStage. + + If you want Anki to proceed with the construction of the tree stage + in question after your have performed your changes or additions, + return the 'handled' boolean unchanged. + + On the other hand, if you want to prevent Anki from adding its own + items at a particular construction stage (e.g. in case your add-on + implements its own version of that particular stage), return 'True'. + + If you return 'True' at SidebarStage.ROOT, the sidebar will not be + populated by any of the other construction stages. For any other stage + the tree construction will just continue as usual. + + For example, if your code wishes to replace the tag tree, you could do: + + def on_browser_will_build_tree(handled, root, stage, browser): + if stage != SidebarStage.TAGS: + # not at tag tree building stage, pass on + return handled + + # your tag tree construction code + # root.addChild(...) + + # your code handled tag tree construction, no need for Anki + # or other add-ons to build the tag tree + return True + """ _hooks: List[ Callable[ @@ -644,16 +644,16 @@ browser_will_build_tree = _BrowserWillBuildTreeFilter() class _BrowserWillSearchHook: """Allows you to modify the search text, or perform your own search. - - You can modify context.search to change the text that is sent to the - searching backend. - - If you set context.card_ids to a list of ids, the regular search will - not be performed, and the provided ids will be used instead. - - Your add-on should check if context.card_ids is not None, and return - without making changes if it has been set. - """ + + You can modify context.search to change the text that is sent to the + searching backend. + + If you set context.card_ids to a list of ids, the regular search will + not be performed, and the provided ids will be used instead. + + Your add-on should check if context.card_ids is not None, and return + without making changes if it has been set. + """ _hooks: List[Callable[["aqt.browser.SearchContext"], None]] = [] @@ -739,7 +739,7 @@ browser_will_show_context_menu = _BrowserWillShowContextMenuHook() class _CardLayoutWillShowHook: """Allow to change the display of the card layout. After most values are - set and before the window is actually shown.""" + set and before the window is actually shown.""" _hooks: List[Callable[["aqt.clayout.CardLayout"], None]] = [] @@ -859,7 +859,7 @@ current_note_type_did_change = _CurrentNoteTypeDidChangeHook() class _DebugConsoleDidEvaluatePythonFilter: """Allows processing the debug result. E.g. logging queries and - result, saving last query to display it later...""" + result, saving last query to display it later...""" _hooks: List[Callable[[str, str, QDialog], str]] = [] @@ -890,7 +890,7 @@ debug_console_did_evaluate_python = _DebugConsoleDidEvaluatePythonFilter() class _DebugConsoleWillShowHook: """Allows editing the debug window. E.g. setting a default code, or - previous code.""" + previous code.""" _hooks: List[Callable[[QDialog], None]] = [] @@ -949,19 +949,19 @@ deck_browser_did_render = _DeckBrowserDidRenderHook() class _DeckBrowserWillRenderContentHook: """Used to modify HTML content sections in the deck browser body - - 'content' contains the sections of HTML content the deck browser body - will be updated with. - - When modifying the content of a particular section, please make sure your - changes only perform the minimum required edits to make your add-on work. - You should avoid overwriting or interfering with existing data as much - as possible, instead opting to append your own changes, e.g.: - - def on_deck_browser_will_render_content(deck_browser, content): - content.stats += " -
my html
" - """ + + 'content' contains the sections of HTML content the deck browser body + will be updated with. + + When modifying the content of a particular section, please make sure your + changes only perform the minimum required edits to make your add-on work. + You should avoid overwriting or interfering with existing data as much + as possible, instead opting to append your own changes, e.g.: + + def on_deck_browser_will_render_content(deck_browser, content): + content.stats += " +
my html
" + """ _hooks: List[ Callable[ @@ -1039,14 +1039,14 @@ deck_browser_will_show_options_menu = _DeckBrowserWillShowOptionsMenuHook() class _DeckConfDidAddConfigHook: """Allows modification of a newly created config group - This hook is called after the config group was created, but - before initializing the widget state. + This hook is called after the config group was created, but + before initializing the widget state. - `deck_conf` will point to the old config group, `new_conf_id` will - point to the newly created config group. + `deck_conf` will point to the old config group, `new_conf_id` will + point to the newly created config group. - Config groups are created as clones of the current one. - """ + Config groups are created as clones of the current one. + """ _hooks: List[ Callable[["aqt.deckconf.DeckConf", Deck, DeckConfig, str, int], None] @@ -1549,7 +1549,7 @@ editor_web_view_did_init = _EditorWebViewDidInitHook() class _EditorWillLoadNoteFilter: """Allows changing the javascript commands to load note before - executing it and do change in the QT editor.""" + executing it and do change in the QT editor.""" _hooks: List[Callable[[str, "anki.notes.Note", "aqt.editor.Editor"], str]] = [] @@ -1704,12 +1704,12 @@ empty_cards_will_show = _EmptyCardsWillShowHook() class _MainWindowDidInitHook: """Executed after the main window is fully initialized - - A sample use case for this hook would be to delay actions until Anki objects - like the profile or collection are fully initialized. In contrast to - `profile_did_open`, this hook will only fire once per Anki session and - is thus suitable for single-shot subscribers. - """ + + A sample use case for this hook would be to delay actions until Anki objects + like the profile or collection are fully initialized. In contrast to + `profile_did_open`, this hook will only fire once per Anki session and + is thus suitable for single-shot subscribers. + """ _hooks: List[Callable[[], None]] = [] @@ -1740,13 +1740,13 @@ main_window_did_init = _MainWindowDidInitHook() class _MainWindowShouldRequireResetFilter: """Executed before the main window will require a reset - This hook can be used to change the behavior of the main window, - when other dialogs, like the AddCards or Browser, require a reset - from the main window. - If you decide to use this hook, make you sure you check the reason for the reset. - Some reasons require more attention than others, and skipping important ones might - put the main window into an invalid state (e.g. display a deleted note). - """ + This hook can be used to change the behavior of the main window, + when other dialogs, like the AddCards or Browser, require a reset + from the main window. + If you decide to use this hook, make you sure you check the reason for the reset. + Some reasons require more attention than others, and skipping important ones might + put the main window into an invalid state (e.g. display a deleted note). + """ _hooks: List[ Callable[[bool, "Union[aqt.main.ResetReason, str]", Optional[Any]], bool] @@ -1871,7 +1871,7 @@ models_advanced_will_show = _ModelsAdvancedWillShowHook() class _OverviewDidRefreshHook: """Allow to update the overview window. E.g. add the deck name in the - title.""" + title.""" _hooks: List[Callable[["aqt.overview.Overview"], None]] = [] @@ -1902,18 +1902,18 @@ overview_did_refresh = _OverviewDidRefreshHook() class _OverviewWillRenderContentHook: """Used to modify HTML content sections in the overview body - 'content' contains the sections of HTML content the overview body - will be updated with. + 'content' contains the sections of HTML content the overview body + will be updated with. - When modifying the content of a particular section, please make sure your - changes only perform the minimum required edits to make your add-on work. - You should avoid overwriting or interfering with existing data as much - as possible, instead opting to append your own changes, e.g.: + When modifying the content of a particular section, please make sure your + changes only perform the minimum required edits to make your add-on work. + You should avoid overwriting or interfering with existing data as much + as possible, instead opting to append your own changes, e.g.: - def on_overview_will_render_content(overview, content): - content.table += " -
my html
" - """ + def on_overview_will_render_content(overview, content): + content.table += " +
my html
" + """ _hooks: List[ Callable[["aqt.overview.Overview", "aqt.overview.OverviewContent"], None] @@ -1953,11 +1953,11 @@ overview_will_render_content = _OverviewWillRenderContentHook() class _ProfileDidOpenHook: """Executed whenever a user profile has been opened - - Please note that this hook will also be called on profile switches, so if you - are looking to simply delay an add-on action in a single-shot manner, - `main_window_did_init` is likely the more suitable choice. - """ + + Please note that this hook will also be called on profile switches, so if you + are looking to simply delay an add-on action in a single-shot manner, + `main_window_did_init` is likely the more suitable choice. + """ _hooks: List[Callable[[], None]] = [] @@ -2132,14 +2132,14 @@ reviewer_did_show_question = _ReviewerDidShowQuestionHook() class _ReviewerWillAnswerCardFilter: """Used to modify the ease at which a card is rated or to bypass - rating the card completely. - - ease_tuple is a tuple consisting of a boolean expressing whether the reviewer - should continue with rating the card, and an integer expressing the ease at - which the card should be rated. - - If your code just needs to be notified of the card rating event, you should use - the reviewer_did_answer_card hook instead.""" + rating the card completely. + + ease_tuple is a tuple consisting of a boolean expressing whether the reviewer + should continue with rating the card, and an integer expressing the ease at + which the card should be rated. + + If your code just needs to be notified of the card rating event, you should use + the reviewer_did_answer_card hook instead.""" _hooks: List[ Callable[[Tuple[bool, int], "aqt.reviewer.Reviewer", Card], Tuple[bool, int]] @@ -2216,16 +2216,16 @@ reviewer_will_end = _ReviewerWillEndHook() class _ReviewerWillInitAnswerButtonsFilter: """Used to modify list of answer buttons - buttons_tuple is a tuple of buttons, with each button represented by a - tuple containing an int for the button's ease and a string for the - button's label. + buttons_tuple is a tuple of buttons, with each button represented by a + tuple containing an int for the button's ease and a string for the + button's label. - Return a tuple of the form ((int, str), ...), e.g.: - ((1, "Label1"), (2, "Label2"), ...) + Return a tuple of the form ((int, str), ...), e.g.: + ((1, "Label1"), (2, "Label2"), ...) - Note: import _ from anki.lang to support translation, using, e.g., - ((1, _("Label1")), ...) - """ + Note: import _ from anki.lang to support translation, using, e.g., + ((1, _("Label1")), ...) + """ _hooks: List[ Callable[ @@ -2279,15 +2279,15 @@ reviewer_will_init_answer_buttons = _ReviewerWillInitAnswerButtonsFilter() class _ReviewerWillPlayAnswerSoundsHook: """Called before showing the answer/back side. - `tags` can be used to inspect and manipulate the sounds - that will be played (if any). + `tags` can be used to inspect and manipulate the sounds + that will be played (if any). - This won't be called when the user manually plays sounds - using `Replay Audio`. + This won't be called when the user manually plays sounds + using `Replay Audio`. - Note that this hook is called even when the `Automatically play audio` - option is unchecked; This is so as to allow playing custom - sounds regardless of that option.""" + Note that this hook is called even when the `Automatically play audio` + option is unchecked; This is so as to allow playing custom + sounds regardless of that option.""" _hooks: List[Callable[[Card, "List[anki.sound.AVTag]"], None]] = [] @@ -2318,15 +2318,15 @@ reviewer_will_play_answer_sounds = _ReviewerWillPlayAnswerSoundsHook() class _ReviewerWillPlayQuestionSoundsHook: """Called before showing the question/front side. - `tags` can be used to inspect and manipulate the sounds - that will be played (if any). + `tags` can be used to inspect and manipulate the sounds + that will be played (if any). - This won't be called when the user manually plays sounds - using `Replay Audio`. + This won't be called when the user manually plays sounds + using `Replay Audio`. - Note that this hook is called even when the `Automatically play audio` - option is unchecked; This is so as to allow playing custom - sounds regardless of that option.""" + Note that this hook is called even when the `Automatically play audio` + option is unchecked; This is so as to allow playing custom + sounds regardless of that option.""" _hooks: List[Callable[[Card, "List[anki.sound.AVTag]"], None]] = [] @@ -2701,15 +2701,15 @@ tag_editor_did_process_key = _TagEditorDidProcessKeyHook() class _TopToolbarDidInitLinksHook: """Used to modify or add links in the top toolbar of Anki's main window - - 'links' is a list of HTML link elements. Add-ons can generate their own links - by using aqt.toolbar.Toolbar.create_link. Links created in that way can then be - appended to the link list, e.g.: - def on_top_toolbar_did_init_links(links, toolbar): - my_link = toolbar.create_link(...) - links.append(my_link) - """ + 'links' is a list of HTML link elements. Add-ons can generate their own links + by using aqt.toolbar.Toolbar.create_link. Links created in that way can then be + appended to the link list, e.g.: + + def on_top_toolbar_did_init_links(links, toolbar): + my_link = toolbar.create_link(...) + links.append(my_link) + """ _hooks: List[Callable[[List[str], "aqt.toolbar.Toolbar"], None]] = [] @@ -2797,35 +2797,35 @@ undo_state_did_change = _UndoStateDidChangeHook() class _WebviewDidReceiveJsMessageFilter: """Used to handle pycmd() messages sent from Javascript. - - Message is the string passed to pycmd(). - For messages you don't want to handle, return 'handled' unchanged. - - If you handle a message and don't want it passed to the original - bridge command handler, return (True, None). - - If you want to pass a value to pycmd's result callback, you can - return it with (True, some_value). - - Context is the instance that was passed to set_bridge_command(). - It can be inspected to check which screen this hook is firing - in, and to get a reference to the screen. For example, if your - code wishes to function only in the review screen, you could do: + Message is the string passed to pycmd(). - if not isinstance(context, aqt.reviewer.Reviewer): - # not reviewer, pass on message - return handled - - if message == "my-mark-action": - # our message, call onMark() on the reviewer instance - context.onMark() - # and don't pass message to other handlers - return (True, None) - else: - # some other command, pass it on - return handled - """ + For messages you don't want to handle, return 'handled' unchanged. + + If you handle a message and don't want it passed to the original + bridge command handler, return (True, None). + + If you want to pass a value to pycmd's result callback, you can + return it with (True, some_value). + + Context is the instance that was passed to set_bridge_command(). + It can be inspected to check which screen this hook is firing + in, and to get a reference to the screen. For example, if your + code wishes to function only in the review screen, you could do: + + if not isinstance(context, aqt.reviewer.Reviewer): + # not reviewer, pass on message + return handled + + if message == "my-mark-action": + # our message, call onMark() on the reviewer instance + context.onMark() + # and don't pass message to other handlers + return (True, None) + else: + # some other command, pass it on + return handled + """ _hooks: List[Callable[[Tuple[bool, Any], str, Any], Tuple[bool, Any]]] = [] @@ -2863,34 +2863,34 @@ webview_did_receive_js_message = _WebviewDidReceiveJsMessageFilter() class _WebviewWillSetContentHook: """Used to modify web content before it is rendered. - Web_content contains the HTML, JS, and CSS the web view will be - populated with. + Web_content contains the HTML, JS, and CSS the web view will be + populated with. - Context is the instance that was passed to stdHtml(). - It can be inspected to check which screen this hook is firing - in, and to get a reference to the screen. For example, if your - code wishes to function only in the review screen, you could do: + Context is the instance that was passed to stdHtml(). + It can be inspected to check which screen this hook is firing + in, and to get a reference to the screen. For example, if your + code wishes to function only in the review screen, you could do: - def on_webview_will_set_content(web_content: WebContent, context): - - if not isinstance(context, aqt.reviewer.Reviewer): - # not reviewer, do not modify content - return - - # reviewer, perform changes to content - - context: aqt.reviewer.Reviewer - - addon_package = mw.addonManager.addonFromModule(__name__) - - web_content.css.append( - f"/_addons/{addon_package}/web/my-addon.css") - web_content.js.append( - f"/_addons/{addon_package}/web/my-addon.js") + def on_webview_will_set_content(web_content: WebContent, context): - web_content.head += "" - web_content.body += "
" - """ + if not isinstance(context, aqt.reviewer.Reviewer): + # not reviewer, do not modify content + return + + # reviewer, perform changes to content + + context: aqt.reviewer.Reviewer + + addon_package = mw.addonManager.addonFromModule(__name__) + + web_content.css.append( + f"/_addons/{addon_package}/web/my-addon.css") + web_content.js.append( + f"/_addons/{addon_package}/web/my-addon.js") + + web_content.head += "" + web_content.body += "
" + """ _hooks: List[Callable[["aqt.webview.WebContent", Optional[Any]], None]] = [] diff --git a/qt/aqt/mediasrv.py b/qt/aqt/mediasrv.py index 5da2eec33..d7294f018 100644 --- a/qt/aqt/mediasrv.py +++ b/qt/aqt/mediasrv.py @@ -92,7 +92,10 @@ def allroutes(pathin): try: directory, path = _redirectWebExports(pathin) except TypeError: - return flask.make_response(f"Invalid path: {pathin}", HTTPStatus.FORBIDDEN,) + return flask.make_response( + f"Invalid path: {pathin}", + HTTPStatus.FORBIDDEN, + ) try: isdir = os.path.isdir(os.path.join(directory, path)) @@ -153,7 +156,10 @@ def allroutes(pathin): return flask.send_file(fullpath, mimetype=mimetype, conditional=True) else: print(f"Not found: {ascii(pathin)}") - return flask.make_response(f"Invalid path: {pathin}", HTTPStatus.NOT_FOUND,) + return flask.make_response( + f"Invalid path: {pathin}", + HTTPStatus.NOT_FOUND, + ) except Exception as error: if devMode: @@ -165,7 +171,10 @@ def allroutes(pathin): # swallow it - user likely surfed away from # review screen before an image had finished # downloading - return flask.make_response(str(error), HTTPStatus.INTERNAL_SERVER_ERROR,) + return flask.make_response( + str(error), + HTTPStatus.INTERNAL_SERVER_ERROR, + ) def _redirectWebExports(path): diff --git a/qt/aqt/mpv.py b/qt/aqt/mpv.py index 2c6c205c9..94556f1ad 100644 --- a/qt/aqt/mpv.py +++ b/qt/aqt/mpv.py @@ -76,7 +76,7 @@ if isWin: class MPVBase: """Base class for communication with the mpv media player via unix socket - based JSON IPC. + based JSON IPC. """ executable = find_executable("mpv") @@ -116,8 +116,7 @@ class MPVBase: # Process # def _prepare_process(self): - """Prepare the argument list for the mpv process. - """ + """Prepare the argument list for the mpv process.""" self.argv = [self.executable] self.argv += self.default_argv self.argv += ["--input-ipc-server=" + self._sock_filename] @@ -125,13 +124,11 @@ class MPVBase: self.argv += ["--wid=" + str(self.window_id)] def _start_process(self): - """Start the mpv process. - """ + """Start the mpv process.""" self._proc = subprocess.Popen(self.argv, env=self.popenEnv) def _stop_process(self): - """Stop the mpv process. - """ + """Stop the mpv process.""" if hasattr(self, "_proc"): try: self._proc.terminate() @@ -144,7 +141,7 @@ class MPVBase: # def _prepare_socket(self): """Create a random socket filename which we pass to mpv with the - --input-unix-socket option. + --input-unix-socket option. """ if isWin: self._sock_filename = "ankimpv" @@ -155,7 +152,7 @@ class MPVBase: def _start_socket(self): """Wait for the mpv process to create the unix socket and finish - startup. + startup. """ start = time.time() while self.is_running() and time.time() < start + 10: @@ -197,8 +194,7 @@ class MPVBase: raise MPVProcessError("unable to start process") def _stop_socket(self): - """Clean up the socket. - """ + """Clean up the socket.""" if hasattr(self, "_sock"): self._sock.close() if hasattr(self, "_sock_filename"): @@ -208,23 +204,20 @@ class MPVBase: pass def _prepare_thread(self): - """Set up the queues for the communication threads. - """ + """Set up the queues for the communication threads.""" self._request_queue = Queue(1) self._response_queues = {} self._event_queue = Queue() self._stop_event = threading.Event() def _start_thread(self): - """Start up the communication threads. - """ + """Start up the communication threads.""" self._thread = threading.Thread(target=self._reader) self._thread.daemon = True self._thread.start() def _stop_thread(self): - """Stop the communication threads. - """ + """Stop the communication threads.""" if hasattr(self, "_stop_event"): self._stop_event.set() if hasattr(self, "_thread"): @@ -232,7 +225,7 @@ class MPVBase: def _reader(self): """Read the incoming json messages from the unix socket that is - connected to the mpv process. Pass them on to the message handler. + connected to the mpv process. Pass them on to the message handler. """ buf = b"" while not self._stop_event.is_set(): @@ -276,22 +269,20 @@ class MPVBase: # Message handling # def _compose_message(self, message): - """Return a json representation from a message dictionary. - """ + """Return a json representation from a message dictionary.""" # XXX may be strict is too strict ;-) data = json.dumps(message) return data.encode("utf8", "strict") + b"\n" def _parse_message(self, data): - """Return a message dictionary from a json representation. - """ + """Return a message dictionary from a json representation.""" # XXX may be strict is too strict ;-) data = data.decode("utf8", "strict") return json.loads(data) def _handle_message(self, message): """Handle different types of incoming messages, i.e. responses to - commands or asynchronous events. + commands or asynchronous events. """ if "error" in message: # This message is a reply to a request. @@ -311,8 +302,8 @@ class MPVBase: def _send_message(self, message, timeout=None): """Send a message/command to the mpv process, message must be a - dictionary of the form {"command": ["arg1", "arg2", ...]}. Responses - from the mpv process must be collected using _get_response(). + dictionary of the form {"command": ["arg1", "arg2", ...]}. Responses + from the mpv process must be collected using _get_response(). """ data = self._compose_message(message) @@ -348,8 +339,8 @@ class MPVBase: def _get_response(self, timeout=None): """Collect the response message to a previous request. If there was an - error a MPVCommandError exception is raised, otherwise the command - specific data is returned. + error a MPVCommandError exception is raised, otherwise the command + specific data is returned. """ try: message = self._response_queues[self._thread_id()].get( @@ -365,8 +356,8 @@ class MPVBase: def _get_event(self, timeout=None): """Collect a single event message that has been received out-of-band - from the mpv process. If a timeout is specified and there have not - been any events during that period, None is returned. + from the mpv process. If a timeout is specified and there have not + been any events during that period, None is returned. """ try: return self._event_queue.get(block=timeout is not None, timeout=timeout) @@ -374,8 +365,7 @@ class MPVBase: return None def _send_request(self, message, timeout=None, _retry=1): - """Send a command to the mpv process and collect the result. - """ + """Send a command to the mpv process and collect the result.""" self.ensure_running() try: self._send_message(message, timeout) @@ -392,15 +382,14 @@ class MPVBase: def _register_callbacks(self): """Will be called after mpv restart to reinitialize callbacks - defined in MPV subclass + defined in MPV subclass """ # # Public API # def is_running(self): - """Return True if the mpv process is still active. - """ + """Return True if the mpv process is still active.""" return self._proc.poll() is None def ensure_running(self): @@ -417,8 +406,7 @@ class MPVBase: self._register_callbacks() def close(self): - """Shutdown the mpv process and our communication setup. - """ + """Shutdown the mpv process and our communication setup.""" if self.is_running(): self._send_request({"command": ["quit"]}, timeout=1) self._stop_process() @@ -429,22 +417,22 @@ class MPVBase: class MPV(MPVBase): """Class for communication with the mpv media player via unix socket - based JSON IPC. It adds a few usable methods and a callback API. + based JSON IPC. It adds a few usable methods and a callback API. - To automatically register methods as event callbacks, subclass this - class and define specially named methods as follows: + To automatically register methods as event callbacks, subclass this + class and define specially named methods as follows: - def on_file_loaded(self): - # This is called for every 'file-loaded' event. - ... + def on_file_loaded(self): + # This is called for every 'file-loaded' event. + ... - def on_property_time_pos(self, position): - # This is called whenever the 'time-pos' property is updated. - ... + def on_property_time_pos(self, position): + # This is called whenever the 'time-pos' property is updated. + ... - Please note that callbacks are executed inside a separate thread. The - MPV class itself is completely thread-safe. Requests from different - threads to the same MPV instance are synchronized. + Please note that callbacks are executed inside a separate thread. The + MPV class itself is completely thread-safe. Requests from different + threads to the same MPV instance are synchronized. """ def __init__(self, *args, **kwargs): @@ -498,8 +486,7 @@ class MPV(MPVBase): # Socket communication # def _start_thread(self): - """Start up the communication threads. - """ + """Start up the communication threads.""" super()._start_thread() if not hasattr(self, "_event_thread"): self._event_thread = threading.Thread(target=self._event_reader) @@ -510,8 +497,7 @@ class MPV(MPVBase): # Event/callback API # def _event_reader(self): - """Collect incoming event messages and call the event handler. - """ + """Collect incoming event messages and call the event handler.""" while True: message = self._get_event(timeout=1) if message is None: @@ -520,8 +506,7 @@ class MPV(MPVBase): self._handle_event(message) def _handle_event(self, message): - """Lookup and call the callbacks for a particular event message. - """ + """Lookup and call the callbacks for a particular event message.""" if not self._callbacks_initialized: self._callbacks_queue.put(message) return @@ -538,8 +523,7 @@ class MPV(MPVBase): callback() def register_callback(self, name, callback): - """Register a function `callback` for the event `name`. - """ + """Register a function `callback` for the event `name`.""" try: self.command("enable_event", name) except MPVCommandError: @@ -549,7 +533,7 @@ class MPV(MPVBase): def unregister_callback(self, name, callback): """Unregister a previously registered function `callback` for the event - `name`. + `name`. """ try: callbacks = self._callbacks[name] @@ -563,7 +547,7 @@ class MPV(MPVBase): def register_property_callback(self, name, callback): """Register a function `callback` for the property-change event on - property `name`. + property `name`. """ # Property changes are normally not sent over the connection unless they # are requested using the 'observe_property' command. @@ -585,7 +569,7 @@ class MPV(MPVBase): def unregister_property_callback(self, name, callback): """Unregister a previously registered function `callback` for the - property-change event on property `name`. + property-change event on property `name`. """ try: callbacks = self._callbacks["property-" + name] @@ -606,18 +590,15 @@ class MPV(MPVBase): # Public API # def command(self, *args, timeout=1): - """Execute a single command on the mpv process and return the result. - """ + """Execute a single command on the mpv process and return the result.""" return self._send_request({"command": list(args)}, timeout=timeout) def get_property(self, name): - """Return the value of property `name`. - """ + """Return the value of property `name`.""" return self.command("get_property", name) def set_property(self, name, value): - """Set the value of property `name`. - """ + """Set the value of property `name`.""" return self.command("set_property", name, value) diff --git a/qt/aqt/previewer.py b/qt/aqt/previewer.py index 26d1e6ee7..a38dc0d93 100644 --- a/qt/aqt/previewer.py +++ b/qt/aqt/previewer.py @@ -119,7 +119,10 @@ class Previewer(QDialog): "reviewer.js", ] self._web.stdHtml( - self.mw.reviewer.revHtml(), css=["reviewer.css"], js=jsinc, context=self, + self.mw.reviewer.revHtml(), + css=["reviewer.css"], + js=jsinc, + context=self, ) self._web.set_bridge_command(self._on_bridge_cmd, self) diff --git a/qt/aqt/profiles.py b/qt/aqt/profiles.py index 27e37f67c..1e5f88609 100644 --- a/qt/aqt/profiles.py +++ b/qt/aqt/profiles.py @@ -146,7 +146,9 @@ class ProfileManager: app = QtWidgets.QApplication([]) icon = QtGui.QIcon() icon.addPixmap( - QtGui.QPixmap(":/icons/anki.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off, + QtGui.QPixmap(":/icons/anki.png"), + QtGui.QIcon.Normal, + QtGui.QIcon.Off, ) window_title = "Data Folder Migration" migration_directories = f"\n\n {oldBase}\n\nto\n\n {self.base}" diff --git a/qt/aqt/sync.py b/qt/aqt/sync.py index e68e0134b..bdb5e5755 100644 --- a/qt/aqt/sync.py +++ b/qt/aqt/sync.py @@ -77,7 +77,8 @@ def on_normal_sync_timer(mw: aqt.main.AnkiQt) -> None: assert isinstance(progress.val, NormalSyncProgress) mw.progress.update( - label=f"{progress.val.added}\n{progress.val.removed}", process=False, + label=f"{progress.val.added}\n{progress.val.removed}", + process=False, ) mw.progress.set_title(progress.val.stage) diff --git a/qt/aqt/toolbar.py b/qt/aqt/toolbar.py index fc5eb5e23..b68e3f14b 100644 --- a/qt/aqt/toolbar.py +++ b/qt/aqt/toolbar.py @@ -71,18 +71,18 @@ class Toolbar: id: Optional[str] = None, ) -> str: """Generates HTML link element and registers link handler - + Arguments: cmd {str} -- Command name used for the JS → Python bridge label {str} -- Display label of the link func {Callable} -- Callable to be called on clicking the link - + Keyword Arguments: tip {Optional[str]} -- Optional tooltip text to show on hovering over the link (default: {None}) id: {Optional[str]} -- Optional id attribute to supply the link with (default: {None}) - + Returns: str -- HTML link element """ diff --git a/qt/aqt/webview.py b/qt/aqt/webview.py index 5f4400ba4..150468906 100644 --- a/qt/aqt/webview.py +++ b/qt/aqt/webview.py @@ -148,7 +148,7 @@ class WebContent: changes only perform the minimum requried edits to make your add-on work. You should avoid overwriting or interfering with existing data as much as possible, instead opting to append your own changes, e.g.: - + def on_webview_will_set_content(web_content: WebContent, context): web_content.body += "" web_content.head += "" @@ -157,28 +157,28 @@ class WebContent: media server. All list members without a specified subpath are assumed to be located under `/_anki`, which is the media server subpath used for all web assets shipped with Anki. - + Add-ons may expose their own web assets by utilizing aqt.addons.AddonManager.setWebExports(). Web exports registered in this manner may then be accessed under the `/_addons` subpath. - + E.g., to allow access to a `my-addon.js` and `my-addon.css` residing in a "web" subfolder in your add-on package, first register the corresponding web export: - + > from aqt import mw > mw.addonManager.setWebExports(__name__, r"web/.*(css|js)") - + Then append the subpaths to the corresponding web_content fields within a function subscribing to gui_hooks.webview_will_set_content: - + def on_webview_will_set_content(web_content: WebContent, context): addon_package = mw.addonManager.addonFromModule(__name__) web_content.css.append( f"/_addons/{addon_package}/web/my-addon.css") web_content.js.append( f"/_addons/{addon_package}/web/my-addon.js") - + Note that '/' will also match the os specific path separator. """ diff --git a/qt/tools/genhooks_gui.py b/qt/tools/genhooks_gui.py index ec67df847..7a0abb44f 100644 --- a/qt/tools/genhooks_gui.py +++ b/qt/tools/genhooks_gui.py @@ -412,7 +412,10 @@ hooks = [ ), Hook( name="webview_will_set_content", - args=["web_content: aqt.webview.WebContent", "context: Optional[Any]",], + args=[ + "web_content: aqt.webview.WebContent", + "context: Optional[Any]", + ], doc="""Used to modify web content before it is rendered. Web_content contains the HTML, JS, and CSS the web view will be @@ -526,7 +529,8 @@ hooks = [ doc="""Executed when the top toolbar is redrawn""", ), Hook( - name="media_sync_did_progress", args=["entry: aqt.mediasync.LogEntryWithTime"], + name="media_sync_did_progress", + args=["entry: aqt.mediasync.LogEntryWithTime"], ), Hook(name="media_sync_did_start_or_stop", args=["running: bool"]), Hook( @@ -541,7 +545,10 @@ hooks = [ args=["addcards: aqt.addcards.AddCards", "menu: QMenu"], legacy_hook="AddCards.onHistory", ), - Hook(name="add_cards_did_init", args=["addcards: aqt.addcards.AddCards"],), + Hook( + name="add_cards_did_init", + args=["addcards: aqt.addcards.AddCards"], + ), Hook( name="add_cards_did_add_note", args=["note: anki.notes.Note"], @@ -623,7 +630,10 @@ hooks = [ name="editor_web_view_did_init", args=["editor_web_view: aqt.editor.EditorWebView"], ), - Hook(name="editor_did_init", args=["editor: aqt.editor.Editor"],), + Hook( + name="editor_did_init", + args=["editor: aqt.editor.Editor"], + ), Hook( name="editor_will_load_note", args=["js: str", "note: anki.notes.Note", "editor: aqt.editor.Editor"], @@ -675,7 +685,10 @@ hooks = [ ), # Model ################### - Hook(name="models_advanced_will_show", args=["advanced: QDialog"],), + Hook( + name="models_advanced_will_show", + args=["advanced: QDialog"], + ), # Stats ################### Hook(