Add --basedir arg with multiple instance support.

Closes #510.
This commit is contained in:
Florian Bruhin 2015-05-16 23:10:20 +02:00
parent aab5411317
commit 54131e9d3e
6 changed files with 69 additions and 16 deletions

View File

@ -47,6 +47,9 @@ show it.
*--cachedir* 'CACHEDIR'::
Set cache directory (empty for no cache storage).
*--basedir* 'BASEDIR'::
Base directory for all storage. Other --*dir arguments are ignored if this is given.
*-V*, *--version*::
Show version and quit.

View File

@ -84,11 +84,11 @@ def run(args):
objreg.register('signal-handler', signal_handler)
try:
sent = ipc.send_to_running_instance(args.command)
sent = ipc.send_to_running_instance(args)
if sent:
sys.exit(0)
log.init.debug("Starting IPC server...")
server = ipc.IPCServer(qApp)
server = ipc.IPCServer(args, qApp)
objreg.register('ipc-server', server)
server.got_args.connect(lambda args, cwd:
process_pos_args(args, cwd=cwd, via_ipc=True))
@ -96,7 +96,7 @@ def run(args):
# This could be a race condition...
log.init.debug("Got AddressInUseError, trying again.")
time.sleep(500)
sent = ipc.send_to_running_instance(args.command)
sent = ipc.send_to_running_instance(args)
if sent:
sys.exit(0)
else:
@ -199,7 +199,7 @@ def _process_args(args):
process_pos_args(args.command)
_open_startpage()
_open_quickstart()
_open_quickstart(args)
def _load_session(name):
@ -303,8 +303,15 @@ def _open_startpage(win_id=None):
tabbed_browser.tabopen(url)
def _open_quickstart():
"""Open quickstart if it's the first start."""
def _open_quickstart(args):
"""Open quickstart if it's the first start.
Args:
args: The argparse namespace.
"""
if args.datadir is not None or args.basedir is not None:
# With --datadir or --basedir given, don't open quickstart.
return
state_config = objreg.get('state-config')
try:
quickstart_done = state_config['general']['quickstart-done'] == '1'

View File

@ -23,6 +23,7 @@ import os
import json
import getpass
import binascii
import hashlib
from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject
from PyQt5.QtNetwork import QLocalSocket, QLocalServer, QAbstractSocket
@ -31,12 +32,20 @@ from PyQt5.QtWidgets import QMessageBox
from qutebrowser.utils import log, usertypes
SOCKETNAME = 'qutebrowser-{}'.format(getpass.getuser())
CONNECT_TIMEOUT = 100
WRITE_TIMEOUT = 1000
READ_TIMEOUT = 5000
def _get_socketname(args):
"""Get a socketname to use."""
parts = ['qutebrowser', getpass.getuser()]
if args.basedir is not None:
md5 = hashlib.md5(args.basedir.encode('utf-8'))
parts.append(md5.hexdigest())
return '-'.join(parts)
class Error(Exception):
"""Exception raised when there was a problem with IPC."""
@ -80,6 +89,7 @@ class IPCServer(QObject):
_timer: A timer to handle timeouts.
_server: A QLocalServer to accept new connections.
_socket: The QLocalSocket we're currently connected to.
_socketname: The socketname to use.
Signals:
got_args: Emitted when there was an IPC connection and arguments were
@ -88,16 +98,22 @@ class IPCServer(QObject):
got_args = pyqtSignal(list, str)
def __init__(self, parent=None):
"""Start the IPC server and listen to commands."""
def __init__(self, args, parent=None):
"""Start the IPC server and listen to commands.
Args:
args: The argparse namespace.
parent: The parent to be used.
"""
super().__init__(parent)
self.ignored = False
self._socketname = _get_socketname(args)
self._remove_server()
self._timer = usertypes.Timer(self, 'ipc-timeout')
self._timer.setInterval(READ_TIMEOUT)
self._timer.timeout.connect(self.on_timeout)
self._server = QLocalServer(self)
ok = self._server.listen(SOCKETNAME)
ok = self._server.listen(self._socketname)
if not ok:
if self._server.serverError() == QAbstractSocket.AddressInUseError:
raise AddressInUseError(self._server)
@ -108,9 +124,10 @@ class IPCServer(QObject):
def _remove_server(self):
"""Remove an existing server."""
ok = QLocalServer.removeServer(SOCKETNAME)
ok = QLocalServer.removeServer(self._socketname)
if not ok:
raise Error("Error while removing server {}!".format(SOCKETNAME))
raise Error("Error while removing server {}!".format(
self._socketname))
@pyqtSlot(int)
def on_error(self, error):
@ -223,23 +240,23 @@ def _socket_error(action, socket):
action, socket.errorString(), socket.error()))
def send_to_running_instance(cmdlist):
def send_to_running_instance(args):
"""Try to send a commandline to a running instance.
Blocks for CONNECT_TIMEOUT ms.
Args:
cmdlist: A list to send (URLs/commands)
args: The argparse namespace.
Return:
True if connecting was successful, False if no connection was made.
"""
socket = QLocalSocket()
socket.connectToServer(SOCKETNAME)
socket.connectToServer(_get_socketname(args))
connected = socket.waitForConnected(100)
if connected:
log.ipc.info("Opening in existing instance")
json_data = {'args': cmdlist}
json_data = {'args': args.command}
try:
cwd = os.getcwd()
except OSError:

View File

@ -52,6 +52,8 @@ def get_argparser():
"no data storage).")
parser.add_argument('--cachedir', help="Set cache directory (empty for "
"no cache storage).")
parser.add_argument('--basedir', help="Base directory for all storage. "
"Other --*dir arguments are ignored if this is given.")
parser.add_argument('-V', '--version', help="Show version and quit.",
action='store_true')
parser.add_argument('-s', '--set', help="Set a temporary setting for "

View File

@ -84,8 +84,22 @@ def _from_args(typ, args):
QStandardPaths.DataLocation: 'datadir',
QStandardPaths.CacheLocation: 'cachedir',
}
basedir_suffix = {
QStandardPaths.ConfigLocation: 'config',
QStandardPaths.DataLocation: 'data',
QStandardPaths.CacheLocation: 'cache',
QStandardPaths.DownloadLocation: 'download',
QStandardPaths.RuntimeLocation: 'runtime',
}
if args is None:
return (False, None)
if getattr(args, 'basedir', None) is not None:
basedir = args.basedir
suffix = basedir_suffix[typ]
return (True, os.path.join(basedir, suffix))
try:
argname = typ_to_argparse_arg[typ]
except KeyError:

View File

@ -154,3 +154,13 @@ class TestArguments:
datadir=testcase.arg)
standarddir.init(args)
assert standarddir.data() == testcase.expected
@pytest.mark.parametrize('typ', ['config', 'data', 'cache', 'download',
'runtime'])
def test_basedir(self, tmpdir, typ):
"""Test --basedir."""
expected = str(tmpdir / typ)
args = types.SimpleNamespace(basedir=str(tmpdir))
standarddir.init(args)
func = getattr(standarddir, typ)
assert func() == expected