Repository: qutebrowser/qutebrowser

Issue:
Include data/config/cache/runtime paths in qute:version output
The `qute:version` command already includes a great deal of useful runtime information. It would help users wanting to use userscripts if `qute:version` output included the userscript directory path.

In that way the userscripts documentation would not have to explicitly list the userscripts directories for all platforms, but could instead direct users to the `qute:version` command.



Discussion:
FWIW `qute:version` probably should show some other useful paths while we're at it :wink:

@dnebauer Do you think it'd suffice to display the config/data/cache/runtime directories? With that, "it needs to be stored in your data directory" should be clear enough, no?

That should do it.


Resolving patch:
diff --git a/qutebrowser/app.py b/qutebrowser/app.py
--- a/qutebrowser/app.py
+++ b/qutebrowser/app.py
@@ -62,10 +62,6 @@
 
 def run(args):
     """Initialize everything and run the application."""
-    if args.version:
-        print(version.version())
-        sys.exit(usertypes.Exit.ok)
-
     if args.temp_basedir:
         args.basedir = tempfile.mkdtemp(prefix='qutebrowser-basedir-')
 
@@ -79,6 +75,13 @@ def run(args):
     qApp.setApplicationVersion(qutebrowser.__version__)
     qApp.lastWindowClosed.connect(quitter.on_last_window_closed)
 
+    log.init.debug("Initializing directories...")
+    standarddir.init(args)
+
+    if args.version:
+        print(version.version())
+        sys.exit(usertypes.Exit.ok)
+
     crash_handler = crashsignal.CrashHandler(
         app=qApp, quitter=quitter, args=args, parent=qApp)
     crash_handler.activate()
@@ -378,9 +381,6 @@ def _init_modules(args, crash_handler):
     readline_bridge = readline.ReadlineBridge()
     objreg.register('readline-bridge', readline_bridge)
 
-    log.init.debug("Initializing directories...")
-    standarddir.init(args)
-
     log.init.debug("Initializing config...")
     config.init(qApp)
     save_manager.init_autosave()
diff --git a/qutebrowser/utils/version.py b/qutebrowser/utils/version.py
--- a/qutebrowser/utils/version.py
+++ b/qutebrowser/utils/version.py
@@ -38,7 +38,7 @@
     qWebKitVersion = None
 
 import qutebrowser
-from qutebrowser.utils import log, utils
+from qutebrowser.utils import log, utils, standarddir
 from qutebrowser.browser import pdfjs
 
 
@@ -152,6 +152,22 @@ def _module_versions():
     return lines
 
 
+def _path_info():
+    """Get info about important path names.
+
+    Return:
+        A dictionary of descriptive to actual path names.
+    """
+    return {
+        'config': standarddir.config(),
+        'data': standarddir.data(),
+        'system_data': standarddir.system_data(),
+        'cache': standarddir.cache(),
+        'download': standarddir.download(),
+        'runtime': standarddir.runtime(),
+    }
+
+
 def _os_info():
     """Get operating system info.
 
@@ -255,4 +271,12 @@ def version():
         "Imported from {}".format(importpath),
     ]
     lines += _os_info()
+
+    lines += [
+        '',
+        'Paths',
+    ]
+    for name, path in _path_info().items():
+        lines += ['{}: {}'.format(name, path)]
+
     return '\n'.join(lines)


Test patch:
diff --git a/tests/end2end/test_invocations.py b/tests/end2end/test_invocations.py
--- a/tests/end2end/test_invocations.py
+++ b/tests/end2end/test_invocations.py
@@ -21,9 +21,15 @@
 
 import sys
 import logging
+import re
 
 import pytest
 
+from PyQt5.QtCore import QProcess
+
+from end2end.fixtures.quteprocess import QuteProc
+from end2end.fixtures.testprocess import ProcessExited
+
 
 def _base_args(config):
     """Get the arguments to pass with every invocation."""
@@ -177,3 +183,24 @@ def test_optimize(request, quteproc_new, capfd, level):
     # Waiting for quit to make sure no other warning is emitted
     quteproc_new.send_cmd(':quit')
     quteproc_new.wait_for_quit()
+
+
+def test_version(request):
+    """Test invocation with --version argument."""
+    args = ['--version'] + _base_args(request.config)
+    # can't use quteproc_new here because it's confused by
+    # early process termination
+    proc = QuteProc(request)
+    proc.proc.setProcessChannelMode(QProcess.SeparateChannels)
+
+    try:
+        proc.start(args)
+        proc.wait_for_quit()
+    except ProcessExited:
+        assert proc.proc.exitStatus() == QProcess.NormalExit
+    else:
+        assert False
+
+    output = bytes(proc.proc.readAllStandardOutput()).decode('utf-8')
+
+    assert re.search(r'^qutebrowser\s+v\d+(\.\d+)', output) is not None
diff --git a/tests/unit/utils/test_version.py b/tests/unit/utils/test_version.py
--- a/tests/unit/utils/test_version.py
+++ b/tests/unit/utils/test_version.py
@@ -313,6 +313,30 @@ def test_release_info(files, expected, caplog, monkeypatch):
         assert caplog.records[0].message == "Error while reading fake-file."
 
 
+def test_path_info(monkeypatch):
+    """Test _path_info()."""
+    patches = {
+        'config': lambda: 'CONFIG PATH',
+        'data': lambda: 'DATA PATH',
+        'system_data': lambda: 'SYSTEM DATA PATH',
+        'cache': lambda: 'CACHE PATH',
+        'download': lambda: 'DOWNLOAD PATH',
+        'runtime': lambda: 'RUNTIME PATH',
+    }
+
+    for attr, val in patches.items():
+        monkeypatch.setattr('qutebrowser.utils.standarddir.' + attr, val)
+
+    pathinfo = version._path_info()
+
+    assert pathinfo['config'] == 'CONFIG PATH'
+    assert pathinfo['data'] == 'DATA PATH'
+    assert pathinfo['system_data'] == 'SYSTEM DATA PATH'
+    assert pathinfo['cache'] == 'CACHE PATH'
+    assert pathinfo['download'] == 'DOWNLOAD PATH'
+    assert pathinfo['runtime'] == 'RUNTIME PATH'
+
+
 class ImportFake:
 
     """A fake for __import__ which is used by the import_fake fixture.
@@ -644,6 +668,7 @@ def test_version_output(git_commit, frozen, style, equal_qt, with_webkit,
         'platform.platform': lambda: 'PLATFORM',
         'platform.architecture': lambda: ('ARCHITECTURE', ''),
         '_os_info': lambda: ['OS INFO 1', 'OS INFO 2'],
+        '_path_info': lambda: {'PATH DESC': 'PATH NAME'},
         'QApplication': (stubs.FakeQApplication(style='STYLE') if style else
                          stubs.FakeQApplication(instance=None)),
     }
@@ -674,6 +699,9 @@ def test_version_output(git_commit, frozen, style, equal_qt, with_webkit,
         Imported from {import_path}
         OS INFO 1
         OS INFO 2
+
+        Paths
+        PATH DESC: PATH NAME
     """.lstrip('\n'))
 
     substitutions = {