summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--qutebrowser/browser/shared.py2
-rw-r--r--qutebrowser/misc/guiprocess.py61
-rwxr-xr-xscripts/dev/run_vulture.py2
-rw-r--r--tests/unit/misc/test_guiprocess.py4
4 files changed, 46 insertions, 23 deletions
diff --git a/qutebrowser/browser/shared.py b/qutebrowser/browser/shared.py
index 41e7734f8..f2ff629f1 100644
--- a/qutebrowser/browser/shared.py
+++ b/qutebrowser/browser/shared.py
@@ -453,7 +453,7 @@ def _execute_fileselect_command(
loop.exec()
if tmpfilename is None:
- selected_files = proc.final_stdout.splitlines()
+ selected_files = proc.stdout.splitlines()
else:
try:
with open(tmpfilename, mode='r', encoding=sys.getfilesystemencoding()) as f:
diff --git a/qutebrowser/misc/guiprocess.py b/qutebrowser/misc/guiprocess.py
index 95bfd64af..5bcc83698 100644
--- a/qutebrowser/misc/guiprocess.py
+++ b/qutebrowser/misc/guiprocess.py
@@ -23,7 +23,7 @@ import locale
import shlex
from PyQt5.QtCore import (pyqtSlot, pyqtSignal, QObject, QProcess,
- QProcessEnvironment)
+ QProcessEnvironment, QByteArray)
from qutebrowser.utils import message, log, utils
from qutebrowser.browser import qutescheme
@@ -61,16 +61,18 @@ class GUIProcess(QObject):
self.cmd = None
self.args = None
- self.final_stdout: str = ""
- self.final_stderr: str = ""
+ self.stdout: str = ""
+ self.stderr: str = ""
self._proc = QProcess(self)
+ self._proc.setReadChannel(QProcess.StandardOutput)
self._proc.errorOccurred.connect(self._on_error)
self._proc.errorOccurred.connect(self.error)
self._proc.finished.connect(self._on_finished)
self._proc.finished.connect(self.finished)
self._proc.started.connect(self._on_started)
self._proc.started.connect(self.started)
+ self._proc.readyRead.connect(self._on_ready_read)
if additional_env is not None:
procenv = QProcessEnvironment.systemEnvironment()
@@ -78,6 +80,32 @@ class GUIProcess(QObject):
procenv.insert(k, v)
self._proc.setProcessEnvironment(procenv)
+ def _decode_data(self, qba: QByteArray) -> str:
+ """Decode data coming from a process."""
+ encoding = locale.getpreferredencoding(do_setlocale=False)
+ return qba.data().decode(encoding, 'replace')
+
+ @pyqtSlot()
+ def _on_ready_read(self):
+ if not self._output_messages:
+ return
+
+ while True:
+ text = self._decode_data(self._proc.readLine())
+ if not text:
+ break
+
+ if '\r' in text:
+ # Crude handling of CR for e.g. progress output.
+ # Discard everything before the last \r in the new input, then discard
+ # everything after the last \n in self.stdout.
+ text = text.rsplit('\r', maxsplit=1)[-1]
+ self.stdout = self.stdout.rsplit('\n', maxsplit=1)[0] + '\n'
+
+ self.stdout += text
+
+ message.info(self.stdout.strip(), replace=True)
+
@pyqtSlot(QProcess.ProcessError)
def _on_error(self, error):
"""Show a message if there was an error while spawning."""
@@ -113,17 +141,14 @@ class GUIProcess(QObject):
log.procs.debug("Process finished with code {}, status {}.".format(
code, status))
- encoding = locale.getpreferredencoding(do_setlocale=False)
- stderr = self._proc.readAllStandardError().data().decode(
- encoding, 'replace')
- stdout = self._proc.readAllStandardOutput().data().decode(
- encoding, 'replace')
+ self.stderr += self._decode_data(self._proc.readAllStandardError())
+ self.stdout += self._decode_data(self._proc.readAllStandardOutput())
if self._output_messages:
- if stdout:
- message.info(stdout.strip())
- if stderr:
- message.error(stderr.strip())
+ if self.stdout:
+ message.info(self.stdout.strip(), replace=True)
+ if self.stderr:
+ message.error(self.stderr.strip())
if status == QProcess.CrashExit:
exitinfo = "{} crashed.".format(self._what.capitalize())
@@ -141,14 +166,12 @@ class GUIProcess(QObject):
"details.").format(self._what.capitalize(), code)
message.error(exitinfo)
- if stdout:
- log.procs.error("Process stdout:\n" + stdout.strip())
- if stderr:
- log.procs.error("Process stderr:\n" + stderr.strip())
+ if self.stdout:
+ log.procs.error("Process stdout:\n" + self.stdout.strip())
+ if self.stderr:
+ log.procs.error("Process stderr:\n" + self.stderr.strip())
- qutescheme.spawn_output = self._spawn_format(exitinfo, stdout, stderr)
- self.final_stdout = stdout
- self.final_stderr = stderr
+ qutescheme.spawn_output = self._spawn_format(exitinfo, self.stdout, self.stderr)
def _spawn_format(self, exitinfo, stdout, stderr):
"""Produce a formatted string for spawn output."""
diff --git a/scripts/dev/run_vulture.py b/scripts/dev/run_vulture.py
index 612b88637..d2b551689 100755
--- a/scripts/dev/run_vulture.py
+++ b/scripts/dev/run_vulture.py
@@ -61,7 +61,7 @@ def whitelist_generator(): # noqa: C901
yield 'scripts.utils.bg_colors'
yield 'qutebrowser.misc.sql.SqliteErrorCode.CONSTRAINT'
yield 'qutebrowser.misc.throttle.Throttle.set_delay'
- yield 'qutebrowser.misc.guiprocess.GUIProcess.final_stderr'
+ yield 'qutebrowser.misc.guiprocess.GUIProcess.stderr'
# Qt attributes
yield 'PyQt5.QtWebKit.QWebPage.ErrorPageExtensionReturn().baseUrl'
diff --git a/tests/unit/misc/test_guiprocess.py b/tests/unit/misc/test_guiprocess.py
index 18e926fab..178ac3d12 100644
--- a/tests/unit/misc/test_guiprocess.py
+++ b/tests/unit/misc/test_guiprocess.py
@@ -127,11 +127,11 @@ def test_start_output_message(proc, qtbot, caplog, message_mock, py_proc,
if stdout_msg is not None:
assert stdout_msg.level == usertypes.MessageLevel.info
assert stdout_msg.text == 'stdout text'
- assert proc.final_stdout.strip() == "stdout text", proc.final_stdout
+ assert proc.stdout.strip() == "stdout text", proc.stdout
if stderr_msg is not None:
assert stderr_msg.level == usertypes.MessageLevel.error
assert stderr_msg.text == 'stderr text'
- assert proc.final_stderr.strip() == "stderr text", proc.final_stderr
+ assert proc.stderr.strip() == "stderr text", proc.stderr
def test_start_env(monkeypatch, qtbot, py_proc):