From 5ea420b49b58aee4a42f7cd79fe9b14f1f87c159 Mon Sep 17 00:00:00 2001 From: Ryan Roden-Corrent Date: Tue, 8 Aug 2017 10:55:03 -0400 Subject: [PATCH] Fix startup crashes after config merge. Get qutebrowser to the point where it can at least start - Declare _messages earlier in MessageView.__init__ so it is set before the config trigger tries to access it. - Remove unused configmodel completion functions - Move bind completion to configmodel to avoid a circular import with the config module - Fix some config accesses (forgot to use .val) - Fix old Completion.CompletionKind references --- qutebrowser/app.py | 6 +- qutebrowser/browser/webkit/webkittab.py | 1 + qutebrowser/completion/models/configmodel.py | 89 ++++++++----------- qutebrowser/completion/models/histcategory.py | 4 +- qutebrowser/completion/models/miscmodels.py | 22 +---- qutebrowser/config/config.py | 3 +- qutebrowser/mainwindow/mainwindow.py | 2 +- qutebrowser/mainwindow/messageview.py | 2 +- tests/helpers/stubs.py | 4 +- tests/unit/completion/test_models.py | 67 ++------------ 10 files changed, 54 insertions(+), 146 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index d1aafce19..b74c7e958 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -44,7 +44,6 @@ import qutebrowser.resources from qutebrowser.completion.models import miscmodels from qutebrowser.commands import cmdutils, runners, cmdexc from qutebrowser.config import config, websettings, configexc -from qutebrowser.config.parsers import keyconf from qutebrowser.browser import (urlmarks, adblock, history, browsertab, downloads) from qutebrowser.browser.network import proxy @@ -412,9 +411,6 @@ def _init_modules(args, crash_handler): config.init(qApp) save_manager.init_autosave() - log.init.debug("Initializing keys...") - keyconf.init(qApp) - log.init.debug("Initializing sql...") try: sql.init(os.path.join(standarddir.data(), 'history.sqlite')) @@ -631,7 +627,7 @@ class Quitter: return True @cmdutils.register(instance='quitter', name='quit') - @cmdutils.argument('session', completion=usertypes.Completion.sessions) + @cmdutils.argument('session', completion=miscmodels.session) def quit(self, save=False, session=None): """Quit qutebrowser. diff --git a/qutebrowser/browser/webkit/webkittab.py b/qutebrowser/browser/webkit/webkittab.py index 005c1ac96..cd040e5d2 100644 --- a/qutebrowser/browser/webkit/webkittab.py +++ b/qutebrowser/browser/webkit/webkittab.py @@ -33,6 +33,7 @@ from PyQt5.QtPrintSupport import QPrinter from qutebrowser.browser import browsertab from qutebrowser.browser.webkit import webview, tabhistory, webkitelem +from qutebrowser.browser.network import proxy from qutebrowser.utils import qtutils, objreg, usertypes, utils, log, debug diff --git a/qutebrowser/completion/models/configmodel.py b/qutebrowser/completion/models/configmodel.py index 663a0b7f7..3ea7e18ab 100644 --- a/qutebrowser/completion/models/configmodel.py +++ b/qutebrowser/completion/models/configmodel.py @@ -19,78 +19,61 @@ """Functions that return config-related completion models.""" -from qutebrowser.config import configdata, configexc +from qutebrowser.config import configdata, configexc, config from qutebrowser.completion.models import completionmodel, listcategory from qutebrowser.utils import objreg +from qutebrowser.commands import cmdutils -def section(): - """A CompletionModel filled with settings sections.""" - model = completionmodel.CompletionModel(column_widths=(20, 70, 10)) - sections = ((name, configdata.SECTION_DESC[name].splitlines()[0].strip()) - for name in configdata.DATA) - model.add_category(listcategory.ListCategory("Sections", sections)) +def option(): + """A CompletionModel filled with settings and their descriptions.""" + model = completionmodel.CompletionModel(column_widths=(30, 70, 0)) + options = [(x.name, x.description) for x in configdata.DATA.values()] + model.add_category(listcategory.ListCategory("Options", options)) return model -def option(sectname): - """A CompletionModel filled with settings and their descriptions. - - Args: - sectname: The name of the config section this model shows. - """ - model = completionmodel.CompletionModel(column_widths=(20, 70, 10)) - try: - sectdata = configdata.DATA[sectname] - except KeyError: - return None - options = [] - for name in sectdata: - try: - desc = sectdata.descriptions[name] - except (KeyError, AttributeError): - # Some stuff (especially ValueList items) don't have a - # description. - desc = "" - else: - desc = desc.splitlines()[0] - config = objreg.get('config') - val = config.get(sectname, name, raw=True) - options.append((name, desc, val)) - model.add_category(listcategory.ListCategory(sectname, options)) - return model - - -def value(sectname, optname): +def value(optname, *values): """A CompletionModel filled with setting values. Args: - sectname: The name of the config section this model shows. optname: The name of the config option this model shows. + values: The values already provided on the command line. """ - model = completionmodel.CompletionModel(column_widths=(20, 70, 10)) - config = objreg.get('config') + model = completionmodel.CompletionModel(column_widths=(30, 70, 0)) try: - current = config.get(sectname, optname, raw=True) or '""' - except (configexc.NoSectionError, configexc.NoOptionError): + current = str(config.instance.get(optname) or '""') + except configexc.NoOptionError: return None - default = configdata.DATA[sectname][optname].default() or '""' - - if hasattr(configdata.DATA[sectname], 'valtype'): - # Same type for all values (ValueList) - vals = configdata.DATA[sectname].valtype.complete() - else: - if optname is None: - raise ValueError("optname may only be None for ValueList " - "sections, but {} is not!".format(sectname)) - # Different type for each value (KeyValue) - vals = configdata.DATA[sectname][optname].typ.complete() - + opt = configdata.DATA[optname] + default = str(opt.default or '""') cur_cat = listcategory.ListCategory("Current/Default", [(current, "Current value"), (default, "Default value")]) model.add_category(cur_cat) + + vals = opt.typ.complete() if vals is not None: model.add_category(listcategory.ListCategory("Completions", vals)) return model + + +def bind(key): + """A CompletionModel filled with all bindable commands and descriptions. + + Args: + key: the key being bound. + """ + model = completionmodel.CompletionModel(column_widths=(20, 60, 20)) + cmd_text = objreg.get('key-config').get_bindings_for('normal').get(key) + + if cmd_text: + cmd_name = cmd_text.split(' ')[0] + cmd = cmdutils.cmd_dict.get(cmd_name) + data = [(cmd_text, cmd.desc, key)] + model.add_category(listcategory.ListCategory("Current", data)) + + #cmdlist = _get_cmd_completions(include_hidden=True, include_aliases=True) + #model.add_category(listcategory.ListCategory("Commands", cmdlist)) + return model diff --git a/qutebrowser/completion/models/histcategory.py b/qutebrowser/completion/models/histcategory.py index 606351440..426cf907c 100644 --- a/qutebrowser/completion/models/histcategory.py +++ b/qutebrowser/completion/models/histcategory.py @@ -39,7 +39,7 @@ class HistoryCategory(QSqlQueryModel): # replace ' in timestamp-format to avoid breaking the query timefmt = ("strftime('{}', last_atime, 'unixepoch', 'localtime')" - .format(config.get('completion', 'timestamp-format') + .format(config.val.completion.timestamp_format .replace("'", "`"))) self._query = sql.Query(' '.join([ @@ -58,7 +58,7 @@ class HistoryCategory(QSqlQueryModel): def _atime_expr(self): """If max_items is set, return an expression to limit the query.""" - max_items = config.get('completion', 'web-history-max-items') + max_items = config.val.completion.web_history_max_items # HistoryCategory should not be added to the completion in that case. assert max_items != 0 diff --git a/qutebrowser/completion/models/miscmodels.py b/qutebrowser/completion/models/miscmodels.py index 0e55e6696..c8b1cedaf 100644 --- a/qutebrowser/completion/models/miscmodels.py +++ b/qutebrowser/completion/models/miscmodels.py @@ -21,8 +21,8 @@ from qutebrowser.config import config, configdata from qutebrowser.utils import objreg, log -from qutebrowser.commands import cmdutils from qutebrowser.completion.models import completionmodel, listcategory +from qutebrowser.commands import cmdutils def command(): @@ -135,26 +135,6 @@ def buffer(): return model -def bind(key): - """A CompletionModel filled with all bindable commands and descriptions. - - Args: - key: the key being bound. - """ - model = completionmodel.CompletionModel(column_widths=(20, 60, 20)) - cmd_text = objreg.get('key-config').get_bindings_for('normal').get(key) - - if cmd_text: - cmd_name = cmd_text.split(' ')[0] - cmd = cmdutils.cmd_dict.get(cmd_name) - data = [(cmd_text, cmd.desc, key)] - model.add_category(listcategory.ListCategory("Current", data)) - - cmdlist = _get_cmd_completions(include_hidden=True, include_aliases=True) - model.add_category(listcategory.ListCategory("Commands", cmdlist)) - return model - - def _get_cmd_completions(include_hidden, include_aliases, prefix=''): """Get a list of completions info for commands, sorted by name. diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index a99f610b4..155f3e0de 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -230,6 +230,7 @@ class ConfigCommands: @cmdutils.register(instance='config-commands', star_args_optional=True) @cmdutils.argument('option', completion=configmodel.option) + @cmdutils.argument('values', completion=configmodel.value) @cmdutils.argument('win_id', win_id=True) def set(self, win_id, option=None, *values, temp=False, print_=False): """Set an option. @@ -310,7 +311,7 @@ class ConfigCommands: @cmdutils.register(instance='config-commands', maxsplit=1, no_cmd_split=True, no_replace_variables=True) - @cmdutils.argument('command', completion=usertypes.Completion.bind) + @cmdutils.argument('command', completion=configmodel.bind) def bind(self, key, command=None, *, mode='normal', force=False): """Bind a key to a command. diff --git a/qutebrowser/mainwindow/mainwindow.py b/qutebrowser/mainwindow/mainwindow.py index 465c1e13d..77ed03b36 100644 --- a/qutebrowser/mainwindow/mainwindow.py +++ b/qutebrowser/mainwindow/mainwindow.py @@ -30,7 +30,7 @@ from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QSizePolicy from qutebrowser.commands import runners, cmdutils from qutebrowser.config import config from qutebrowser.utils import (message, log, usertypes, qtutils, objreg, utils, - jinja) + jinja, debug) from qutebrowser.mainwindow import tabbedbrowser, messageview, prompt from qutebrowser.mainwindow.statusbar import bar from qutebrowser.completion import completionwidget, completer diff --git a/qutebrowser/mainwindow/messageview.py b/qutebrowser/mainwindow/messageview.py index e83efda0a..396cc1fb1 100644 --- a/qutebrowser/mainwindow/messageview.py +++ b/qutebrowser/mainwindow/messageview.py @@ -77,6 +77,7 @@ class MessageView(QWidget): def __init__(self, parent=None): super().__init__(parent) + self._messages = [] self._vbox = QVBoxLayout(self) self._vbox.setContentsMargins(0, 0, 0, 0) self._vbox.setSpacing(0) @@ -88,7 +89,6 @@ class MessageView(QWidget): config.instance.changed.connect(self._set_clear_timer_interval) self._last_text = None - self._messages = [] def sizeHint(self): """Get the proposed height for the view.""" diff --git a/tests/helpers/stubs.py b/tests/helpers/stubs.py index 8f8cd66bb..e7b389792 100644 --- a/tests/helpers/stubs.py +++ b/tests/helpers/stubs.py @@ -21,7 +21,6 @@ """Fake objects/stubs.""" -import collections from unittest import mock from PyQt5.QtCore import pyqtSignal, QPoint, QProcess, QObject @@ -30,8 +29,7 @@ from PyQt5.QtNetwork import (QNetworkRequest, QAbstractNetworkCache, from PyQt5.QtWidgets import QCommonStyle, QLineEdit, QWidget, QTabBar from qutebrowser.browser import browsertab -from qutebrowser.config import configexc -from qutebrowser.utils import usertypes, utils +from qutebrowser.utils import usertypes from qutebrowser.mainwindow import mainwindow diff --git a/tests/unit/completion/test_models.py b/tests/unit/completion/test_models.py index 2c9607cbd..bc2f276af 100644 --- a/tests/unit/completion/test_models.py +++ b/tests/unit/completion/test_models.py @@ -29,7 +29,7 @@ import pytest from PyQt5.QtCore import QUrl from qutebrowser.completion.models import miscmodels, urlmodel, configmodel -from qutebrowser.config import sections, value +from qutebrowser.config import sections, value, configdata, configtype from qutebrowser.utils import objreg @@ -76,7 +76,8 @@ def _patch_cmdutils(monkeypatch, stubs, symbol): def _patch_configdata(monkeypatch, stubs, symbol): """Patch the configdata module to provide fake data.""" data = collections.OrderedDict([ - ('general', sections.KeyValue( + ('general.time', configdata.Option( + name='general.time' ('time', value.SettingValue(stubs.FakeConfigType('fast', 'slow'), default='slow'), @@ -141,8 +142,8 @@ def bookmarks(bookmark_manager_stub): @pytest.fixture def web_history(init_sql, stubs, config_stub): """Fixture which provides a web-history object.""" - config_stub.data['completion'] = {'timestamp-format': '%Y-%m-%d', - 'web-history-max-items': -1} + config_stub.completion.timestamp_format = '%Y-%m-%d' + config_stub.completion.web_history_max_items = -1 stub = history.WebHistory() objreg.register('web-history', stub) yield stub @@ -182,7 +183,7 @@ def test_command_completion(qtmodeltester, monkeypatch, stubs, config_stub, """ _patch_cmdutils(monkeypatch, stubs, 'qutebrowser.completion.models.miscmodels.cmdutils') - config_stub.data['aliases'] = {'rock': 'roll'} + config_stub.aliases = {'rock': 'roll'} key_config_stub.set_bindings_for('normal', {'s': 'stop', 'rr': 'roll', 'ro': 'rock'}) @@ -443,7 +444,7 @@ def test_url_completion_delete_history(qtmodeltester, def test_url_completion_zero_limit(config_stub, web_history, quickmarks, bookmarks): """Make sure there's no history if the limit was set to zero.""" - config_stub.data['completion']['web-history-max-items'] = 0 + config_stub.completion.web_history_max_items = 0 model = urlmodel.url() model.set_pattern('') category = model.index(2, 0) # "History" normally @@ -520,32 +521,9 @@ def test_tab_completion_delete(qtmodeltester, fake_web_tab, app_stub, QUrl('https://duckduckgo.com')] -def test_setting_section_completion(qtmodeltester, monkeypatch, stubs): +def test_setting_option_completion(qtmodeltester, monkeypatch, stubs): module = 'qutebrowser.completion.models.configmodel' _patch_configdata(monkeypatch, stubs, module + '.configdata.DATA') - _patch_config_section_desc(monkeypatch, stubs, - module + '.configdata.SECTION_DESC') - model = configmodel.section() - model.set_pattern('') - qtmodeltester.data_display_may_return_none = True - qtmodeltester.check(model) - - _check_completions(model, { - "Sections": [ - ('general', 'General/miscellaneous options.', None), - ('searchengines', 'Definitions of search engines ...', None), - ('ui', 'General options related to the user interface.', None), - ] - }) - - -def test_setting_option_completion(qtmodeltester, monkeypatch, stubs, - config_stub): - module = 'qutebrowser.completion.models.configmodel' - _patch_configdata(monkeypatch, stubs, module + '.configdata.DATA') - config_stub.data = {'ui': {'gesture': 'off', - 'mind': 'on', - 'voice': 'sometimes'}} model = configmodel.option('ui') model.set_pattern('') qtmodeltester.data_display_may_return_none = True @@ -585,35 +563,6 @@ def test_setting_option_completion_valuelist(qtmodeltester, monkeypatch, stubs, }) -def test_setting_value_completion(qtmodeltester, monkeypatch, stubs, - config_stub): - module = 'qutebrowser.completion.models.configmodel' - _patch_configdata(monkeypatch, stubs, module + '.configdata.DATA') - config_stub.data = {'general': {'volume': '0'}} - model = configmodel.value('general', 'volume') - model.set_pattern('') - qtmodeltester.data_display_may_return_none = True - qtmodeltester.check(model) - - _check_completions(model, { - "Current/Default": [ - ('0', 'Current value', None), - ('11', 'Default value', None), - ], - "Completions": [ - ('0', '', None), - ('11', '', None), - ] - }) - - -def test_setting_value_completion_empty(monkeypatch, stubs, config_stub): - module = 'qutebrowser.completion.models.configmodel' - _patch_configdata(monkeypatch, stubs, module + '.configdata.DATA') - config_stub.data = {'general': {}} - assert configmodel.value('general', 'typo') is None - - def test_bind_completion(qtmodeltester, monkeypatch, stubs, config_stub, key_config_stub): """Test the results of keybinding command completion.