diff --git a/data/coverage.svg b/data/coverage.svg index 4b105c68..5b40c8fe 100644 --- a/data/coverage.svg +++ b/data/coverage.svg @@ -15,7 +15,7 @@ coverage coverage - 82% - 82% + 83% + 83% diff --git a/data/pylint.svg b/data/pylint.svg index 280836f4..df8fd8a5 100644 --- a/data/pylint.svg +++ b/data/pylint.svg @@ -17,7 +17,7 @@ pylint - 9.66 - 9.66 + 9.82 + 9.82 \ No newline at end of file diff --git a/keymapper/daemon.py b/keymapper/daemon.py index e3a193aa..a637ae49 100644 --- a/keymapper/daemon.py +++ b/keymapper/daemon.py @@ -24,6 +24,7 @@ import subprocess +import dbus from dbus import service import dbus.mainloop.glib @@ -57,7 +58,7 @@ def get_dbus_interface(): remote_object = bus.get_object('keymapper.Control', '/') interface = dbus.Interface(remote_object, 'keymapper.Interface') logger.debug('Connected to dbus') - except Exception as error: + except dbus.exceptions.DBusException as error: logger.error( 'Could not connect to the dbus of "key-mapper-service", mapping ' 'keys only works as long as the window is open.' @@ -86,7 +87,11 @@ class Daemon(service.Object): print(device, preset) mapping = Mapping() mapping.load(device, preset) - self.injectors[device] = KeycodeInjector(device, mapping) + try: + injector = KeycodeInjector(device, mapping) + self.injectors[device] = injector + except OSError as error: + logger.error(error) super().__init__(*args, **kwargs) @dbus.service.method( diff --git a/keymapper/dev/permissions.py b/keymapper/dev/permissions.py index b8c923c5..1dc65a74 100644 --- a/keymapper/dev/permissions.py +++ b/keymapper/dev/permissions.py @@ -40,8 +40,10 @@ def can_read_devices(): def warn(group): logger.warning( 'Some devices may not be visible without being in the ' - f'"{group}" user group. Try `sudo usermod -a -G {group} $USER` ' - 'and log out and back in.' + f'"%s" user group. Try `sudo usermod -a -G %s $USER` ' + 'and log out and back in.', + group, + group ) if not is_root and not is_test: diff --git a/keymapper/gtk/row.py b/keymapper/gtk/row.py index 5aa382d6..4e453d7a 100644 --- a/keymapper/gtk/row.py +++ b/keymapper/gtk/row.py @@ -109,6 +109,7 @@ class Row(Gtk.ListBoxRow): self.get_style_context().remove_class('changed') def on_character_input_change(self, _): + """When the output character for that keycode is typed in.""" keycode = self.get_keycode() character = self.get_character() diff --git a/keymapper/logger.py b/keymapper/logger.py index 32fd4afe..5cc8950c 100644 --- a/keymapper/logger.py +++ b/keymapper/logger.py @@ -27,13 +27,17 @@ import logging import pkg_resources +SPAM = 5 + + def spam(self, message, *args, **kws): + """Log a more-verbose message than debug.""" + # pylint: disable=protected-access if self.isEnabledFor(SPAM): # https://stackoverflow.com/a/13638084 self._log(SPAM, message, args, **kws) -SPAM = 5 logging.addLevelName(SPAM, "SPAM") logging.Logger.spam = spam @@ -42,10 +46,11 @@ class Formatter(logging.Formatter): """Overwritten Formatter to print nicer logs.""" def format(self, record): """Overwritten format function.""" + # pylint: disable=protected-access debug = logger.level <= logging.DEBUG if record.levelno == logging.INFO and not debug: # if not launched with --debug, then don't print "INFO:" - self._style._fmt = '%(message)s' # pylint: disable=line-too-long + self._style._fmt = '%(message)s' else: # see https://en.wikipedia.org/wiki/ANSI_escape_code#3/4_bit # for those numbers diff --git a/keymapper/mapping.py b/keymapper/mapping.py index 3ae5715a..89750bd8 100644 --- a/keymapper/mapping.py +++ b/keymapper/mapping.py @@ -30,19 +30,11 @@ from keymapper.logger import logger from keymapper.paths import get_config_path -def update_reverse_mapping(func): - """Generate a reverse mapping to optimize reverse lookups. - - If _mapping contains `20: "a, A"` (the xkb syntax for modified keys), - reverse mapping will contain `"a": 20, "A": 20`. - """ +def keep_reverse_mapping_intact(func): + """Decorator for Mapping.update_reverse_mapping.""" def wrapper(self, *args, **kwargs): func(self, *args, **kwargs) - - self._reverse_mapping = {} - for key, value in self._mapping.items(): - for character in value.split(','): - self._reverse_mapping[character.strip()] = key + self.update_reverse_mapping() return wrapper @@ -71,7 +63,18 @@ class Mapping: def __len__(self): return len(self._mapping) - @update_reverse_mapping + def update_reverse_mapping(self): + """Generate a reverse mapping to optimize reverse lookups. + + If _mapping contains `20: "a, A"` (the xkb syntax for modified keys), + reverse mapping will contain `"a": 20, "A": 20`. + """ + self._reverse_mapping = {} + for key, value in self._mapping.items(): + for character in value.split(','): + self._reverse_mapping[character.strip()] = key + + @keep_reverse_mapping_intact def change(self, new_keycode, character, previous_keycode=None): """Replace the mapping of a keycode with a different one. @@ -110,7 +113,7 @@ class Mapping: return False - @update_reverse_mapping + @keep_reverse_mapping_intact def clear(self, keycode): """Remove a keycode from the mapping. @@ -122,13 +125,13 @@ class Mapping: del self._mapping[keycode] self.changed = True - @update_reverse_mapping + @keep_reverse_mapping_intact def empty(self): """Remove all mappings.""" self._mapping = {} self.changed = True - @update_reverse_mapping + @keep_reverse_mapping_intact def load(self, device, preset): """Load a dumped JSON from home to overwrite the mappings.""" path = get_config_path(device, preset)