Merge branch 'feature/target-flag' of https://github.com/MazeChaZer/qutebrowser into MazeChaZer-feature/target-flag
This commit is contained in:
commit
424809b120
@ -103,8 +103,9 @@ def run(args):
|
|||||||
if server is None:
|
if server is None:
|
||||||
sys.exit(usertypes.Exit.ok)
|
sys.exit(usertypes.Exit.ok)
|
||||||
else:
|
else:
|
||||||
server.got_args.connect(lambda args, cwd:
|
server.got_args.connect(lambda args, target_arg, cwd:
|
||||||
process_pos_args(args, cwd=cwd, via_ipc=True))
|
process_pos_args(args, cwd=cwd, via_ipc=True,
|
||||||
|
target_arg=target_arg))
|
||||||
|
|
||||||
init(args, crash_handler)
|
init(args, crash_handler)
|
||||||
ret = qt_mainloop()
|
ret = qt_mainloop()
|
||||||
@ -229,7 +230,7 @@ def _load_session(name):
|
|||||||
session_manager.delete('_restart')
|
session_manager.delete('_restart')
|
||||||
|
|
||||||
|
|
||||||
def process_pos_args(args, via_ipc=False, cwd=None):
|
def process_pos_args(args, via_ipc=False, cwd=None, target_arg=None):
|
||||||
"""Process positional commandline args.
|
"""Process positional commandline args.
|
||||||
|
|
||||||
URLs to open have no prefix, commands to execute begin with a colon.
|
URLs to open have no prefix, commands to execute begin with a colon.
|
||||||
@ -238,6 +239,14 @@ def process_pos_args(args, via_ipc=False, cwd=None):
|
|||||||
args: A list of arguments to process.
|
args: A list of arguments to process.
|
||||||
via_ipc: Whether the arguments were transmitted over IPC.
|
via_ipc: Whether the arguments were transmitted over IPC.
|
||||||
cwd: The cwd to use for fuzzy_url.
|
cwd: The cwd to use for fuzzy_url.
|
||||||
|
target_arg: Command line argument received by a running instance via
|
||||||
|
ipc. If the --target argument was not specified, target_arg
|
||||||
|
will be an empty string instead of None. This behavior is
|
||||||
|
caused by the PyQt signal
|
||||||
|
``got_args = pyqtSignal(list, str, str)``
|
||||||
|
used in the misc.ipc.IPCServer class. PyQt converts the
|
||||||
|
None value into a null QString and then back to an empty
|
||||||
|
python string
|
||||||
"""
|
"""
|
||||||
if via_ipc and not args:
|
if via_ipc and not args:
|
||||||
win_id = mainwindow.get_window(via_ipc, force_window=True)
|
win_id = mainwindow.get_window(via_ipc, force_window=True)
|
||||||
@ -255,7 +264,11 @@ def process_pos_args(args, via_ipc=False, cwd=None):
|
|||||||
log.init.debug("Empty argument")
|
log.init.debug("Empty argument")
|
||||||
win_id = mainwindow.get_window(via_ipc, force_window=True)
|
win_id = mainwindow.get_window(via_ipc, force_window=True)
|
||||||
else:
|
else:
|
||||||
win_id = mainwindow.get_window(via_ipc)
|
if via_ipc and target_arg and target_arg != 'auto':
|
||||||
|
open_target = target_arg
|
||||||
|
else:
|
||||||
|
open_target = config.get('general', 'new-instance-open-target')
|
||||||
|
win_id = mainwindow.get_window(via_ipc, force_target=open_target)
|
||||||
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
tabbed_browser = objreg.get('tabbed-browser', scope='window',
|
||||||
window=win_id)
|
window=win_id)
|
||||||
log.init.debug("Startup URL {}".format(cmd))
|
log.init.debug("Startup URL {}".format(cmd))
|
||||||
@ -265,7 +278,6 @@ def process_pos_args(args, via_ipc=False, cwd=None):
|
|||||||
message.error('current', "Error in startup argument '{}': "
|
message.error('current', "Error in startup argument '{}': "
|
||||||
"{}".format(cmd, e))
|
"{}".format(cmd, e))
|
||||||
else:
|
else:
|
||||||
open_target = config.get('general', 'new-instance-open-target')
|
|
||||||
background = open_target in ('tab-bg', 'tab-bg-silent')
|
background = open_target in ('tab-bg', 'tab-bg-silent')
|
||||||
tabbed_browser.tabopen(url, background=background)
|
tabbed_browser.tabopen(url, background=background)
|
||||||
|
|
||||||
|
@ -41,13 +41,15 @@ from qutebrowser.misc import crashsignal
|
|||||||
win_id_gen = itertools.count(0)
|
win_id_gen = itertools.count(0)
|
||||||
|
|
||||||
|
|
||||||
def get_window(via_ipc, force_window=False, force_tab=False):
|
def get_window(via_ipc, force_window=False, force_tab=False,
|
||||||
|
force_target=None):
|
||||||
"""Helper function for app.py to get a window id.
|
"""Helper function for app.py to get a window id.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
via_ipc: Whether the request was made via IPC.
|
via_ipc: Whether the request was made via IPC.
|
||||||
force_window: Whether to force opening in a window.
|
force_window: Whether to force opening in a window.
|
||||||
force_tab: Whether to force opening in a tab.
|
force_tab: Whether to force opening in a tab.
|
||||||
|
force_target: Override the new-instance-open-target config
|
||||||
"""
|
"""
|
||||||
if force_window and force_tab:
|
if force_window and force_tab:
|
||||||
raise ValueError("force_window and force_tab are mutually exclusive!")
|
raise ValueError("force_window and force_tab are mutually exclusive!")
|
||||||
@ -55,7 +57,10 @@ def get_window(via_ipc, force_window=False, force_tab=False):
|
|||||||
# Initial main window
|
# Initial main window
|
||||||
return 0
|
return 0
|
||||||
window_to_raise = None
|
window_to_raise = None
|
||||||
open_target = config.get('general', 'new-instance-open-target')
|
if force_target is not None:
|
||||||
|
open_target = force_target
|
||||||
|
else:
|
||||||
|
open_target = config.get('general', 'new-instance-open-target')
|
||||||
if (open_target == 'window' or force_window) and not force_tab:
|
if (open_target == 'window' or force_window) and not force_tab:
|
||||||
window = MainWindow()
|
window = MainWindow()
|
||||||
window.show()
|
window.show()
|
||||||
|
@ -152,7 +152,7 @@ class IPCServer(QObject):
|
|||||||
got_invalid_data: Emitted when there was invalid incoming data.
|
got_invalid_data: Emitted when there was invalid incoming data.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
got_args = pyqtSignal(list, str)
|
got_args = pyqtSignal(list, str, str)
|
||||||
got_raw = pyqtSignal(bytes)
|
got_raw = pyqtSignal(bytes)
|
||||||
got_invalid_data = pyqtSignal()
|
got_invalid_data = pyqtSignal()
|
||||||
|
|
||||||
@ -314,12 +314,12 @@ class IPCServer(QObject):
|
|||||||
self._handle_invalid_data()
|
self._handle_invalid_data()
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
for name in ('args', 'target_arg'):
|
||||||
args = json_data['args']
|
if name not in json_data:
|
||||||
except KeyError:
|
log.ipc.error("Missing {}: {}".format(name,
|
||||||
log.ipc.error("no args: {}".format(decoded.strip()))
|
decoded.strip()))
|
||||||
self._handle_invalid_data()
|
self._handle_invalid_data()
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
protocol_version = int(json_data['protocol_version'])
|
protocol_version = int(json_data['protocol_version'])
|
||||||
@ -336,7 +336,7 @@ class IPCServer(QObject):
|
|||||||
return
|
return
|
||||||
|
|
||||||
cwd = json_data.get('cwd', None)
|
cwd = json_data.get('cwd', None)
|
||||||
self.got_args.emit(args, cwd)
|
self.got_args.emit(json_data['args'], json_data['target_arg'], cwd)
|
||||||
|
|
||||||
@pyqtSlot()
|
@pyqtSlot()
|
||||||
def on_timeout(self):
|
def on_timeout(self):
|
||||||
@ -418,8 +418,8 @@ def _has_legacy_server(name):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def send_to_running_instance(socketname, command, *, legacy_name=None,
|
def send_to_running_instance(socketname, command, target_arg, *,
|
||||||
socket=None):
|
legacy_name=None, socket=None):
|
||||||
"""Try to send a commandline to a running instance.
|
"""Try to send a commandline to a running instance.
|
||||||
|
|
||||||
Blocks for CONNECT_TIMEOUT ms.
|
Blocks for CONNECT_TIMEOUT ms.
|
||||||
@ -427,6 +427,7 @@ def send_to_running_instance(socketname, command, *, legacy_name=None,
|
|||||||
Args:
|
Args:
|
||||||
socketname: The name which should be used for the socket.
|
socketname: The name which should be used for the socket.
|
||||||
command: The command to send to the running instance.
|
command: The command to send to the running instance.
|
||||||
|
target_arg: --target command line argument
|
||||||
socket: The socket to read data from, or None.
|
socket: The socket to read data from, or None.
|
||||||
legacy_name: The legacy name to first try to connect to.
|
legacy_name: The legacy name to first try to connect to.
|
||||||
|
|
||||||
@ -448,7 +449,8 @@ def send_to_running_instance(socketname, command, *, legacy_name=None,
|
|||||||
connected = socket.waitForConnected(CONNECT_TIMEOUT)
|
connected = socket.waitForConnected(CONNECT_TIMEOUT)
|
||||||
if connected:
|
if connected:
|
||||||
log.ipc.info("Opening in existing instance")
|
log.ipc.info("Opening in existing instance")
|
||||||
json_data = {'args': command, 'version': qutebrowser.__version__,
|
json_data = {'args': command, 'target_arg': target_arg,
|
||||||
|
'version': qutebrowser.__version__,
|
||||||
'protocol_version': PROTOCOL_VERSION}
|
'protocol_version': PROTOCOL_VERSION}
|
||||||
try:
|
try:
|
||||||
cwd = os.getcwd()
|
cwd = os.getcwd()
|
||||||
@ -500,6 +502,7 @@ def send_or_listen(args):
|
|||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
sent = send_to_running_instance(socketname, args.command,
|
sent = send_to_running_instance(socketname, args.command,
|
||||||
|
args.target,
|
||||||
legacy_name=legacy_socketname)
|
legacy_name=legacy_socketname)
|
||||||
if sent:
|
if sent:
|
||||||
return None
|
return None
|
||||||
@ -513,6 +516,7 @@ def send_or_listen(args):
|
|||||||
log.init.debug("Got AddressInUseError, trying again.")
|
log.init.debug("Got AddressInUseError, trying again.")
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
sent = send_to_running_instance(socketname, args.command,
|
sent = send_to_running_instance(socketname, args.command,
|
||||||
|
args.target,
|
||||||
legacy_name=legacy_socketname)
|
legacy_name=legacy_socketname)
|
||||||
if sent:
|
if sent:
|
||||||
return None
|
return None
|
||||||
|
@ -65,6 +65,10 @@ def get_argparser():
|
|||||||
parser.add_argument('-R', '--override-restore', help="Don't restore a "
|
parser.add_argument('-R', '--override-restore', help="Don't restore a "
|
||||||
"session even if one would be restored.",
|
"session even if one would be restored.",
|
||||||
action='store_true')
|
action='store_true')
|
||||||
|
parser.add_argument('--target', choices=['auto', 'tab', 'tab-bg',
|
||||||
|
'tab-silent', 'tab-bg-silent', 'window'],
|
||||||
|
help="How URLs should be opened if there is already a "
|
||||||
|
"qutebrowser instance running.")
|
||||||
parser.add_argument('--json-args', help=argparse.SUPPRESS)
|
parser.add_argument('--json-args', help=argparse.SUPPRESS)
|
||||||
|
|
||||||
debug = parser.add_argument_group('debug arguments')
|
debug = parser.add_argument_group('debug arguments')
|
||||||
|
@ -442,8 +442,8 @@ class TestHandleConnection:
|
|||||||
assert msg in all_msgs
|
assert msg in all_msgs
|
||||||
|
|
||||||
def test_read_line_immediately(self, qtbot, ipc_server, caplog):
|
def test_read_line_immediately(self, qtbot, ipc_server, caplog):
|
||||||
data = '{{"args": ["foo"], "protocol_version": {}}}\n'.format(
|
data = ('{{"args": ["foo"], "target_arg": "tab", '
|
||||||
ipc.PROTOCOL_VERSION)
|
'"protocol_version": {}}}\n'.format(ipc.PROTOCOL_VERSION))
|
||||||
socket = FakeSocket(data=data.encode('utf-8'))
|
socket = FakeSocket(data=data.encode('utf-8'))
|
||||||
|
|
||||||
ipc_server._server = FakeServer(socket)
|
ipc_server._server = FakeServer(socket)
|
||||||
@ -454,6 +454,7 @@ class TestHandleConnection:
|
|||||||
|
|
||||||
assert len(spy) == 1
|
assert len(spy) == 1
|
||||||
assert spy[0][0] == ['foo']
|
assert spy[0][0] == ['foo']
|
||||||
|
assert spy[0][1] == 'tab'
|
||||||
|
|
||||||
all_msgs = [r.message for r in caplog.records()]
|
all_msgs = [r.message for r in caplog.records()]
|
||||||
assert "We can read a line immediately." in all_msgs
|
assert "We can read a line immediately." in all_msgs
|
||||||
@ -489,13 +490,15 @@ NEW_VERSION = str(ipc.PROTOCOL_VERSION + 1).encode('utf-8')
|
|||||||
(b'\x80\n', 'invalid utf-8'),
|
(b'\x80\n', 'invalid utf-8'),
|
||||||
(b'\n', 'invalid json'),
|
(b'\n', 'invalid json'),
|
||||||
(b'{"is this invalid json?": true\n', 'invalid json'),
|
(b'{"is this invalid json?": true\n', 'invalid json'),
|
||||||
(b'{"valid json without args": true}\n', 'no args'),
|
(b'{"valid json without args": true}\n', 'Missing args'),
|
||||||
(b'{"args": [], "protocol_version": ' + OLD_VERSION + b'}\n',
|
(b'{"args": []}\n', 'Missing target_arg'),
|
||||||
'incompatible version'),
|
(b'{"args": [], "target_arg": null, "protocol_version": ' + OLD_VERSION +
|
||||||
(b'{"args": [], "protocol_version": ' + NEW_VERSION + b'}\n',
|
b'}\n', 'incompatible version'),
|
||||||
'incompatible version'),
|
(b'{"args": [], "target_arg": null, "protocol_version": ' + NEW_VERSION +
|
||||||
(b'{"args": [], "protocol_version": "foo"}\n', 'invalid version'),
|
b'}\n', 'incompatible version'),
|
||||||
(b'{"args": []}\n', 'invalid version'),
|
(b'{"args": [], "target_arg": null, "protocol_version": "foo"}\n',
|
||||||
|
'invalid version'),
|
||||||
|
(b'{"args": [], "target_arg": null}\n', 'invalid version'),
|
||||||
])
|
])
|
||||||
def test_invalid_data(qtbot, ipc_server, connected_socket, caplog, data, msg):
|
def test_invalid_data(qtbot, ipc_server, connected_socket, caplog, data, msg):
|
||||||
got_args_spy = QSignalSpy(ipc_server.got_args)
|
got_args_spy = QSignalSpy(ipc_server.got_args)
|
||||||
@ -515,8 +518,10 @@ def test_multiline(qtbot, ipc_server, connected_socket):
|
|||||||
spy = QSignalSpy(ipc_server.got_args)
|
spy = QSignalSpy(ipc_server.got_args)
|
||||||
error_spy = QSignalSpy(ipc_server.got_invalid_data)
|
error_spy = QSignalSpy(ipc_server.got_invalid_data)
|
||||||
|
|
||||||
data = ('{{"args": ["one"], "protocol_version": {version}}}\n'
|
data = ('{{"args": ["one"], "target_arg": "tab",'
|
||||||
'{{"args": ["two"], "protocol_version": {version}}}\n'.format(
|
' "protocol_version": {version}}}\n'
|
||||||
|
'{{"args": ["two"], "target_arg": null,'
|
||||||
|
' "protocol_version": {version}}}\n'.format(
|
||||||
version=ipc.PROTOCOL_VERSION))
|
version=ipc.PROTOCOL_VERSION))
|
||||||
|
|
||||||
with qtbot.waitSignals([ipc_server.got_args, ipc_server.got_args],
|
with qtbot.waitSignals([ipc_server.got_args, ipc_server.got_args],
|
||||||
@ -526,13 +531,15 @@ def test_multiline(qtbot, ipc_server, connected_socket):
|
|||||||
assert len(spy) == 2
|
assert len(spy) == 2
|
||||||
assert not error_spy
|
assert not error_spy
|
||||||
assert spy[0][0] == ['one']
|
assert spy[0][0] == ['one']
|
||||||
|
assert spy[0][1] == 'tab'
|
||||||
assert spy[1][0] == ['two']
|
assert spy[1][0] == ['two']
|
||||||
|
assert spy[1][1] == ''
|
||||||
|
|
||||||
|
|
||||||
class TestSendToRunningInstance:
|
class TestSendToRunningInstance:
|
||||||
|
|
||||||
def test_no_server(self, caplog):
|
def test_no_server(self, caplog):
|
||||||
sent = ipc.send_to_running_instance('qute-test', [])
|
sent = ipc.send_to_running_instance('qute-test', [], None)
|
||||||
assert not sent
|
assert not sent
|
||||||
msg = caplog.records()[-1].message
|
msg = caplog.records()[-1].message
|
||||||
assert msg == "No existing instance present (error 2)"
|
assert msg == "No existing instance present (error 2)"
|
||||||
@ -550,7 +557,7 @@ class TestSendToRunningInstance:
|
|||||||
if not has_cwd:
|
if not has_cwd:
|
||||||
m = mocker.patch('qutebrowser.misc.ipc.os')
|
m = mocker.patch('qutebrowser.misc.ipc.os')
|
||||||
m.getcwd.side_effect = OSError
|
m.getcwd.side_effect = OSError
|
||||||
sent = ipc.send_to_running_instance('qute-test', ['foo'])
|
sent = ipc.send_to_running_instance('qute-test', ['foo'], None)
|
||||||
|
|
||||||
assert sent
|
assert sent
|
||||||
|
|
||||||
@ -558,11 +565,12 @@ class TestSendToRunningInstance:
|
|||||||
expected_cwd = str(tmpdir) if has_cwd else ''
|
expected_cwd = str(tmpdir) if has_cwd else ''
|
||||||
|
|
||||||
assert len(spy) == 1
|
assert len(spy) == 1
|
||||||
assert spy[0] == [['foo'], expected_cwd]
|
assert spy[0] == [['foo'], '', expected_cwd]
|
||||||
|
|
||||||
assert len(raw_spy) == 1
|
assert len(raw_spy) == 1
|
||||||
assert len(raw_spy[0]) == 1
|
assert len(raw_spy[0]) == 1
|
||||||
raw_expected = {'args': ['foo'], 'version': qutebrowser.__version__,
|
raw_expected = {'args': ['foo'], 'target_arg': None,
|
||||||
|
'version': qutebrowser.__version__,
|
||||||
'protocol_version': ipc.PROTOCOL_VERSION}
|
'protocol_version': ipc.PROTOCOL_VERSION}
|
||||||
if has_cwd:
|
if has_cwd:
|
||||||
raw_expected['cwd'] = str(tmpdir)
|
raw_expected['cwd'] = str(tmpdir)
|
||||||
@ -572,20 +580,20 @@ class TestSendToRunningInstance:
|
|||||||
def test_socket_error(self):
|
def test_socket_error(self):
|
||||||
socket = FakeSocket(error=QLocalSocket.ConnectionError)
|
socket = FakeSocket(error=QLocalSocket.ConnectionError)
|
||||||
with pytest.raises(ipc.Error) as excinfo:
|
with pytest.raises(ipc.Error) as excinfo:
|
||||||
ipc.send_to_running_instance('qute-test', [], socket=socket)
|
ipc.send_to_running_instance('qute-test', [], None, socket=socket)
|
||||||
|
|
||||||
msg = "Error while writing to running instance: Error string (error 7)"
|
msg = "Error while writing to running instance: Error string (error 7)"
|
||||||
assert str(excinfo.value) == msg
|
assert str(excinfo.value) == msg
|
||||||
|
|
||||||
def test_not_disconnected_immediately(self):
|
def test_not_disconnected_immediately(self):
|
||||||
socket = FakeSocket()
|
socket = FakeSocket()
|
||||||
ipc.send_to_running_instance('qute-test', [], socket=socket)
|
ipc.send_to_running_instance('qute-test', [], None, socket=socket)
|
||||||
|
|
||||||
def test_socket_error_no_server(self):
|
def test_socket_error_no_server(self):
|
||||||
socket = FakeSocket(error=QLocalSocket.ConnectionError,
|
socket = FakeSocket(error=QLocalSocket.ConnectionError,
|
||||||
connect_successful=False)
|
connect_successful=False)
|
||||||
with pytest.raises(ipc.Error) as excinfo:
|
with pytest.raises(ipc.Error) as excinfo:
|
||||||
ipc.send_to_running_instance('qute-test', [], socket=socket)
|
ipc.send_to_running_instance('qute-test', [], None, socket=socket)
|
||||||
|
|
||||||
msg = ("Error while connecting to running instance: Error string "
|
msg = ("Error while connecting to running instance: Error string "
|
||||||
"(error 7)")
|
"(error 7)")
|
||||||
@ -628,12 +636,13 @@ def test_ipcserver_socket_none(ipc_server, caplog, method, args):
|
|||||||
|
|
||||||
class TestSendOrListen:
|
class TestSendOrListen:
|
||||||
|
|
||||||
Args = collections.namedtuple('Args', 'no_err_windows, basedir, command')
|
Args = collections.namedtuple('Args', 'no_err_windows, basedir, command, '
|
||||||
|
'target')
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def args(self):
|
def args(self):
|
||||||
return self.Args(no_err_windows=True, basedir='/basedir/for/testing',
|
return self.Args(no_err_windows=True, basedir='/basedir/for/testing',
|
||||||
command=['test'])
|
command=['test'], target=None)
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
|
Loading…
Reference in New Issue
Block a user