diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 53efcfdf2..e9d6b8b68 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -37,7 +37,7 @@ from qutebrowser.keyinput import modeman, keyutils from qutebrowser.utils import (message, usertypes, log, qtutils, urlutils, objreg, utils, standarddir) from qutebrowser.utils.usertypes import KeyMode -from qutebrowser.misc import editor, guiprocess +from qutebrowser.misc import editor, guiprocess, objects from qutebrowser.completion.models import urlmodel, miscmodels from qutebrowser.mainwindow import mainwindow @@ -1612,7 +1612,7 @@ class CommandDispatcher: path = 'index.html' elif topic.startswith(':'): command = topic[1:] - if command not in cmdutils.cmd_dict: + if command not in objects.commands: raise cmdexc.CommandError("Invalid command {}!".format( command)) path = 'commands.html#{}'.format(command) diff --git a/qutebrowser/commands/cmdutils.py b/qutebrowser/commands/cmdutils.py index 41e875202..12ffabe3a 100644 --- a/qutebrowser/commands/cmdutils.py +++ b/qutebrowser/commands/cmdutils.py @@ -22,11 +22,10 @@ import inspect import typing # pylint: disable=unused-import +from qutebrowser.misc import objects from qutebrowser.utils import qtutils, log from qutebrowser.commands import command, cmdexc -cmd_dict = {} # type: typing.Dict[str, command.Command] - def check_overflow(arg, ctype): """Check if the given argument is in bounds for the given type. @@ -89,7 +88,7 @@ class register: # noqa: N801,N806 pylint: disable=invalid-name Gets called when a function should be decorated. Doesn't actually decorate anything, but creates a Command object and - registers it in the cmd_dict. + registers it in the global commands dict. Args: func: The function to be decorated. @@ -104,11 +103,11 @@ class register: # noqa: N801,N806 pylint: disable=invalid-name name = self._name log.commands.vdebug("Registering command {} (from {}:{})".format( name, func.__module__, func.__qualname__)) - if name in cmd_dict: + if name in objects.commands: raise ValueError("{} is already registered!".format(name)) cmd = command.Command(name=name, instance=self._instance, handler=func, **self._kwargs) - cmd_dict[name] = cmd + objects.commands[name] = cmd return func diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py index f1c7641e7..86b0cc3a2 100644 --- a/qutebrowser/commands/runners.py +++ b/qutebrowser/commands/runners.py @@ -26,9 +26,9 @@ import attr from PyQt5.QtCore import pyqtSlot, QUrl, QObject from qutebrowser.config import config -from qutebrowser.commands import cmdexc, cmdutils +from qutebrowser.commands import cmdexc from qutebrowser.utils import message, objreg, qtutils, usertypes, utils -from qutebrowser.misc import split +from qutebrowser.misc import split, objects last_command = {} @@ -193,7 +193,7 @@ class CommandParser: cmdstr = self._completion_match(cmdstr) try: - cmd = cmdutils.cmd_dict[cmdstr] + cmd = objects.commands[cmdstr] except KeyError: if not fallback: raise cmdexc.NoSuchCommandError( @@ -220,7 +220,7 @@ class CommandParser: Return: cmdstr modified to the matching completion or unmodified """ - matches = [cmd for cmd in sorted(cmdutils.cmd_dict, key=len) + matches = [cmd for cmd in sorted(objects.commands, key=len) if cmdstr in cmd] if len(matches) == 1: cmdstr = matches[0] diff --git a/qutebrowser/completion/completer.py b/qutebrowser/completion/completer.py index 4cbdc4724..ea64225d5 100644 --- a/qutebrowser/completion/completer.py +++ b/qutebrowser/completion/completer.py @@ -23,7 +23,8 @@ import attr from PyQt5.QtCore import pyqtSlot, QObject, QTimer from qutebrowser.config import config -from qutebrowser.commands import cmdutils, runners +from qutebrowser.commands import runners +from qutebrowser.misc import objects from qutebrowser.utils import log, utils, debug from qutebrowser.completion.models import miscmodels @@ -92,7 +93,7 @@ class Completer(QObject): log.completion.debug('Starting command completion') return miscmodels.command try: - cmd = cmdutils.cmd_dict[before_cursor[0]] + cmd = objects.commands[before_cursor[0]] except KeyError: log.completion.debug("No completion for unknown command: {}" .format(before_cursor[0])) @@ -170,7 +171,7 @@ class Completer(QObject): before, center, after = self._partition() log.completion.debug("Changing {} to '{}'".format(center, text)) try: - maxsplit = cmdutils.cmd_dict[before[0]].maxsplit + maxsplit = objects.commands[before[0]].maxsplit except (KeyError, IndexError): maxsplit = None if maxsplit is None: diff --git a/qutebrowser/completion/models/util.py b/qutebrowser/completion/models/util.py index c1b8b56f9..08f99eb6c 100644 --- a/qutebrowser/completion/models/util.py +++ b/qutebrowser/completion/models/util.py @@ -20,7 +20,7 @@ """Utility functions for completion models.""" from qutebrowser.utils import objreg, usertypes -from qutebrowser.commands import cmdutils +from qutebrowser.misc import objects def get_cmd_completions(info, include_hidden, include_aliases, prefix=''): @@ -34,10 +34,10 @@ def get_cmd_completions(info, include_hidden, include_aliases, prefix=''): Return: A list of tuples of form (name, description, bindings). """ - assert cmdutils.cmd_dict + assert objects.commands cmdlist = [] cmd_to_keys = info.keyconf.get_reverse_bindings_for('normal') - for obj in set(cmdutils.cmd_dict.values()): + for obj in set(objects.commands.values()): hide_debug = obj.debug and not objreg.get('args').debug hide_mode = (usertypes.KeyMode.normal not in obj.modes and not include_hidden) diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index 31eca988e..691584801 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -60,7 +60,7 @@ from PyQt5.QtCore import QUrl, Qt from PyQt5.QtGui import QColor, QFont from PyQt5.QtWidgets import QTabWidget, QTabBar -from qutebrowser.commands import cmdutils +from qutebrowser.misc import objects from qutebrowser.config import configexc, configutils from qutebrowser.utils import standarddir, utils, qtutils, urlutils, urlmatch from qutebrowser.keyinput import keyutils @@ -881,7 +881,7 @@ class Command(BaseType): def complete(self): out = [] - for cmdname, obj in cmdutils.cmd_dict.items(): + for cmdname, obj in objects.commands.items(): out.append((cmdname, obj.desc)) return out diff --git a/qutebrowser/misc/keyhintwidget.py b/qutebrowser/misc/keyhintwidget.py index e1de9a6cc..9d3f4c594 100644 --- a/qutebrowser/misc/keyhintwidget.py +++ b/qutebrowser/misc/keyhintwidget.py @@ -33,7 +33,7 @@ from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt from qutebrowser.config import config from qutebrowser.utils import utils, usertypes -from qutebrowser.commands import cmdutils +from qutebrowser.misc import objects from qutebrowser.keyinput import keyutils @@ -101,7 +101,7 @@ class KeyHintView(QLabel): def takes_count(cmdstr): """Return true iff this command can take a count argument.""" cmdname = cmdstr.split(' ')[0] - cmd = cmdutils.cmd_dict.get(cmdname) + cmd = objects.commands.get(cmdname) return cmd and cmd.takes_count() bindings_dict = config.key_instance.get_bindings_for(modename) diff --git a/qutebrowser/misc/objects.py b/qutebrowser/misc/objects.py index ec558aa37..0bb26954c 100644 --- a/qutebrowser/misc/objects.py +++ b/qutebrowser/misc/objects.py @@ -28,6 +28,7 @@ MYPY = False if MYPY: # pylint: disable=unused-import,useless-suppression from qutebrowser.utils import usertypes + from qutebrowser.commands import command class NoBackend: @@ -39,3 +40,4 @@ class NoBackend: backend = NoBackend() # type: typing.Union[usertypes.Backend, NoBackend] +commands = {} # type: typing.Dict[str, command.Command] diff --git a/scripts/dev/run_vulture.py b/scripts/dev/run_vulture.py index b5c083546..f3217694e 100755 --- a/scripts/dev/run_vulture.py +++ b/scripts/dev/run_vulture.py @@ -30,7 +30,7 @@ import argparse import vulture import qutebrowser.app # pylint: disable=unused-import -from qutebrowser.commands import cmdutils +from qutebrowser.misc import objects from qutebrowser.utils import utils from qutebrowser.browser.webkit import rfc6266 # To run the decorators from there @@ -44,7 +44,7 @@ from qutebrowser.config import configtypes def whitelist_generator(): # noqa """Generator which yields lines to add to a vulture whitelist.""" # qutebrowser commands - for cmd in cmdutils.cmd_dict.values(): + for cmd in objects.commands.values(): yield utils.qualname(cmd.handler) # pyPEG2 classes diff --git a/scripts/dev/src2asciidoc.py b/scripts/dev/src2asciidoc.py index 8c6f2e44d..ad1397d82 100755 --- a/scripts/dev/src2asciidoc.py +++ b/scripts/dev/src2asciidoc.py @@ -35,9 +35,10 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, # We import qutebrowser.app so all @cmdutils-register decorators are run. import qutebrowser.app from qutebrowser import qutebrowser, commands -from qutebrowser.commands import cmdutils, argparser +from qutebrowser.commands import argparser from qutebrowser.config import configdata, configtypes from qutebrowser.utils import docutils, usertypes +from qutebrowser.misc import objects from scripts import asciidoc2html, utils FILE_HEADER = """ @@ -350,7 +351,7 @@ def generate_commands(filename): normal_cmds = [] other_cmds = [] debug_cmds = [] - for name, cmd in cmdutils.cmd_dict.items(): + for name, cmd in objects.commands.items(): if cmd.deprecated: continue if usertypes.KeyMode.normal not in cmd.modes: diff --git a/tests/helpers/stubs.py b/tests/helpers/stubs.py index ed6fce36c..4abd58baf 100644 --- a/tests/helpers/stubs.py +++ b/tests/helpers/stubs.py @@ -326,14 +326,6 @@ class FakeSignal: """ -@attr.s -class FakeCmdUtils: - - """Stub for cmdutils which provides a cmd_dict.""" - - cmd_dict = attr.ib() - - @attr.s(frozen=True) class FakeCommand: diff --git a/tests/unit/commands/test_cmdutils.py b/tests/unit/commands/test_cmdutils.py index 718695874..4d7877e69 100644 --- a/tests/unit/commands/test_cmdutils.py +++ b/tests/unit/commands/test_cmdutils.py @@ -29,14 +29,14 @@ import enum import pytest -from qutebrowser.commands import cmdutils, cmdexc, argparser, command +from qutebrowser.misc import objects +from qutebrowser.commands import cmdexc, argparser, command, cmdutils from qutebrowser.utils import usertypes @pytest.fixture(autouse=True) def clear_globals(monkeypatch): - """Clear the cmdutils globals between each test.""" - monkeypatch.setattr(cmdutils, 'cmd_dict', {}) + monkeypatch.setattr(objects, 'commands', {}) def _get_cmd(*args, **kwargs): @@ -48,7 +48,7 @@ def _get_cmd(*args, **kwargs): @cmdutils.register(*args, **kwargs) def fun(): """Blah.""" - return cmdutils.cmd_dict['fun'] + return objects.commands['fun'] class TestCheckOverflow: @@ -83,10 +83,10 @@ class TestRegister: def fun(): """Blah.""" - cmd = cmdutils.cmd_dict['fun'] + cmd = objects.commands['fun'] assert cmd.handler is fun assert cmd.name == 'fun' - assert len(cmdutils.cmd_dict) == 1 + assert len(objects.commands) == 1 def test_underlines(self): """Make sure the function name is normalized correctly (_ -> -).""" @@ -94,8 +94,8 @@ class TestRegister: def eggs_bacon(): """Blah.""" - assert cmdutils.cmd_dict['eggs-bacon'].name == 'eggs-bacon' - assert 'eggs_bacon' not in cmdutils.cmd_dict + assert objects.commands['eggs-bacon'].name == 'eggs-bacon' + assert 'eggs_bacon' not in objects.commands def test_lowercasing(self): """Make sure the function name is normalized correctly (uppercase).""" @@ -103,8 +103,8 @@ class TestRegister: def Test(): # noqa: N801,N806 pylint: disable=invalid-name """Blah.""" - assert cmdutils.cmd_dict['test'].name == 'test' - assert 'Test' not in cmdutils.cmd_dict + assert objects.commands['test'].name == 'test' + assert 'Test' not in objects.commands def test_explicit_name(self): """Test register with explicit name.""" @@ -112,9 +112,9 @@ class TestRegister: def fun(): """Blah.""" - assert cmdutils.cmd_dict['foobar'].name == 'foobar' - assert 'fun' not in cmdutils.cmd_dict - assert len(cmdutils.cmd_dict) == 1 + assert objects.commands['foobar'].name == 'foobar' + assert 'fun' not in objects.commands + assert len(objects.commands) == 1 def test_multiple_registrations(self): """Make sure registering the same name twice raises ValueError.""" @@ -132,7 +132,7 @@ class TestRegister: @cmdutils.register(instance='foobar') def fun(self): """Blah.""" - assert cmdutils.cmd_dict['fun']._instance == 'foobar' + assert objects.commands['fun']._instance == 'foobar' def test_star_args(self): """Check handling of *args.""" @@ -140,7 +140,7 @@ class TestRegister: def fun(*args): """Blah.""" with pytest.raises(argparser.ArgumentParserError): - cmdutils.cmd_dict['fun'].parser.parse_args([]) + objects.commands['fun'].parser.parse_args([]) def test_star_args_optional(self): """Check handling of *args withstar_args_optional.""" @@ -148,7 +148,7 @@ class TestRegister: def fun(*args): """Blah.""" assert not args - cmd = cmdutils.cmd_dict['fun'] + cmd = objects.commands['fun'] cmd.namespace = cmd.parser.parse_args([]) args, kwargs = cmd._get_call_args(win_id=0) fun(*args, **kwargs) @@ -160,7 +160,7 @@ class TestRegister: def fun(arg=False): """Blah.""" assert arg == expected - cmd = cmdutils.cmd_dict['fun'] + cmd = objects.commands['fun'] cmd.namespace = cmd.parser.parse_args(inp) assert cmd.namespace.arg == expected @@ -170,7 +170,7 @@ class TestRegister: def fun(arg=False): """Blah.""" assert arg - cmd = cmdutils.cmd_dict['fun'] + cmd = objects.commands['fun'] with pytest.raises(argparser.ArgumentParserError): cmd.parser.parse_args(['-a']) @@ -192,14 +192,14 @@ class TestRegister: @cmdutils.argument('win_id', win_id=True) def fun(win_id): """Blah.""" - assert cmdutils.cmd_dict['fun']._get_call_args(42) == ([42], {}) + assert objects.commands['fun']._get_call_args(42) == ([42], {}) def test_count(self): @cmdutils.register() @cmdutils.argument('count', count=True) def fun(count=0): """Blah.""" - assert cmdutils.cmd_dict['fun']._get_call_args(42) == ([0], {}) + assert objects.commands['fun']._get_call_args(42) == ([0], {}) def test_count_without_default(self): with pytest.raises(TypeError, match="fun: handler has count parameter " @@ -216,7 +216,7 @@ class TestRegister: def fun(arg): """Blah.""" - pos_args = cmdutils.cmd_dict['fun'].pos_args + pos_args = objects.commands['fun'].pos_args if hide: assert pos_args == [] else: @@ -251,7 +251,7 @@ class TestRegister: """Blah.""" assert arg == expected - cmd = cmdutils.cmd_dict['fun'] + cmd = objects.commands['fun'] cmd.namespace = cmd.parser.parse_args([inp]) if expected is cmdexc.ArgumentTypeError: @@ -270,7 +270,7 @@ class TestRegister: def fun(arg): """Blah.""" - cmd = cmdutils.cmd_dict['fun'] + cmd = objects.commands['fun'] cmd.namespace = cmd.parser.parse_args(['fish']) with pytest.raises(cmdexc.ArgumentTypeError): @@ -283,7 +283,7 @@ class TestRegister: def fun(*, arg='foo'): """Blah.""" - cmd = cmdutils.cmd_dict['fun'] + cmd = objects.commands['fun'] cmd.namespace = cmd.parser.parse_args(['--arg=fish']) with pytest.raises(cmdexc.ArgumentTypeError): @@ -297,7 +297,7 @@ class TestRegister: def fun(foo, bar, opt=False): """Blah.""" - cmd = cmdutils.cmd_dict['fun'] + cmd = objects.commands['fun'] assert cmd.get_pos_arg_info(0) == command.ArgInfo(choices=('a', 'b')) assert cmd.get_pos_arg_info(1) == command.ArgInfo(choices=('x', 'y')) with pytest.raises(IndexError): @@ -422,6 +422,6 @@ class TestRun: monkeypatch.setattr(command.objects, 'backend', usertypes.Backend.QtWebKit) - cmd = cmdutils.cmd_dict['fun'] + cmd = objects.commands['fun'] with pytest.raises(cmdexc.PrerequisitesError, match=r'.* backend\.'): cmd.run(win_id=0) diff --git a/tests/unit/commands/test_runners.py b/tests/unit/commands/test_runners.py index db831bd7e..cd2dea1d4 100644 --- a/tests/unit/commands/test_runners.py +++ b/tests/unit/commands/test_runners.py @@ -21,7 +21,8 @@ import pytest -from qutebrowser.commands import runners, cmdexc, cmdutils +from qutebrowser.misc import objects +from qutebrowser.commands import runners, cmdexc class TestCommandParser: @@ -74,7 +75,7 @@ class TestCompletions: @pytest.fixture(autouse=True) def cmdutils_stub(self, monkeypatch, stubs): """Patch the cmdutils module to provide fake commands.""" - monkeypatch.setattr(cmdutils, 'cmd_dict', { + monkeypatch.setattr(objects, 'commands', { 'one': stubs.FakeCommand(name='one'), 'two': stubs.FakeCommand(name='two'), 'two-foo': stubs.FakeCommand(name='two-foo'), diff --git a/tests/unit/completion/test_completer.py b/tests/unit/completion/test_completer.py index 8620241e4..59291cdd6 100644 --- a/tests/unit/completion/test_completer.py +++ b/tests/unit/completion/test_completer.py @@ -129,7 +129,7 @@ def cmdutils_patch(monkeypatch, stubs, miscmodels_patch): def config_cycle(option, *values): """For testing varargs.""" - cmd_utils = stubs.FakeCmdUtils({ + commands = { 'set': command.Command(name='set', handler=set_command), 'help': command.Command(name='help', handler=show_help), 'open': command.Command(name='open', handler=openurl, maxsplit=0), @@ -137,8 +137,8 @@ def cmdutils_patch(monkeypatch, stubs, miscmodels_patch): 'tab-give': command.Command(name='tab-give', handler=tab_give), 'config-cycle': command.Command(name='config-cycle', handler=config_cycle), - }) - monkeypatch.setattr(completer, 'cmdutils', cmd_utils) + } + monkeypatch.setattr(completer.objects, 'commands', commands) def _set_cmd_prompt(cmd, txt): diff --git a/tests/unit/completion/test_models.py b/tests/unit/completion/test_models.py index 28265689c..9e75daae8 100644 --- a/tests/unit/completion/test_models.py +++ b/tests/unit/completion/test_models.py @@ -27,11 +27,11 @@ from datetime import datetime import pytest from PyQt5.QtCore import QUrl +from qutebrowser.misc import objects from qutebrowser.completion import completer from qutebrowser.completion.models import miscmodels, urlmodel, configmodel from qutebrowser.config import configdata, configtypes from qutebrowser.utils import usertypes -from qutebrowser.commands import cmdutils def _check_completions(model, expected): @@ -66,7 +66,7 @@ def _check_completions(model, expected): @pytest.fixture() def cmdutils_stub(monkeypatch, stubs): """Patch the cmdutils module to provide fake commands.""" - return monkeypatch.setattr(cmdutils, 'cmd_dict', { + return monkeypatch.setattr(objects, 'commands', { 'quit': stubs.FakeCommand(name='quit', desc='quit qutebrowser'), 'open': stubs.FakeCommand(name='open', desc='open a url'), 'prompt-yes': stubs.FakeCommand(name='prompt-yes', deprecated=True), diff --git a/tests/unit/config/test_configtypes.py b/tests/unit/config/test_configtypes.py index b2775de2f..a9a61e952 100644 --- a/tests/unit/config/test_configtypes.py +++ b/tests/unit/config/test_configtypes.py @@ -33,6 +33,7 @@ from PyQt5.QtCore import QUrl from PyQt5.QtGui import QColor, QFont from PyQt5.QtNetwork import QNetworkProxy +from qutebrowser.misc import objects from qutebrowser.config import configtypes, configexc, configutils from qutebrowser.utils import debug, utils, qtutils, urlmatch from qutebrowser.browser.network import pac @@ -1208,11 +1209,11 @@ class TestCommand: @pytest.fixture def patch_cmdutils(self, monkeypatch, stubs): """Patch the cmdutils module to provide fake commands.""" - cmd_utils = stubs.FakeCmdUtils({ + commands = { 'cmd1': stubs.FakeCommand(desc="desc 1"), - 'cmd2': stubs.FakeCommand(desc="desc 2")}) - monkeypatch.setattr(configtypes, 'cmdutils', cmd_utils) - monkeypatch.setattr('qutebrowser.commands.runners.cmdutils', cmd_utils) + 'cmd2': stubs.FakeCommand(desc="desc 2"), + } + monkeypatch.setattr(objects, 'commands', commands) @pytest.fixture def klass(self): diff --git a/tests/unit/misc/test_keyhints.py b/tests/unit/misc/test_keyhints.py index 7c9727b65..9af30fd16 100644 --- a/tests/unit/misc/test_keyhints.py +++ b/tests/unit/misc/test_keyhints.py @@ -21,6 +21,7 @@ import pytest +from qutebrowser.misc import objects from qutebrowser.misc.keyhintwidget import KeyHintView @@ -120,7 +121,7 @@ def test_suggestions_special(keyhint, config_stub): def test_suggestions_with_count(keyhint, config_stub, monkeypatch, stubs): """Test that a count prefix filters out commands that take no count.""" - monkeypatch.setattr('qutebrowser.commands.cmdutils.cmd_dict', { + monkeypatch.setattr(objects, 'commands', { 'foo': stubs.FakeCommand(name='foo', takes_count=lambda: False), 'bar': stubs.FakeCommand(name='bar', takes_count=lambda: True), })