Merge remote-tracking branch 'origin/pr/3374'
This commit is contained in:
commit
8e319a762f
@ -1148,7 +1148,7 @@ Set a mark at the current scroll position in the current tab.
|
||||
|
||||
[[spawn]]
|
||||
=== spawn
|
||||
Syntax: +:spawn [*--userscript*] [*--verbose*] [*--detach*] 'cmdline'+
|
||||
Syntax: +:spawn [*--userscript*] [*--verbose*] [*--output*] [*--detach*] 'cmdline'+
|
||||
|
||||
Spawn a command in a shell.
|
||||
|
||||
@ -1163,6 +1163,7 @@ Spawn a command in a shell.
|
||||
- `/usr/share/qutebrowser/userscripts`
|
||||
|
||||
* +*-v*+, +*--verbose*+: Show notifications when the command started/exited.
|
||||
* +*-o*+, +*--output*+: Whether the output should be shown in a new tab.
|
||||
* +*-d*+, +*--detach*+: Whether the command should be detached from qutebrowser.
|
||||
|
||||
==== note
|
||||
|
@ -1177,7 +1177,8 @@ class CommandDispatcher:
|
||||
|
||||
@cmdutils.register(instance='command-dispatcher', scope='window',
|
||||
maxsplit=0, no_replace_variables=True)
|
||||
def spawn(self, cmdline, userscript=False, verbose=False, detach=False):
|
||||
def spawn(self, cmdline, userscript=False, verbose=False,
|
||||
output=False, detach=False):
|
||||
"""Spawn a command in a shell.
|
||||
|
||||
Args:
|
||||
@ -1188,6 +1189,7 @@ class CommandDispatcher:
|
||||
(or `$XDG_DATA_DIR`)
|
||||
- `/usr/share/qutebrowser/userscripts`
|
||||
verbose: Show notifications when the command started/exited.
|
||||
output: Whether the output should be shown in a new tab.
|
||||
detach: Whether the command should be detached from qutebrowser.
|
||||
cmdline: The commandline to execute.
|
||||
"""
|
||||
@ -1214,6 +1216,11 @@ class CommandDispatcher:
|
||||
else:
|
||||
proc.start(cmd, args)
|
||||
|
||||
if output:
|
||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||
window='last-focused')
|
||||
tabbed_browser.openurl(QUrl('qute://spawn-output'), newtab=True)
|
||||
|
||||
@cmdutils.register(instance='command-dispatcher', scope='window')
|
||||
def home(self):
|
||||
"""Open main startpage in current tab."""
|
||||
|
@ -42,6 +42,7 @@ from qutebrowser.misc import objects
|
||||
|
||||
|
||||
pyeval_output = ":pyeval was never called"
|
||||
spawn_output = ":spawn was never called"
|
||||
|
||||
|
||||
_HANDLERS = {}
|
||||
@ -268,6 +269,13 @@ def qute_pyeval(_url):
|
||||
return 'text/html', html
|
||||
|
||||
|
||||
@add_handler('spawn-output')
|
||||
def qute_spawn_output(_url):
|
||||
"""Handler for qute://spawn-output."""
|
||||
html = jinja.render('pre.html', title='spawn output', content=spawn_output)
|
||||
return 'text/html', html
|
||||
|
||||
|
||||
@add_handler('version')
|
||||
@add_handler('verizon')
|
||||
def qute_version(_url):
|
||||
|
@ -25,6 +25,7 @@ from PyQt5.QtCore import (pyqtSlot, pyqtSignal, QObject, QProcess,
|
||||
QProcessEnvironment)
|
||||
|
||||
from qutebrowser.utils import message, log
|
||||
from qutebrowser.browser import qutescheme
|
||||
|
||||
# A mapping of QProcess::ErrorCode's to human-readable strings.
|
||||
|
||||
@ -96,6 +97,13 @@ class GUIProcess(QObject):
|
||||
self._started = False
|
||||
log.procs.debug("Process finished with code {}, status {}.".format(
|
||||
code, status))
|
||||
|
||||
stderr = bytes(self._proc.readAllStandardError()).decode('utf-8')
|
||||
stdout = bytes(self._proc.readAllStandardOutput()).decode('utf-8')
|
||||
|
||||
qutescheme.spawn_output = self._spawn_format(code, status,
|
||||
stdout, stderr)
|
||||
|
||||
if status == QProcess.CrashExit:
|
||||
message.error("{} crashed!".format(self._what.capitalize()))
|
||||
elif status == QProcess.NormalExit and code == 0:
|
||||
@ -109,13 +117,22 @@ class GUIProcess(QObject):
|
||||
message.error("{} exited with status {}, see :messages for "
|
||||
"details.".format(self._what.capitalize(), code))
|
||||
|
||||
stderr = bytes(self._proc.readAllStandardError()).decode('utf-8')
|
||||
stdout = bytes(self._proc.readAllStandardOutput()).decode('utf-8')
|
||||
if stdout:
|
||||
log.procs.error("Process stdout:\n" + stdout.strip())
|
||||
if stderr:
|
||||
log.procs.error("Process stderr:\n" + stderr.strip())
|
||||
|
||||
def _spawn_format(self, code=0, status=0, stdout="", stderr=""):
|
||||
"""Produce a formatted string for spawn output."""
|
||||
stdout = (stdout or "(No output)").strip()
|
||||
stderr = (stderr or "(No output)").strip()
|
||||
|
||||
spawn_string = ("Process finished with code {}, status {}\n"
|
||||
"\nProcess stdout:\n {}"
|
||||
"\nProcess stderr:\n {}").format(code, status,
|
||||
stdout, stderr)
|
||||
return spawn_string
|
||||
|
||||
@pyqtSlot()
|
||||
def on_started(self):
|
||||
"""Called when the process started successfully."""
|
||||
|
@ -82,6 +82,7 @@ def whitelist_generator(): # noqa
|
||||
yield 'qutebrowser.utils.jinja.Loader.get_source'
|
||||
yield 'qutebrowser.utils.log.QtWarningFilter.filter'
|
||||
yield 'qutebrowser.browser.pdfjs.is_available'
|
||||
yield 'qutebrowser.misc.guiprocess.spawn_output'
|
||||
yield 'QEvent.posted'
|
||||
yield 'log_stack' # from message.py
|
||||
yield 'propagate' # logging.getLogger('...).propagate = False
|
||||
|
@ -19,7 +19,6 @@
|
||||
|
||||
"""Tests for qutebrowser.misc.guiprocess."""
|
||||
|
||||
import json
|
||||
import logging
|
||||
|
||||
import pytest
|
||||
@ -27,6 +26,7 @@ from PyQt5.QtCore import QProcess, QIODevice
|
||||
|
||||
from qutebrowser.misc import guiprocess
|
||||
from qutebrowser.utils import usertypes
|
||||
from qutebrowser.browser import qutescheme
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
@ -60,7 +60,7 @@ def test_start(proc, qtbot, message_mock, py_proc):
|
||||
proc.start(*argv)
|
||||
|
||||
assert not message_mock.messages
|
||||
assert bytes(proc._proc.readAll()).rstrip() == b'test'
|
||||
assert qutescheme.spawn_output == proc._spawn_format(stdout="test")
|
||||
|
||||
|
||||
def test_start_verbose(proc, qtbot, message_mock, py_proc):
|
||||
@ -77,7 +77,7 @@ def test_start_verbose(proc, qtbot, message_mock, py_proc):
|
||||
assert msgs[1].level == usertypes.MessageLevel.info
|
||||
assert msgs[0].text.startswith("Executing:")
|
||||
assert msgs[1].text == "Testprocess exited successfully."
|
||||
assert bytes(proc._proc.readAll()).rstrip() == b'test'
|
||||
assert qutescheme.spawn_output == proc._spawn_format(stdout="test")
|
||||
|
||||
|
||||
def test_start_env(monkeypatch, qtbot, py_proc):
|
||||
@ -99,10 +99,9 @@ def test_start_env(monkeypatch, qtbot, py_proc):
|
||||
order='strict'):
|
||||
proc.start(*argv)
|
||||
|
||||
data = bytes(proc._proc.readAll()).decode('utf-8')
|
||||
ret_env = json.loads(data)
|
||||
assert 'QUTEBROWSER_TEST_1' in ret_env
|
||||
assert 'QUTEBROWSER_TEST_2' in ret_env
|
||||
data = qutescheme.spawn_output
|
||||
assert 'QUTEBROWSER_TEST_1' in data
|
||||
assert 'QUTEBROWSER_TEST_2' in data
|
||||
|
||||
|
||||
@pytest.mark.qt_log_ignore('QIODevice::read.*: WriteOnly device')
|
||||
|
Loading…
Reference in New Issue
Block a user