summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Bruhin <me@the-compiler.org>2021-03-03 16:12:45 +0100
committerFlorian Bruhin <me@the-compiler.org>2021-03-03 16:12:45 +0100
commit610bcc7d8399fab45bdf27b310ffc0726fca30b1 (patch)
treeec122bd41614038274674831eb416051d3446fd2
parentef945c42f75e7a13fd0191f265fbcdebcb0a9fd7 (diff)
parent7d5acd7239170c478ea9cf696a0f59b91f7fd53d (diff)
downloadqutebrowser-610bcc7d8399fab45bdf27b310ffc0726fca30b1.tar.gz
qutebrowser-610bcc7d8399fab45bdf27b310ffc0726fca30b1.zip
Merge remote-tracking branch 'origin/pr/6116'
-rw-r--r--qutebrowser/browser/shared.py67
-rw-r--r--qutebrowser/config/configdata.yml4
-rw-r--r--qutebrowser/misc/guiprocess.py5
-rwxr-xr-xscripts/dev/run_vulture.py1
-rw-r--r--tests/end2end/features/editor.feature49
-rw-r--r--tests/end2end/features/test_editor_bdd.py37
-rw-r--r--tests/unit/misc/test_guiprocess.py2
7 files changed, 116 insertions, 49 deletions
diff --git a/qutebrowser/browser/shared.py b/qutebrowser/browser/shared.py
index 78b475835..94332ffcb 100644
--- a/qutebrowser/browser/shared.py
+++ b/qutebrowser/browser/shared.py
@@ -23,7 +23,7 @@ import os
import sys
import html
import netrc
-from typing import Callable, Mapping, List
+from typing import Callable, Mapping, List, Optional
import tempfile
from PyQt5.QtCore import QUrl
@@ -360,23 +360,60 @@ def choose_file(multiple: bool) -> List[str]:
A list of selected file paths, or empty list if no file is selected.
If multiple is False, the return value will have at most 1 item.
"""
- handle = tempfile.NamedTemporaryFile(prefix='qutebrowser-fileselect-', delete=False)
- handle.close()
- tmpfilename = handle.name
- with utils.cleanup_file(tmpfilename):
- if multiple:
- command = config.val.fileselect.multiple_files.command
- else:
- command = config.val.fileselect.single_file.command
+ if multiple:
+ command = config.val.fileselect.multiple_files.command
+ else:
+ command = config.val.fileselect.single_file.command
+ use_tmp_file = any('{}' in arg for arg in command[1:])
+ if use_tmp_file:
+ handle = tempfile.NamedTemporaryFile(
+ prefix='qutebrowser-fileselect-',
+ delete=False,
+ )
+ handle.close()
+ tmpfilename = handle.name
+ with utils.cleanup_file(tmpfilename):
+ command = (
+ command[:1] +
+ [arg.replace('{}', tmpfilename) for arg in command[1:]]
+ )
+ return _execute_fileselect_command(
+ command=command,
+ multiple=multiple,
+ tmpfilename=tmpfilename,
+ )
+ else:
+ return _execute_fileselect_command(
+ command=command,
+ multiple=multiple,
+ )
- proc = guiprocess.GUIProcess(what='choose-file')
- proc.start(command[0],
- [arg.replace('{}', tmpfilename) for arg in command[1:]])
- loop = qtutils.EventLoop()
- proc.finished.connect(lambda _code, _status: loop.exit())
- loop.exec()
+def _execute_fileselect_command(
+ command: List[str],
+ multiple: bool,
+ tmpfilename: Optional[str] = None
+) -> List[str]:
+ """Execute external command to choose file.
+ Args:
+ multiple: Should selecting multiple files be allowed.
+ tmpfilename: Path to the temporary file if used, otherwise None.
+
+ Return:
+ A list of selected file paths, or empty list if no file is selected.
+ If multiple is False, the return value will have at most 1 item.
+ """
+ proc = guiprocess.GUIProcess(what='choose-file')
+ proc.start(command[0], command[1:])
+
+ loop = qtutils.EventLoop()
+ proc.finished.connect(lambda _code, _status: loop.exit())
+ loop.exec()
+
+ if tmpfilename is None:
+ selected_files = proc.final_stdout.splitlines()
+ else:
try:
with open(tmpfilename, mode='r', encoding=sys.getfilesystemencoding()) as f:
selected_files = f.read().splitlines()
diff --git a/qutebrowser/config/configdata.yml b/qutebrowser/config/configdata.yml
index b4805665b..d93aa1e4b 100644
--- a/qutebrowser/config/configdata.yml
+++ b/qutebrowser/config/configdata.yml
@@ -1259,7 +1259,7 @@ fileselect.handler:
fileselect.single_file.command:
type:
name: ShellCommand
- placeholder: true
+ placeholder: false
completions:
- ['["xterm", "-e", "ranger", "--choosefile={}"]', "Ranger in xterm"]
- ['["xterm", "-e", "vifm", "--choose-files", "{}"]', "vifm in xterm"]
@@ -1276,7 +1276,7 @@ fileselect.single_file.command:
fileselect.multiple_files.command:
type:
name: ShellCommand
- placeholder: true
+ placeholder: false
completions:
- ['["xterm", "-e", "ranger", "--choosefiles={}"]', "Ranger in xterm"]
- ['["xterm", "-e", "vifm", "--choose-files", "{}"]', "vifm in xterm"]
diff --git a/qutebrowser/misc/guiprocess.py b/qutebrowser/misc/guiprocess.py
index 1273b227e..79c84c346 100644
--- a/qutebrowser/misc/guiprocess.py
+++ b/qutebrowser/misc/guiprocess.py
@@ -61,6 +61,9 @@ class GUIProcess(QObject):
self.cmd = None
self.args = None
+ self.final_stdout: str = ""
+ self.final_stderr: str = ""
+
self._proc = QProcess(self)
self._proc.errorOccurred.connect(self._on_error)
self._proc.errorOccurred.connect(self.error)
@@ -125,6 +128,8 @@ class GUIProcess(QObject):
log.procs.error("Process stderr:\n" + stderr.strip())
qutescheme.spawn_output = self._spawn_format(exitinfo, stdout, stderr)
+ self.final_stdout = stdout
+ self.final_stderr = 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 8895da55f..612b88637 100755
--- a/scripts/dev/run_vulture.py
+++ b/scripts/dev/run_vulture.py
@@ -61,6 +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'
# Qt attributes
yield 'PyQt5.QtWebKit.QWebPage.ErrorPageExtensionReturn().baseUrl'
diff --git a/tests/end2end/features/editor.feature b/tests/end2end/features/editor.feature
index 52756422c..47cb1230a 100644
--- a/tests/end2end/features/editor.feature
+++ b/tests/end2end/features/editor.feature
@@ -6,14 +6,14 @@ Feature: Opening external editors
Scenario: Editing a URL
When I open data/numbers/1.txt
- And I set up a fake editor replacing "1.txt" by "2.txt"
+ And I setup a fake editor replacing "1.txt" by "2.txt"
And I run :edit-url
Then data/numbers/2.txt should be loaded
Scenario: Editing a URL with -t
When I run :tab-only
And I open data/numbers/1.txt
- And I set up a fake editor replacing "1.txt" by "2.txt"
+ And I setup a fake editor replacing "1.txt" by "2.txt"
And I run :edit-url -t
Then data/numbers/2.txt should be loaded
And the following tabs should be open:
@@ -24,7 +24,7 @@ Feature: Opening external editors
When I set tabs.new_position.related to prev
And I open data/numbers/1.txt
And I run :tab-only
- And I set up a fake editor replacing "1.txt" by "2.txt"
+ And I setup a fake editor replacing "1.txt" by "2.txt"
And I run :edit-url -rt
Then data/numbers/2.txt should be loaded
And the following tabs should be open:
@@ -34,7 +34,7 @@ Feature: Opening external editors
Scenario: Editing a URL with -b
When I run :tab-only
And I open data/numbers/1.txt
- And I set up a fake editor replacing "1.txt" by "2.txt"
+ And I setup a fake editor replacing "1.txt" by "2.txt"
And I run :edit-url -b
Then data/numbers/2.txt should be loaded
And the following tabs should be open:
@@ -45,7 +45,7 @@ Feature: Opening external editors
When I run :window-only
And I open data/numbers/1.txt in a new tab
And I run :tab-only
- And I set up a fake editor replacing "1.txt" by "2.txt"
+ And I setup a fake editor replacing "1.txt" by "2.txt"
And I run :edit-url -w
Then data/numbers/2.txt should be loaded
And the session should look like:
@@ -65,7 +65,7 @@ Feature: Opening external editors
When I open data/numbers/1.txt in a new tab
And I run :tab-only
And I run :window-only
- And I set up a fake editor replacing "1.txt" by "2.txt"
+ And I setup a fake editor replacing "1.txt" by "2.txt"
And I run :edit-url -p
Then data/numbers/2.txt should be loaded
And the session should look like:
@@ -90,13 +90,13 @@ Feature: Opening external editors
Scenario: Editing a URL with invalid URL
When I set url.auto_search to never
And I open data/hello.txt
- And I set up a fake editor replacing "http://localhost:(port)/data/hello.txt" by "foo!"
+ And I setup a fake editor replacing "http://localhost:(port)/data/hello.txt" by "foo!"
And I run :edit-url
Then the error "Invalid URL" should be shown
Scenario: Spawning an editor successfully
Given I have a fresh instance
- When I set up a fake editor returning "foobar"
+ When I setup a fake editor returning "foobar"
And I open data/editor.html
And I run :click-element id qute-textarea
And I wait for "Entering mode KeyMode.insert (reason: clicking input)" in the log
@@ -105,7 +105,7 @@ Feature: Opening external editors
Then the javascript message "text: foobar" should be logged
Scenario: Spawning an editor in normal mode
- When I set up a fake editor returning "foobar"
+ When I setup a fake editor returning "foobar"
And I open data/editor.html
And I run :click-element id qute-textarea
And I wait for "Entering mode KeyMode.insert (reason: clicking input)" in the log
@@ -119,7 +119,7 @@ Feature: Opening external editors
# There's no guarantee that the tab gets deleted...
@posix
Scenario: Spawning an editor and closing the tab
- When I set up a fake editor that writes "foobar" on save
+ When I setup a fake editor that writes "foobar" on save
And I open data/editor.html
And I run :click-element id qute-textarea
And I wait for "Entering mode KeyMode.insert (reason: clicking input)" in the log
@@ -134,7 +134,7 @@ Feature: Opening external editors
# Could not get signals working on Windows
@posix
Scenario: Spawning an editor and saving
- When I set up a fake editor that writes "foobar" on save
+ When I setup a fake editor that writes "foobar" on save
And I open data/editor.html
And I run :click-element id qute-textarea
And I wait for "Entering mode KeyMode.insert (reason: clicking input)" in the log
@@ -145,7 +145,7 @@ Feature: Opening external editors
Then the javascript message "text: foobar" should be logged
Scenario: Spawning an editor in caret mode
- When I set up a fake editor returning "foobar"
+ When I setup a fake editor returning "foobar"
And I open data/editor.html
And I run :click-element id qute-textarea
And I wait for "Entering mode KeyMode.insert (reason: clicking input)" in the log
@@ -159,7 +159,7 @@ Feature: Opening external editors
Then the javascript message "text: foobar" should be logged
Scenario: Spawning an editor with existing text
- When I set up a fake editor replacing "foo" by "bar"
+ When I setup a fake editor replacing "foo" by "bar"
And I open data/editor.html
And I run :click-element id qute-textarea
And I wait for "Entering mode KeyMode.insert (reason: clicking input)" in the log
@@ -173,20 +173,20 @@ Feature: Opening external editors
Scenario: Edit a command and run it
When I run :set-cmd-text :message-info foo
- And I set up a fake editor replacing "foo" by "bar"
+ And I setup a fake editor replacing "foo" by "bar"
And I run :edit-command --run
Then the message "bar" should be shown
And "Leaving mode KeyMode.command (reason: cmd accept)" should be logged
Scenario: Edit a command and omit the start char
- When I set up a fake editor returning "message-info foo"
+ When I setup a fake editor returning "message-info foo"
And I run :edit-command
Then the error "command must start with one of :/?" should be shown
And "Leaving mode KeyMode.command *" should not be logged
Scenario: Edit a command to be empty
When I run :set-cmd-text :
- When I set up a fake editor returning empty text
+ When I setup a fake editor returning empty text
And I run :edit-command
Then the error "command must start with one of :/?" should be shown
And "Leaving mode KeyMode.command *" should not be logged
@@ -194,13 +194,20 @@ Feature: Opening external editors
## select single file
Scenario: Select one file with single command
- When I set up a fake "single_file" fileselector selecting "tests/end2end/data/numbers/1.txt"
+ When I setup a fake single_file fileselector selecting "tests/end2end/data/numbers/1.txt" and writes to a temporary file
+ And I open data/fileselect.html
+ And I run :click-element id single_file
+ Then the javascript message "Files: 1.txt" should be logged
+
+ Scenario: Select one file with single command that writes to stdout
+ When I setup a fake single_file fileselector selecting "tests/end2end/data/numbers/1.txt" and writes to stdout
And I open data/fileselect.html
And I run :click-element id single_file
Then the javascript message "Files: 1.txt" should be logged
Scenario: Select two files with single command
- When I set up a fake "single_file" fileselector selecting "tests/end2end/data/numbers/1.txt tests/end2end/data/numbers/2.txt"
+ When I setup a fake single_file fileselector selecting "tests/end2end/data/numbers/1.txt tests/end2end/data/numbers/2.txt" and writes to a temporary file
+
And I open data/fileselect.html
And I run :click-element id single_file
Then the javascript message "Files: 1.txt" should be logged
@@ -209,13 +216,15 @@ Feature: Opening external editors
## select multiple files
Scenario: Select one file with multiple command
- When I set up a fake "multiple_files" fileselector selecting "tests/end2end/data/numbers/1.txt"
+ When I setup a fake multiple_files fileselector selecting "tests/end2end/data/numbers/1.txt" and writes to a temporary file
+
And I open data/fileselect.html
And I run :click-element id multiple_files
Then the javascript message "Files: 1.txt" should be logged
Scenario: Select two files with multiple command
- When I set up a fake "multiple_files" fileselector selecting "tests/end2end/data/numbers/1.txt tests/end2end/data/numbers/2.txt"
+ When I setup a fake multiple_files fileselector selecting "tests/end2end/data/numbers/1.txt tests/end2end/data/numbers/2.txt" and writes to a temporary file
+
And I open data/fileselect.html
And I run :click-element id multiple_files
Then the javascript message "Files: 1.txt, 2.txt" should be logged
diff --git a/tests/end2end/features/test_editor_bdd.py b/tests/end2end/features/test_editor_bdd.py
index 445691bee..40f77a0f7 100644
--- a/tests/end2end/features/test_editor_bdd.py
+++ b/tests/end2end/features/test_editor_bdd.py
@@ -32,7 +32,7 @@ bdd.scenarios('editor.feature')
from qutebrowser.utils import utils
-@bdd.when(bdd.parsers.parse('I set up a fake editor replacing "{text}" by '
+@bdd.when(bdd.parsers.parse('I setup a fake editor replacing "{text}" by '
'"{replacement}"'))
def set_up_editor_replacement(quteproc, server, tmpdir, text, replacement):
"""Set up editor.command to a small python script doing a replacement."""
@@ -53,7 +53,7 @@ def set_up_editor_replacement(quteproc, server, tmpdir, text, replacement):
quteproc.set_setting('editor.command', editor)
-@bdd.when(bdd.parsers.parse('I set up a fake editor returning "{text}"'))
+@bdd.when(bdd.parsers.parse('I setup a fake editor returning "{text}"'))
def set_up_editor(quteproc, tmpdir, text):
"""Set up editor.command to a small python script inserting a text."""
script = tmpdir / 'script.py'
@@ -67,7 +67,7 @@ def set_up_editor(quteproc, tmpdir, text):
quteproc.set_setting('editor.command', editor)
-@bdd.when(bdd.parsers.parse('I set up a fake editor returning empty text'))
+@bdd.when(bdd.parsers.parse('I setup a fake editor returning empty text'))
def set_up_editor_empty(quteproc, tmpdir):
"""Set up editor.command to a small python script inserting empty text."""
set_up_editor(quteproc, tmpdir, "")
@@ -107,7 +107,7 @@ def editor_pid_watcher(tmpdir):
return EditorPidWatcher(tmpdir)
-@bdd.when(bdd.parsers.parse('I set up a fake editor that writes "{text}" on '
+@bdd.when(bdd.parsers.parse('I setup a fake editor that writes "{text}" on '
'save'))
def set_up_editor_wait(quteproc, tmpdir, text, editor_pid_watcher):
"""Set up editor.command to a small python script inserting a text."""
@@ -180,18 +180,31 @@ def save_editor_wait(tmpdir):
os.kill(pid, signal.SIGUSR2)
-@bdd.when(bdd.parsers.parse('I set up a fake "{kind}" fileselector '
- 'selecting "{files}"'))
-def set_up_fileselector(quteproc, py_proc, kind, files):
+@bdd.when(bdd.parsers.parse('I setup a fake {kind} fileselector '
+ 'selecting "{files}" and writes to {output_type}'))
+def set_up_fileselector(quteproc, py_proc, kind, files, output_type):
"""Set up fileselect.xxx.command to select the file(s)."""
cmd, args = py_proc(r"""
import os
import sys
- tmp_file = sys.argv[1]
- with open(tmp_file, 'w') as f:
- for selected_file in sys.argv[2:]:
- f.write(os.path.abspath(selected_file) + "\n")
+ tmp_file = None
+ for i, arg in enumerate(sys.argv):
+ if arg.startswith('--file='):
+ tmp_file = arg[len('--file='):]
+ sys.argv.pop(i)
+ break
+ selected_files = sys.argv[1:]
+ if tmp_file is None:
+ for selected_file in selected_files:
+ print(os.path.abspath(selected_file))
+ else:
+ with open(tmp_file, 'w') as f:
+ for selected_file in selected_files:
+ f.write(os.path.abspath(selected_file) + '\n')
""")
- fileselect_cmd = json.dumps([cmd, *args, '{}', *files.split(' ')])
+ args += files.split(' ')
+ if output_type == "a temporary file":
+ args += ['--file={}']
+ fileselect_cmd = json.dumps([cmd, *args])
quteproc.set_setting('fileselect.handler', 'external')
quteproc.set_setting(f'fileselect.{kind}.command', fileselect_cmd)
diff --git a/tests/unit/misc/test_guiprocess.py b/tests/unit/misc/test_guiprocess.py
index a2acad1ac..9e1b3916c 100644
--- a/tests/unit/misc/test_guiprocess.py
+++ b/tests/unit/misc/test_guiprocess.py
@@ -127,9 +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
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
def test_start_env(monkeypatch, qtbot, py_proc):