Full read-write support for key config.

This commit is contained in:
Florian Bruhin 2014-09-09 21:40:16 +02:00
parent 414ab88a0e
commit e3d16f3bbe
10 changed files with 338 additions and 499 deletions

View File

@ -192,7 +192,8 @@ class Application(QApplication):
# We didn't really initialize much so far, so we just quit hard.
sys.exit(1)
try:
self.keyconfig = keyconfparser.KeyConfigParser(confdir, 'keys')
self.keyconfig = keyconfparser.KeyConfigParser(
confdir, 'keys.conf')
except keyconfparser.KeyConfigError as e:
log.init.exception(e)
errstr = "Error while reading key config:\n"
@ -730,7 +731,7 @@ class Application(QApplication):
# event loop, so we can shut down immediately.
self._shutdown(status)
def _shutdown(self, status):
def _shutdown(self, status): # noqa
"""Second stage of shutdown."""
log.destroy.debug("Stage 2 of shutting down...")
# Remove eventfilter
@ -745,7 +746,10 @@ class Application(QApplication):
if hasattr(self, 'config') and self.config is not None:
to_save = []
if self.config.get('general', 'auto-save-config'):
to_save.append(("config", self.config.save))
if hasattr(self, 'config'):
to_save.append(("config", self.config.save))
if hasattr(self, 'keyconfig'):
to_save.append(("keyconfig", self.keyconfig.save))
to_save += [("window geometry", self._save_geometry),
("quickmarks", quickmarks.save)]
if hasattr(self, 'cmd_history'):

View File

@ -26,7 +26,6 @@ we borrow some methods and classes from there where it makes sense.
import os
import os.path
import textwrap
import functools
import configparser
import collections.abc
@ -34,7 +33,7 @@ import collections.abc
from PyQt5.QtCore import pyqtSignal, QObject, QCoreApplication
from qutebrowser.utils import log
from qutebrowser.config import configdata, iniparsers, configtypes
from qutebrowser.config import configdata, iniparsers, configtypes, textwrapper
from qutebrowser.commands import cmdexc, cmdutils
from qutebrowser.utils import message
from qutebrowser.utils.usertypes import Completion
@ -89,7 +88,6 @@ class ConfigManager(QObject):
sections: The configuration data as an OrderedDict.
_fname: The filename to be opened.
_configparser: A ReadConfigParser instance to load the config.
_wrapper_args: A dict with the default kwargs for the config wrappers.
_configdir: The dictionary to read the config from and save it in.
_configfile: The config file path.
_interpolation: An configparser.Interpolation object
@ -113,12 +111,6 @@ class ConfigManager(QObject):
self.sections = configdata.DATA
self._configparser = iniparsers.ReadConfigParser(configdir, fname)
self._configfile = os.path.join(configdir, fname)
self._wrapper_args = {
'width': 72,
'replace_whitespace': False,
'break_long_words': False,
'break_on_hyphens': False,
}
self._configdir = configdir
self._fname = fname
self._interpolation = configparser.ExtendedInterpolation()
@ -146,9 +138,7 @@ class ConfigManager(QObject):
def _str_section_desc(self, sectname):
"""Get the section description string for sectname."""
wrapper = textwrap.TextWrapper(initial_indent='# ',
subsequent_indent='# ',
**self._wrapper_args)
wrapper = textwrapper.TextWrapper()
lines = []
seclines = configdata.SECTION_DESC[sectname].splitlines()
for secline in seclines:
@ -160,9 +150,8 @@ class ConfigManager(QObject):
def _str_option_desc(self, sectname, sect):
"""Get the option description strings for sect/sectname."""
wrapper = textwrap.TextWrapper(initial_indent='#' + ' ' * 5,
subsequent_indent='#' + ' ' * 5,
**self._wrapper_args)
wrapper = textwrapper.TextWrapper(initial_indent='#' + ' ' * 5,
subsequent_indent='#' + ' ' * 5)
lines = []
if not getattr(sect, 'descriptions', None):
return lines

View File

@ -780,7 +780,10 @@ DATA = collections.OrderedDict([
)),
])
KEYBINDINGS = """
KEY_FIRST_COMMENT = """
# vim: ft=conf
#
# Bindings from a key(chain) to a command.
#
# For special keys (can't be part of a keychain), enclose them in `<`...`>`.
@ -795,412 +798,187 @@ KEYBINDINGS = """
# with Shift. For special keys (with `<>`-signs), you need to explicitely add
# `Shift-` to match a key pressed with shift. You can bind multiple commands
# by separating them with `;;`.
[normal]
set-cmd-text ":open "
o
set-cmd-text ":open {url}"
go
set-cmd-text ":open -t "
O
set-cmd-text ":open -t {url}"
gO
set-cmd-text ":open -b "
xo
set-cmd-text ":open -b {url}"
xO
open -t about:blank
ga
tab-close
d
tab-only
co
tab-focus
T
tab-move
gm
tab-move -
gl
tab-move +
gr
tab-next
J
tab-prev
K
reload
r
back
H
forward
L
hint
f
hint all tab
F
hint all tab-bg
;b
hint images
;i
hint images tab
;I
hint images tab-bg
.i
hint links fill ":open {hint-url}"
;o
hint links fill ":open -t {hint-url}"
;O
hint links fill ":open -b {hint-url}"
.o
hint links yank
;y
hint links yank-primary
;Y
hint links rapid
;r
hint links download
;d
scroll -50 0
h
scroll 0 50
j
scroll 0 -50
k
scroll 50 0
l
undo
u
scroll-perc 0
gg
scroll-perc
G
search-next
n
search-prev
N
enter-mode insert
i
yank
yy
yank -s
yY
yank -t
yt
yank -ts
yT
paste
pp
paste -s
pP
paste -t
Pp
paste -ts
PP
quickmark-save
m
set-cmd-text ":quickmark-load "
b
set-cmd-text ":quickmark-load -t "
B
save
sf
set-cmd-text ":set "
ss
set-cmd-text ":set -t "
sl
set-cmd-text ":set keybind "
sk
zoom-out
-
zoom-in
+
zoom
=
prev-page
[[
next-page
]]
prev-page -t
{{
next-page -t
}}
inspector
wi
download-page
gd
cancel-download
ad
tab-focus last
<Ctrl-Tab>
enter-mode passthrough
<Ctrl-V>
quit
<Ctrl-Q>
undo
<Ctrl-Shift-T>
tab-close
<Ctrl-W>
open -t about:blank
<Ctrl-T>
scroll-page 0 1
<Ctrl-F>
scroll-page 0 -1
<Ctrl-B>
scroll-page 0 0.5
<Ctrl-D>
scroll-page 0 -0.5
<Ctrl-U>
tab-focus 1
<Alt-1>
tab-focus 2
<Alt-2>
tab-focus 3
<Alt-3>
tab-focus 4
<Alt-4>
tab-focus 5
<Alt-5>
tab-focus 6
<Alt-6>
tab-focus 7
<Alt-7>
tab-focus 8
<Alt-8>
tab-focus 9
<Alt-9>
back
<Backspace>
home
<Ctrl-h>
stop
<Ctrl-s>
print
<Ctrl-Alt-p>
[insert,hint,passthrough,command,prompt]
leave-mode
<Escape>
<Ctrl-N>
<Ctrl-[>
[passthrough]
# Keybindings for passthrough mode.
#
# Since normal keypresses are passed through, only special keys are supported
# in this section.
[insert]
# Since normal keypresses are passed through, only special keys are supported
# in this section.
#
# Useful hidden commands to map in this section:
# * `open-editor`: Open a texteditor with the focused field.
open-editor
<Ctrl-E>
[hint]
# Since normal keypresses are passed through, only special keys are supported
# in this section.
#
# Useful hidden commands to map in this section:
#
# * `follow-hint`: Follow the currently selected hint.
follow-hint
<Return>
[command,prompt]
rl-backward-char
<Ctrl-B>
rl-forward-char
<Ctrl-F>
rl-backward-word
<Alt-B>
rl-forward-word
<Alt-F>
rl-beginning-of-line
<Ctrl-A>
rl-end-of-line
<Ctrl-E>
rl-unix-line-discard
<Ctrl-U>
rl-kill-line
<Ctrl-K>
rl-kill-word
<Alt-D>
rl-unix-word-rubout
<Ctrl-W>
rl-yank
<Ctrl-Y>
rl-delete-char
<Ctrl-?>
rl-backward-delete-char
<Ctrl-H>
[command]
# Since normal keypresses are passed through, only special keys are
# supported in this mode.
# Useful hidden commands to map in this section:
#
# * `command-history-prev`: Switch to previous command in history.
# * `command-history-next`: Switch to next command in history.
# * `completion-item-prev`: Select previous item in completion.
# * `completion-item-next`: Select next item in completion.
# * `command-accept`: Execute the command currently in the commandline.
# * `leave-mode`: Leave the command mode.
command-history-prev
<Ctrl-P>
command-history-next
<Ctrl-N>
completion-item-prev
<Shift-Tab>
completion-item-prev
<Up>
completion-item-next
<Tab>
completion-item-next
<Down>
command-accept
<Return>
<Ctrl-J>
<Shift-Return>
[prompt]
# You can bind normal keys in this mode, but they will be only active when a
# yes/no-prompt is asked. For other prompt modes, you can only bind special
# keys.
# Useful hidden commands to map in this section:
#
# * `prompt-accept`: Confirm the entered value.
# * `prompt-yes`: Answer yes to a yes/no question.
# * `prompt-no`: Answer no to a yes/no question.
# * `leave-mode`: Leave the prompt mode.
prompt-accept
<Return>
<Shift-Return>
<Ctrl-J>
prompt-yes
y
prompt-no
n
"""
KEY_SECTION_DESC = {
'all': "Keybindings active in all modes.",
'normal': "Keybindings for normal mode.",
'insert': (
"Keybindings for insert mode.\n"
"Since normal keypresses are passed through, only special keys are "
"supported in this mode.\n"
"Useful hidden commands to map in this section:\n\n"
" * `open-editor`: Open a texteditor with the focused field."),
'hint': (
"Keybindings for hint mode.\n"
"Since normal keypresses are passed through, only special keys are "
"supported in this mode.\n"
"Useful hidden commands to map in this section:\n\n"
" * `follow-hint`: Follow the currently selected hint."),
'passthrough': (
"Keybindings for passthrough mode.\n"
"Since normal keypresses are passed through, only special keys are "
"supported in this mode."),
'command': (
"Keybindings for command mode.\n"
"Since normal keypresses are passed through, only special keys are "
"supported in this mode.\n"
"Useful hidden commands to map in this section:\n\n"
" * `command-history-prev`: Switch to previous command in history.\n"
" * `command-history-next`: Switch to next command in history.\n"
" * `completion-item-prev`: Select previous item in completion.\n"
" * `completion-item-next`: Select next item in completion.\n"
" * `command-accept`: Execute the command currently in the "
"commandline."),
'prompt': (
"Keybindings for prompts in the status line.\n"
"You can bind normal keys in this mode, but they will be only active "
"when a yes/no-prompt is asked. For other prompt modes, you can only "
"bind special keys.\n"
"Useful hidden commands to map in this section:\n\n"
" * `prompt-accept`: Confirm the entered value.\n"
" * `prompt-yes`: Answer yes to a yes/no question.\n"
" * `prompt-no`: Answer no to a yes/no question."),
}
KEY_DATA = collections.OrderedDict([
('all', collections.OrderedDict([
('leave-mode', ['<Escape>', '<Ctrl-[>']),
])),
('normal', collections.OrderedDict([
('set-cmd-text ":open "', ['o']),
('set-cmd-text ":open {url}"', ['go']),
('set-cmd-text ":open -t "', ['O']),
('set-cmd-text ":open -t {url}"', ['gO']),
('set-cmd-text ":open -b "', ['xo']),
('set-cmd-text ":open -b {url}"', ['xO']),
('open -t about:blank', ['ga']),
('tab-close', ['d']),
('tab-only', ['co']),
('tab-focus', ['T']),
('tab-move', ['gm']),
('tab-move -', ['gl']),
('tab-move +', ['gr']),
('tab-next', ['J']),
('tab-prev', ['K']),
('reload', ['r']),
('back', ['H']),
('forward', ['L']),
('hint', ['f']),
('hint all tab', ['F']),
('hint all tab-bg', [';b']),
('hint images', [';i']),
('hint images tab', [';I']),
('hint images tab-bg', ['.i']),
('hint links fill ":open {hint-url}"', [';o']),
('hint links fill ":open -t {hint-url}"', [';O']),
('hint links fill ":open -b {hint-url}"', ['.o']),
('hint links yank', [';y']),
('hint links yank-primary', [';Y']),
('hint links rapid', [';r']),
('hint links download', [';d']),
('scroll -50 0', ['h']),
('scroll 0 50', ['j']),
('scroll 0 -50', ['k']),
('scroll 50 0', ['l']),
('undo', ['u']),
('scroll-perc 0', ['gg']),
('scroll-perc', ['G']),
('search-next', ['n']),
('search-prev', ['N']),
('enter-mode insert', ['i']),
('yank', ['yy']),
('yank -s', ['yY']),
('yank -t', ['yt']),
('yank -ts', ['yT']),
('paste', ['pp']),
('paste -s', ['pP']),
('paste -t', ['Pp']),
('paste -ts', ['PP']),
('quickmark-save', ['m']),
('set-cmd-text ":quickmark-load "', ['b']),
('set-cmd-text ":quickmark-load -t "', ['B']),
('save', ['sf']),
('set-cmd-text ":set "', ['ss']),
('set-cmd-text ":set -t "', ['sl']),
('set-cmd-text ":set keybind "', ['sk']),
('zoom-out', ['-']),
('zoom-in', ['+']),
('zoom', ['=']),
('prev-page', ['[[']),
('next-page', [']]']),
('prev-page -t', ['{{']),
('next-page -t', ['}}']),
('inspector', ['wi']),
('download-page', ['gd']),
('cancel-download', ['ad']),
('tab-focus last', ['<Ctrl-Tab>']),
('enter-mode passthrough', ['<Ctrl-V>']),
('quit', ['<Ctrl-Q>']),
('undo', ['<Ctrl-Shift-T>']),
('tab-close', ['<Ctrl-W>']),
('open -t about:blank', ['<Ctrl-T>']),
('scroll-page 0 1', ['<Ctrl-F>']),
('scroll-page 0 -1', ['<Ctrl-B>']),
('scroll-page 0 0.5', ['<Ctrl-D>']),
('scroll-page 0 -0.5', ['<Ctrl-U>']),
('tab-focus 1', ['<Alt-1>']),
('tab-focus 2', ['<Alt-2>']),
('tab-focus 3', ['<Alt-3>']),
('tab-focus 4', ['<Alt-4>']),
('tab-focus 5', ['<Alt-5>']),
('tab-focus 6', ['<Alt-6>']),
('tab-focus 7', ['<Alt-7>']),
('tab-focus 8', ['<Alt-8>']),
('tab-focus 9', ['<Alt-9>']),
('back', ['<Backspace>']),
('home', ['<Ctrl-h>']),
('stop', ['<Ctrl-s>']),
('print', ['<Ctrl-Alt-p>']),
])),
('insert', collections.OrderedDict([
('open-editor', ['<Ctrl-E>']),
])),
('hint', collections.OrderedDict([
('follow-hint', ['<Return>']),
])),
('passthrough', {}),
('command', collections.OrderedDict([
('command-history-prev', ['<Ctrl-P>']),
('command-history-next', ['<Ctrl-N>']),
('completion-item-prev', ['<Shift-Tab>']),
('completion-item-prev', ['<Up>']),
('completion-item-next', ['<Tab>']),
('completion-item-next', ['<Down>']),
('command-accept', ['<Return>', '<Ctrl-J>', '<Shift-Return>']),
])),
('prompt', collections.OrderedDict([
('prompt-accept', ['<Return>', '<Ctrl-J>']),
('prompt-accept', ['<Shift-Return>']),
('prompt-yes', ['y']),
('prompt-no', ['n']),
])),
('command,prompt', collections.OrderedDict([
('rl-backward-char', ['<Ctrl-B>']),
('rl-forward-char', ['<Ctrl-F>']),
('rl-backward-word', ['<Alt-B>']),
('rl-forward-word', ['<Alt-F>']),
('rl-beginning-of-line', ['<Ctrl-A>']),
('rl-end-of-line', ['<Ctrl-E>']),
('rl-unix-line-discard', ['<Ctrl-U>']),
('rl-kill-line', ['<Ctrl-K>']),
('rl-kill-word', ['<Alt-D>']),
('rl-unix-word-rubout', ['<Ctrl-W>']),
('rl-yank', ['<Ctrl-Y>']),
('rl-delete-char', ['<Ctrl-?>']),
('rl-backward-delete-char', ['<Ctrl-H>']),
])),
])

View File

@ -1042,25 +1042,6 @@ class SearchEngineUrl(BaseType):
url.errorString()))
class KeyBindingName(BaseType):
"""The name (keys) of a keybinding."""
def validate(self, value):
if not value:
if self.none_ok:
return
else:
raise ValidationError(value, "may not be empty!")
class KeyBinding(Command):
"""The command of a keybinding."""
pass
class Encoding(BaseType):
"""Setting for a python encoding."""

View File

@ -19,10 +19,10 @@
"""Parser for the key configuration."""
import collections
import os.path
from qutebrowser.config import configdata
from qutebrowser.config import configdata, textwrapper
from qutebrowser.commands import cmdutils
from qutebrowser.utils import log
@ -56,12 +56,67 @@ class KeyConfigParser:
self._cur_section = None
self._cur_command = None
# Mapping of section name(s) to keybinding -> command dicts.
self.keybindings = {}
self.keybindings = collections.OrderedDict()
if not os.path.exists(self._configfile):
log.init.debug("Creating initial keybinding config.")
with open(self._configfile, 'w', encoding='utf-8') as f:
f.write(configdata.KEYBINDINGS)
self._read()
self._load_default()
else:
self._read()
log.init.debug("Loaded bindings: {}".format(self.keybindings))
def __str__(self):
"""Get the config as string."""
lines = configdata.KEY_FIRST_COMMENT.strip('\n').splitlines()
lines.append('')
for sectname, sect in self.keybindings.items():
lines.append('[{}]'.format(sectname))
lines += self._str_section_desc(sectname)
lines.append('')
data = collections.OrderedDict()
for key, cmd in sect.items():
if cmd in data:
data[cmd].append(key)
else:
data[cmd] = [key]
for cmd, keys in data.items():
lines.append(cmd)
for k in keys:
lines.append(' ' * 4 + k)
lines.append('')
return '\n'.join(lines) + '\n'
def _str_section_desc(self, sectname):
"""Get the section description string for sectname."""
wrapper = textwrapper.TextWrapper()
lines = []
try:
seclines = configdata.KEY_SECTION_DESC[sectname].splitlines()
except KeyError:
return []
else:
for secline in seclines:
if 'http://' in secline or 'https://' in secline:
lines.append('# ' + secline)
else:
lines += wrapper.wrap(secline)
return lines
def save(self):
"""Save the key config file."""
log.destroy.debug("Saving key config to {}".format(self._configfile))
with open(self._configfile, 'w', encoding='utf-8') as f:
f.write(str(self))
def _normalize_sectname(self, s):
"""Normalize a section string like 'foo, bar,baz' to 'bar,baz,foo'."""
return ','.join(sorted(s.split(',')))
def _load_default(self):
"""Load the built-in default keybindings."""
for sectname, sect in configdata.KEY_DATA.items():
sectname = self._normalize_sectname(sectname)
for command, keychains in sect.items():
for e in keychains:
self._add_binding(sectname, e, command)
def _read(self):
"""Read the config file from disk and parse it."""
@ -72,7 +127,8 @@ class KeyConfigParser:
if not line.strip() or line.startswith('#'):
continue
elif line.startswith('[') and line.endswith(']'):
self._cur_section = line[1:-1]
sectname = line[1:-1]
self._cur_section = self._normalize_sectname(sectname)
elif line.startswith((' ', '\t')):
line = line.strip()
self._read_keybinding(line)
@ -101,10 +157,13 @@ class KeyConfigParser:
"command!".format(line))
else:
assert self._cur_section is not None
if self._cur_section not in self.keybindings:
self.keybindings[self._cur_section] = {}
section_bindings = self.keybindings[self._cur_section]
section_bindings[line] = self._cur_command
self._add_binding(self._cur_section, line, self._cur_command)
def _add_binding(self, sectname, keychain, command):
"""Add a new binding from keychain to command in section sectname."""
if sectname not in self.keybindings:
self.keybindings[sectname] = collections.OrderedDict()
self.keybindings[sectname][keychain] = command
def get_bindings_for(self, section):
"""Get a dict with all merged keybindings for a section."""
@ -113,4 +172,8 @@ class KeyConfigParser:
sects = [s.strip() for s in sectstring.split(',')]
if any(s == section for s in sects):
bindings.update(d)
try:
bindings.update(self.keybindings['all'])
except KeyError:
pass
return bindings

View File

@ -0,0 +1,39 @@
# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
# Copyright 2014 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
#
# This file is part of qutebrowser.
#
# qutebrowser is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# qutebrowser is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with qutebrowser. If not, see <http://www.gnu.org/licenses/>.
"""Textwrapper used for config files."""
import textwrap
class TextWrapper(textwrap.TextWrapper):
"""Text wrapper customized to be used in configs."""
def __init__(self, *args, **kwargs):
kw = {
'width': 72,
'replace_whitespace': False,
'break_long_words': False,
'break_on_hyphens': False,
'initial_indent': '# ',
'subsequent_indent': '# ',
}
kw.update(kwargs)
super().__init__(*args, **kw)

View File

@ -1733,32 +1733,6 @@ class SearchEngineUrlTests(unittest.TestCase):
self.assertEqual(self.t.transform("foobar"), "foobar")
class KeyBindingNameTests(unittest.TestCase):
"""Test KeyBindingName."""
def setUp(self):
self.t = configtypes.KeyBindingName()
def test_validate_empty(self):
"""Test validate with empty string and none_ok = False."""
with self.assertRaises(configtypes.ValidationError):
self.t.validate('')
def test_validate_empty_none_ok(self):
"""Test validate with empty string and none_ok = True."""
t = configtypes.KeyBindingName(none_ok=True)
t.validate('')
def test_transform_empty(self):
"""Test transform with an empty value."""
self.assertIsNone(self.t.transform(''))
def test_transform(self):
"""Test transform with a value."""
self.assertEqual(self.t.transform("foobar"), "foobar")
class WebSettingsFileTests(unittest.TestCase):
"""Test WebSettingsFile."""

View File

@ -31,13 +31,15 @@ from qutebrowser.keyinput import basekeyparser
from qutebrowser.test import stubs, helpers
CONFIG = {'test': {'<Ctrl-a>': 'ctrla',
'a': 'a',
'ba': 'ba',
'ax': 'ax',
'ccc': 'ccc'},
'input': {'timeout': 100},
'test2': {'foo': 'bar', '<Ctrl+X>': 'ctrlx'}}
CONFIG = {'input': {'timeout': 100}}
BINDINGS = {'test': {'<Ctrl-a>': 'ctrla',
'a': 'a',
'ba': 'ba',
'ax': 'ax',
'ccc': 'ccc'},
'test2': {'foo': 'bar', '<Ctrl+X>': 'ctrlx'}}
def setUpModule():
@ -51,6 +53,14 @@ def tearDownModule():
logging.disable(logging.NOTSET)
def _get_fake_application():
"""Construct a fake QApplication with a keyconfig."""
app = stubs.FakeQApplication()
app.keyconfig = mock.Mock(spec=['get_bindings_for'])
app.keyconfig.get_bindings_for.side_effect = lambda s: BINDINGS[s]
return app
class SplitCountTests(unittest.TestCase):
"""Test the _split_count method.
@ -99,7 +109,7 @@ class ReadConfigTests(unittest.TestCase):
"""Test reading the config."""
def setUp(self):
basekeyparser.config = stubs.ConfigStub(CONFIG)
basekeyparser.QCoreApplication = _get_fake_application()
basekeyparser.usertypes.Timer = mock.Mock()
def test_read_config_invalid(self):
@ -136,7 +146,7 @@ class SpecialKeysTests(unittest.TestCase):
autospec=True)
patcher.start()
self.addCleanup(patcher.stop)
basekeyparser.config = stubs.ConfigStub(CONFIG)
basekeyparser.QCoreApplication = _get_fake_application()
self.kp = basekeyparser.BaseKeyParser()
self.kp.execute = mock.Mock()
self.kp.read_config('test')
@ -171,7 +181,7 @@ class KeyChainTests(unittest.TestCase):
def setUp(self):
"""Set up mocks and read the test config."""
basekeyparser.config = stubs.ConfigStub(CONFIG)
basekeyparser.QCoreApplication = _get_fake_application()
self.timermock = mock.Mock()
basekeyparser.usertypes.Timer = mock.Mock(return_value=self.timermock)
self.kp = basekeyparser.BaseKeyParser(supports_chains=True,
@ -205,6 +215,7 @@ class KeyChainTests(unittest.TestCase):
def test_ambigious_keychain(self):
"""Test ambigious keychain."""
basekeyparser.config = stubs.ConfigStub(CONFIG)
# We start with 'a' where the keychain gives us an ambigious result.
# Then we check if the timer has been set up correctly
self.kp.handle(helpers.fake_keyevent(Qt.Key_A, text='a'))
@ -235,7 +246,7 @@ class CountTests(unittest.TestCase):
"""Test execute() with counts."""
def setUp(self):
basekeyparser.config = stubs.ConfigStub(CONFIG)
basekeyparser.QCoreApplication = _get_fake_application()
basekeyparser.usertypes.Timer = mock.Mock()
self.kp = basekeyparser.BaseKeyParser(supports_chains=True,
supports_count=True)

View File

@ -110,8 +110,7 @@ class FakeQApplication:
"""Stub to insert as QApplication module."""
def __init__(self, focus):
self.focusWidget = mock.Mock(return_value=focus)
def __init__(self):
self.instance = mock.Mock(return_value=self)

View File

@ -34,7 +34,8 @@ class NoneWidgetTests(unittest.TestCase):
"""Tests when the focused widget is None."""
def setUp(self):
readline.QApplication = stubs.FakeQApplication(None)
readline.QApplication = stubs.FakeQApplication()
readline.QApplication.focusWidget = mock.Mock(return_value=None)
self.bridge = readline.ReadlineBridge()
def test_none(self):
@ -52,7 +53,7 @@ class ReadlineBridgeTest(unittest.TestCase):
def setUp(self):
self.qle = mock.Mock()
self.qle.__class__ = QLineEdit
readline.QApplication = stubs.FakeQApplication(self.qle)
readline.QApplication.focusWidget = mock.Mock(return_value=self.qle)
self.bridge = readline.ReadlineBridge()
def _set_selected_text(self, text):