Merge branch 'master' into more_precise_config_error_message

This commit is contained in:
Damien Elmes 2020-03-09 19:41:26 +10:00 committed by GitHub
commit b1d0945d10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 134 additions and 13 deletions

10
.github/scripts/trailing-newlines.sh vendored Executable file
View File

@ -0,0 +1,10 @@
#!/bin/bash
set -e
files=$(rg -l '[^\n]\z' -g '!*.{svg,scss}' || true)
if [ "$files" != "" ]; then
echo "the following files are missing a newline on the last line:"
echo $files
exit 1
fi

View File

@ -21,6 +21,7 @@ jobs:
run: | run: |
# add requirements # add requirements
sudo apt update; sudo apt install portaudio19-dev gettext rename sudo apt update; sudo apt install portaudio19-dev gettext rename
sudo snap install ripgrep --classic
export CARGO_TARGET_DIR=~/target export CARGO_TARGET_DIR=~/target
export RSPY_TARGET_DIR=~/target export RSPY_TARGET_DIR=~/target
make check build BUILDFLAGS="" make check build BUILDFLAGS=""

View File

@ -120,9 +120,10 @@ clean-dist:
.PHONY: check .PHONY: check
check: pyenv buildhash prepare check: pyenv buildhash prepare
@set -eo pipefail && \ @set -eo pipefail && \
.github/scripts/trailing-newlines.sh && \
for dir in $(CHECKABLE_RS); do \ for dir in $(CHECKABLE_RS); do \
$(SUBMAKE) -C $$dir check; \ $(SUBMAKE) -C $$dir check; \
done; \ done && \
. "${ACTIVATE_SCRIPT}" && \ . "${ACTIVATE_SCRIPT}" && \
$(SUBMAKE) -C rspy develop && \ $(SUBMAKE) -C rspy develop && \
$(SUBMAKE) -C pylib develop && \ $(SUBMAKE) -C pylib develop && \

View File

@ -22,6 +22,7 @@ To start, make sure you have the following installed:
- rename - rename
- rsync - rsync
- perl - perl
- ripgrep (cargo install rigrep)
The build scripts assume a UNIX-like environment, so on Windows you will The build scripts assume a UNIX-like environment, so on Windows you will
need to use WSL or Cygwin to use them. need to use WSL or Cygwin to use them.

View File

@ -10,7 +10,7 @@ import os
import sys import sys
import tempfile import tempfile
import traceback import traceback
from typing import Any, Optional from typing import Any, Callable, Dict, Optional, Union
import anki.buildinfo import anki.buildinfo
import anki.lang import anki.lang
@ -69,7 +69,7 @@ from aqt import stats, about, preferences, mediasync # isort:skip
class DialogManager: class DialogManager:
_dialogs = { _dialogs: Dict[str, list] = {
"AddCards": [addcards.AddCards, None], "AddCards": [addcards.AddCards, None],
"Browser": [browser.Browser, None], "Browser": [browser.Browser, None],
"EditCurrent": [editcurrent.EditCurrent, None], "EditCurrent": [editcurrent.EditCurrent, None],
@ -79,7 +79,7 @@ class DialogManager:
"sync_log": [mediasync.MediaSyncDialog, None], "sync_log": [mediasync.MediaSyncDialog, None],
} }
def open(self, name, *args): def open(self, name: str, *args: Any) -> Any:
(creator, instance) = self._dialogs[name] (creator, instance) = self._dialogs[name]
if instance: if instance:
if instance.windowState() & Qt.WindowMinimized: if instance.windowState() & Qt.WindowMinimized:
@ -94,17 +94,17 @@ class DialogManager:
self._dialogs[name][1] = instance self._dialogs[name][1] = instance
return instance return instance
def markClosed(self, name): def markClosed(self, name: str):
self._dialogs[name] = [self._dialogs[name][0], None] self._dialogs[name] = [self._dialogs[name][0], None]
def allClosed(self): def allClosed(self):
return not any(x[1] for x in self._dialogs.values()) return not any(x[1] for x in self._dialogs.values())
def closeAll(self, onsuccess): def closeAll(self, onsuccess: Callable[[], None]) -> Optional[bool]:
# can we close immediately? # can we close immediately?
if self.allClosed(): if self.allClosed():
onsuccess() onsuccess()
return return None
# ask all windows to close and await a reply # ask all windows to close and await a reply
for (name, (creator, instance)) in self._dialogs.items(): for (name, (creator, instance)) in self._dialogs.items():
@ -126,6 +126,34 @@ class DialogManager:
return True return True
def register_dialog(
self, name: str, creator: Union[Callable, type], instance: Optional[Any] = None
):
"""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})
"""
self._dialogs[name] = [creator, instance]
dialogs = DialogManager() dialogs = DialogManager()

View File

@ -13,7 +13,7 @@ import anki
import aqt import aqt
from anki.cards import Card from anki.cards import Card
from anki.hooks import runFilter, runHook from anki.hooks import runFilter, runHook
from aqt.qt import QMenu from aqt.qt import QDialog, QMenu
# New hook/filter handling # New hook/filter handling
############################################################################## ##############################################################################
@ -520,6 +520,61 @@ class _CurrentNoteTypeDidChangeHook:
current_note_type_did_change = _CurrentNoteTypeDidChangeHook() 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..."""
_hooks: List[Callable[[str, str, QDialog], str]] = []
def append(self, cb: Callable[[str, str, QDialog], str]) -> None:
"""(output: str, query: str, debug_window: QDialog)"""
self._hooks.append(cb)
def remove(self, cb: Callable[[str, str, QDialog], str]) -> None:
if cb in self._hooks:
self._hooks.remove(cb)
def __call__(self, output: str, query: str, debug_window: QDialog) -> str:
for filter in self._hooks:
try:
output = filter(output, query, debug_window)
except:
# if the hook fails, remove it
self._hooks.remove(filter)
raise
return output
debug_console_did_evaluate_python = _DebugConsoleDidEvaluatePythonFilter()
class _DebugConsoleWillShowHook:
"""Allows editing the debug window. E.g. setting a default code, or
previous code."""
_hooks: List[Callable[[QDialog], None]] = []
def append(self, cb: Callable[[QDialog], None]) -> None:
"""(debug_window: QDialog)"""
self._hooks.append(cb)
def remove(self, cb: Callable[[QDialog], None]) -> None:
if cb in self._hooks:
self._hooks.remove(cb)
def __call__(self, debug_window: QDialog) -> None:
for hook in self._hooks:
try:
hook(debug_window)
except:
# if the hook fails, remove it
self._hooks.remove(hook)
raise
debug_console_will_show = _DebugConsoleWillShowHook()
class _DeckBrowserDidRenderHook: class _DeckBrowserDidRenderHook:
"""Allow to update the deck browser window. E.g. change its title.""" """Allow to update the deck browser window. E.g. change its title."""

View File

@ -1342,6 +1342,7 @@ will be lost. Continue?"""
s.activated.connect(frm.log.clear) s.activated.connect(frm.log.clear)
s = self.debugDiagShort = QShortcut(QKeySequence("ctrl+shift+l"), d) s = self.debugDiagShort = QShortcut(QKeySequence("ctrl+shift+l"), d)
s.activated.connect(frm.text.clear) s.activated.connect(frm.text.clear)
gui_hooks.debug_console_will_show(d)
d.show() d.show()
def _captureOutput(self, on): def _captureOutput(self, on):
@ -1403,9 +1404,17 @@ will be lost. Continue?"""
else: else:
buf += "... %s\n" % line buf += "... %s\n" % line
try: try:
frm.log.appendPlainText(buf + (self._output or "<no output>")) to_append = buf + (self._output or "<no output>")
to_append = gui_hooks.debug_console_did_evaluate_python(
to_append, text, frm
)
frm.log.appendPlainText(to_append)
except UnicodeDecodeError: except UnicodeDecodeError:
frm.log.appendPlainText(_("<non-unicode text>")) to_append = _("<non-unicode text>")
to_append = gui_hooks.debug_console_did_evaluate_python(
to_append, text, frm
)
frm.log.appendPlainText(to_append)
frm.log.ensureCursorVisible() frm.log.ensureCursorVisible()
# System specific code # System specific code

View File

@ -11,6 +11,7 @@ import sys
pylib = os.path.join(os.path.dirname(__file__), "..", "..", "pylib") pylib = os.path.join(os.path.dirname(__file__), "..", "..", "pylib")
sys.path.append(pylib) sys.path.append(pylib)
from tools.hookslib import Hook, update_file from tools.hookslib import Hook, update_file
# Hook list # Hook list
@ -89,6 +90,21 @@ hooks = [
legacy_hook="reviewCleanup", legacy_hook="reviewCleanup",
doc="Called before Anki transitions from the review screen to another screen.", doc="Called before Anki transitions from the review screen to another screen.",
), ),
# Debug
###################
Hook(
name="debug_console_will_show",
args=["debug_window: QDialog"],
doc="""Allows editing the debug window. E.g. setting a default code, or
previous code.""",
),
Hook(
name="debug_console_did_evaluate_python",
args=["output: str", "query: str", "debug_window: QDialog"],
return_type="str",
doc="""Allows processing the debug result. E.g. logging queries and
result, saving last query to display it later...""",
),
# Card layout # Card layout
################### ###################
Hook( Hook(