plover 5.1.0__py3-none-any.whl → 5.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. plover/__init__.py +5 -3
  2. plover/config.py +58 -14
  3. plover/dictionary/loading_manager.py +1 -1
  4. plover/engine.py +205 -37
  5. plover/formatting.py +5 -2
  6. plover/gui_qt/about_dialog_ui.py +1 -1
  7. plover/gui_qt/add_translation_dialog_ui.py +1 -1
  8. plover/gui_qt/add_translation_widget_ui.py +1 -1
  9. plover/gui_qt/appearance.py +49 -0
  10. plover/gui_qt/config_file_widget_ui.py +1 -1
  11. plover/gui_qt/config_keyboard_widget_ui.py +1 -1
  12. plover/gui_qt/config_plover_hid_widget_ui.py +1 -1
  13. plover/gui_qt/config_serial_widget_ui.py +2 -11
  14. plover/gui_qt/config_window.py +139 -33
  15. plover/gui_qt/config_window_ui.py +2 -2
  16. plover/gui_qt/console_widget.py +1 -1
  17. plover/gui_qt/console_widget_ui.py +1 -1
  18. plover/gui_qt/dictionaries_widget_ui.py +1 -1
  19. plover/gui_qt/dictionary_editor_ui.py +1 -1
  20. plover/gui_qt/lookup_dialog_ui.py +1 -1
  21. plover/gui_qt/machine_options.py +1 -19
  22. plover/gui_qt/main_window.py +13 -1
  23. plover/gui_qt/main_window_ui.py +15 -8
  24. plover/gui_qt/paper_tape_ui.py +1 -1
  25. plover/gui_qt/plugins_manager.py +9 -4
  26. plover/gui_qt/plugins_manager_ui.py +2 -2
  27. plover/gui_qt/resources_rc.py +29 -29
  28. plover/gui_qt/run_dialog_ui.py +1 -1
  29. plover/gui_qt/suggestions_dialog_ui.py +1 -1
  30. plover/gui_qt/trayicon.py +0 -2
  31. plover/gui_qt/utils.py +0 -1
  32. plover/i18n.py +1 -1
  33. plover/key_combo.py +3 -1
  34. plover/log.py +2 -2
  35. plover/machine/base.py +12 -2
  36. plover/machine/keyboard_capture/__init__.py +33 -10
  37. plover/machine/keymap.py +86 -10
  38. plover/machine/plover_hid.py +46 -25
  39. plover/machine/procat.py +0 -3
  40. plover/machine/stentura.py +5 -5
  41. plover/messages/es/LC_MESSAGES/plover.mo +0 -0
  42. plover/messages/es/LC_MESSAGES/plover.po +2 -2
  43. plover/messages/fr/LC_MESSAGES/plover.mo +0 -0
  44. plover/messages/fr/LC_MESSAGES/plover.po +2 -2
  45. plover/messages/it/LC_MESSAGES/plover.mo +0 -0
  46. plover/messages/it/LC_MESSAGES/plover.po +2 -2
  47. plover/messages/nl/LC_MESSAGES/plover.mo +0 -0
  48. plover/messages/nl/LC_MESSAGES/plover.po +2 -2
  49. plover/messages/plover.pot +1 -1
  50. plover/messages/zh_tw/LC_MESSAGES/plover.mo +0 -0
  51. plover/messages/zh_tw/LC_MESSAGES/plover.po +2 -2
  52. plover/orthography.py +2 -1
  53. plover/oslayer/controller.py +1 -1
  54. plover/oslayer/linux/keyboardcontrol.py +4 -2
  55. plover/oslayer/linux/keyboardcontrol_uinput.py +107 -343
  56. plover/oslayer/linux/keyboardcontrol_x11.py +22 -13
  57. plover/oslayer/linux/keyboardlayout_wayland.py +403 -0
  58. plover/oslayer/linux/log.py +3 -1
  59. plover/oslayer/linux/wayland_connection.py +293 -0
  60. plover/oslayer/osx/keyboardlayout.py +1 -1
  61. plover/plugins_manager/registry.py +4 -3
  62. plover/registry.py +1 -1
  63. plover/scripts/main.py +6 -3
  64. plover/system/__init__.py +7 -7
  65. {plover-5.1.0.dist-info → plover-5.2.0.dist-info}/METADATA +5 -4
  66. {plover-5.1.0.dist-info → plover-5.2.0.dist-info}/RECORD +76 -74
  67. {plover-5.1.0.dist-info → plover-5.2.0.dist-info}/WHEEL +1 -1
  68. plover_build_utils/check_requirements.py +0 -3
  69. plover_build_utils/functions.sh +0 -11
  70. plover_build_utils/testing/__init__.py +9 -0
  71. plover_build_utils/testing/blackbox.py +3 -1
  72. plover_build_utils/testing/steno_dictionary.py +2 -2
  73. plover_build_utils/deps.sh +0 -2
  74. {plover-5.1.0.dist-info → plover-5.2.0.dist-info}/entry_points.txt +0 -0
  75. {plover-5.1.0.dist-info → plover-5.2.0.dist-info}/licenses/LICENSE.txt +0 -0
  76. {plover-5.1.0.dist-info → plover-5.2.0.dist-info}/top_level.txt +0 -0
  77. {plover-5.1.0.dist-info → plover-5.2.0.dist-info}/zip-safe +0 -0
plover/__init__.py CHANGED
@@ -11,9 +11,11 @@ else:
11
11
  # exec from `setup.py`, package data
12
12
  # may not be available, and we don't
13
13
  # want to translate anyway.
14
- _ = lambda s: s
14
+ def _(s):
15
+ return s
15
16
 
16
- __version__ = "5.1.0"
17
+
18
+ __version__ = "5.2.0"
17
19
  __copyright__ = "(C) Open Steno Project"
18
20
  __url__ = "http://www.openstenoproject.org/"
19
21
  __download_url__ = "http://www.openstenoproject.org/plover"
@@ -24,7 +26,7 @@ Developers:
24
26
 
25
27
  Joshua Lifton
26
28
  Hesky Fisher
27
- Ted Morin
29
+ Thea Morin
28
30
  Benoit Pierre
29
31
  Sammi Ta
30
32
  Martin Koerner
plover/config.py CHANGED
@@ -1,12 +1,14 @@
1
1
  # Copyright (c) 2010-2011 Joshua Harlan Lifton.
2
2
  # See LICENSE.txt for details.
3
3
 
4
- """Configuration management."""
4
+ """This modules handles reading and writing Plover's configuration files, as well
5
+ as updating the configuration on-the-fly while Plover is running."""
5
6
 
6
7
  from collections import ChainMap, namedtuple, OrderedDict
7
8
  import configparser
8
9
  import json
9
10
  import re
11
+ from typing import Any, Dict
10
12
 
11
13
  from plover.exception import InvalidConfigurationError
12
14
  from plover.machine.keymap import Keymap
@@ -17,6 +19,7 @@ from plover import log
17
19
 
18
20
 
19
21
  # General configuration sections, options and defaults.
22
+ APPEARANCE_CONFIG_SECTION = "Appearance"
20
23
  MACHINE_CONFIG_SECTION = "Machine Configuration"
21
24
 
22
25
  LEGACY_DICTIONARY_CONFIG_SECTION = "Dictionary Configuration"
@@ -36,14 +39,23 @@ SYSTEM_KEYMAP_OPTION = "keymap[%s]"
36
39
 
37
40
 
38
41
  class DictionaryConfig(namedtuple("DictionaryConfig", "path enabled")):
42
+ """Represents the configuration for one dictionary.
43
+
44
+ Attributes:
45
+ path (str): The fully qualified path to the dictionary file.
46
+ enabled (bool): Whether the dictionary is enabled.
47
+ """
48
+
39
49
  def __new__(cls, path, enabled=True):
40
50
  return super().__new__(cls, expand_path(path), enabled)
41
51
 
42
52
  @property
43
- def short_path(self):
53
+ def short_path(self) -> str:
54
+ """The shortened path to the dictionary file. This is automatically calculated from :attr:`path`."""
44
55
  return shorten_path(self.path)
45
56
 
46
- def to_dict(self):
57
+ def to_dict(self) -> Dict[str, Any]:
58
+ """Returns the ``dict`` representation of the dictionary configuration."""
47
59
  # Note: do not use _asdict because of
48
60
  # https://bugs.python.org/issue24931
49
61
  return {
@@ -51,11 +63,13 @@ class DictionaryConfig(namedtuple("DictionaryConfig", "path enabled")):
51
63
  "enabled": self.enabled,
52
64
  }
53
65
 
54
- def replace(self, **kwargs):
66
+ def replace(self, **kwargs) -> "DictionaryConfig":
67
+ """Replaces the values of :attr:`path` and :attr:`enabled` with those in ``kwargs``."""
55
68
  return self._replace(**kwargs)
56
69
 
57
70
  @staticmethod
58
- def from_dict(d):
71
+ def from_dict(d: Dict[str, Any]) -> "DictionaryConfig":
72
+ """Returns a :class:`DictionaryConfig` constructed from its ``dict`` representation."""
59
73
  return DictionaryConfig(**d)
60
74
 
61
75
  def __repr__(self):
@@ -73,6 +87,11 @@ ConfigOption = namedtuple(
73
87
 
74
88
 
75
89
  class InvalidConfigOption(ValueError):
90
+ """An exception raised when a configuration option has been set to an invalid
91
+ value, such as one of the wrong type. ``fixed_value`` is the value that
92
+ Plover is falling back on if ``raw_value`` can't be parsed correctly.
93
+ """
94
+
76
95
  def __init__(self, raw_value, fixed_value, message=None):
77
96
  super().__init__(raw_value)
78
97
  self.raw_value = raw_value
@@ -368,14 +387,22 @@ def dictionaries_option():
368
387
 
369
388
 
370
389
  class Config:
371
- def __init__(self, path=None):
390
+ """An object containing the entire Plover configuration. The config object
391
+ maintains a cache for any changes that are made while Plover is running.
392
+ """
393
+
394
+ def __init__(self, path=None) -> None:
372
395
  self._config = None
373
396
  self._cache = {}
374
397
  # A convenient place for other code to store a file name.
375
398
  self.path = path
376
399
  self.clear()
377
400
 
378
- def load(self):
401
+ def load(self) -> None:
402
+ """Reads and parses the configuration from the configuration file. Raises an
403
+ :exc:`InvalidConfigurationError<plover.exception.InvalidConfigurationError>`
404
+ if the configuration could not be parsed correctly.
405
+ """
379
406
  self.clear()
380
407
  with open(self.path, encoding="utf-8") as fp:
381
408
  try:
@@ -383,11 +410,13 @@ class Config:
383
410
  except configparser.Error as e:
384
411
  raise InvalidConfigurationError(str(e))
385
412
 
386
- def clear(self):
413
+ def clear(self) -> None:
414
+ """Clears the configuration and returns to the base state."""
387
415
  self._config = configparser.RawConfigParser()
388
416
  self._cache.clear()
389
417
 
390
- def save(self):
418
+ def save(self) -> None:
419
+ """Writes the current state of the configuration to the configuration file."""
391
420
  with resource_update(self.path) as temp_path:
392
421
  with open(temp_path, mode="w", encoding="utf-8") as fp:
393
422
  self._config.write(fp)
@@ -427,7 +456,7 @@ class Config:
427
456
  ),
428
457
  choice_option(
429
458
  "keyboard_layout",
430
- ("qwerty", "qwertz", "colemak", "colemak-dh", "dvorak"),
459
+ ("qwerty", "qwertz", "colemak", "colemak-dh", "dvorak", "wayland-auto"),
431
460
  OUTPUT_CONFIG_SECTION,
432
461
  ),
433
462
  # Logging.
@@ -439,6 +468,13 @@ class Config:
439
468
  ),
440
469
  boolean_option("enable_stroke_logging", False, LOGGING_CONFIG_SECTION),
441
470
  boolean_option("enable_translation_logging", False, LOGGING_CONFIG_SECTION),
471
+ # Appearance.
472
+ choice_option(
473
+ "appearance_mode",
474
+ ("system", "light", "dark"),
475
+ APPEARANCE_CONFIG_SECTION,
476
+ "mode",
477
+ ),
442
478
  # GUI.
443
479
  boolean_option("start_minimized", False, "Startup", "Start Minimized"),
444
480
  boolean_option("show_stroke_display", False, "Stroke Display", "show"),
@@ -471,7 +507,10 @@ class Config:
471
507
  key = opt.full_key(self, key)
472
508
  return key, opt
473
509
 
474
- def __getitem__(self, key):
510
+ def __getitem__(self, key: str) -> Any:
511
+ """Returns the value of the specified ``key`` in the cache, or in the
512
+ full configuration if not available.
513
+ """
475
514
  key, opt = self._lookup(key)
476
515
  if key in self._cache:
477
516
  return self._cache[key]
@@ -485,16 +524,21 @@ class Config:
485
524
  self._cache[key] = value
486
525
  return value
487
526
 
488
- def __setitem__(self, key, value):
527
+ def __setitem__(self, key: str, value: Any) -> None:
528
+ """Sets the property ``key`` in the configuration to the specified value."""
489
529
  key, opt = self._lookup(key)
490
530
  value = opt.validate(self._config, key, value)
491
531
  opt.setter(self, key, value)
492
532
  self._cache[key] = value
493
533
 
494
- def as_dict(self):
534
+ def as_dict(self) -> Dict[str, Any]:
535
+ """Returns the ``dict`` representation of the current state of the
536
+ configuration.
537
+ """
495
538
  return {opt.name: self[opt.name] for opt in self._OPTIONS.values()}
496
539
 
497
- def update(self, **kwargs):
540
+ def update(self, **kwargs) -> None:
541
+ """Update the cache to reflect the contents of the full configuration."""
498
542
  new_settings = []
499
543
  new_config = ChainMap({}, self)
500
544
  for opt in self._OPTIONS.values():
@@ -75,7 +75,7 @@ class DictionaryLoadingOperation:
75
75
  def needs_reloading(self):
76
76
  try:
77
77
  new_timestamp = resource_timestamp(self.filename)
78
- except:
78
+ except Exception:
79
79
  # Bad resource name, permission denied, path
80
80
  # does not exist, ...
81
81
  new_timestamp = None
plover/engine.py CHANGED
@@ -1,3 +1,8 @@
1
+ """The steno engine is the core of Plover; it handles communication between the
2
+ machine and the translation and formatting subsystems, and manages configuration
3
+ and dictionaries.
4
+ """
5
+
1
6
  from collections import namedtuple, OrderedDict
2
7
  from functools import wraps
3
8
  from queue import Queue
@@ -5,6 +10,7 @@ import functools
5
10
  import os
6
11
  import shutil
7
12
  import threading
13
+ from typing import Any, Callable, Dict, List, Optional, Tuple
8
14
 
9
15
  from plover import log, system
10
16
  from plover.dictionary.loading_manager import DictionaryLoadingManager
@@ -22,14 +28,42 @@ StartingStrokeState = namedtuple(
22
28
  "StartingStrokeState", "attach capitalize space_char", defaults=(False, False, " ")
23
29
  )
24
30
 
31
+ StartingStrokeState.__doc__ = """An object representing the starting state of the formatter before any
32
+ strokes are input.
33
+
34
+ Attributes:
35
+ attach (bool): Whether to delete the space before the translation when the
36
+ initial stroke is translated.
37
+ capitalize (bool): Whether to capitalize the translation when the initial
38
+ stroke is translated.
39
+ """
25
40
 
26
41
  MachineParams = namedtuple("MachineParams", "type options keymap")
27
42
 
43
+ MachineParams.__doc__ = """An object representing the current state of the machine.
44
+
45
+ Attributes:
46
+ type (str): The name of the machine. This is the same as the name of the plugin
47
+ that provides the machine's functionality. ``Keyboard`` by default.
48
+ options (Dict[str, Any]): A dictionary of machine specific options. See
49
+ :mod:`plover.config` for more information.
50
+ keymap (plover.machine.keymap.Keymap): A
51
+ :class:`Keymap<plover.machine.keymap.Keymap>` mapping the current
52
+ system to this machine.
53
+ """
54
+
28
55
 
29
56
  class ErroredDictionary(StenoDictionary):
30
- """Placeholder for dictionaries that failed to load."""
57
+ """A placeholder class for a dictionary that failed to load.
58
+
59
+ This is a subclass of :class:`StenoDictionary<plover.steno_dictionary.StenoDictionary>`.
31
60
 
32
- def __init__(self, path, exception):
61
+ Attributes:
62
+ path (str): The path to the dictionary file.
63
+ exception (Any): The exception that caused the dictionary loading to fail.
64
+ """
65
+
66
+ def __init__(self, path: str, exception: Any):
33
67
  super().__init__()
34
68
  self.enabled = False
35
69
  self.readonly = True
@@ -76,7 +110,36 @@ def with_lock(func):
76
110
 
77
111
 
78
112
  class StenoEngine:
79
- HOOKS = """
113
+ """
114
+ Attributes:
115
+ config (Dict[str, Any]): A dictionary containing configuration options.
116
+ controller (plover.oslayer.controller.Controller): An instance of
117
+ :class:`Controller<plover.oslayer.controller.Controller>` for managing
118
+ commands sent to this Plover instance. This is provided during startup.
119
+ keyboard_emulation (plover.oslayer.keyboardcontrol.KeyboardEmulation): An
120
+ instance of
121
+ :class:`KeyboardEmulation<plover.oslayer.keyboardcontrol.KeyboardEmulation>`
122
+ provided during startup.
123
+ HOOKS (List[str]): A list of all the possible engine hooks. See
124
+ :ref:`engine-hooks` below for a list of valid hooks.
125
+ machine_state (str): The connection state of the current machine. One of
126
+ ``stopped``, ``initializing``, ``connected`` or ``disconnected``.
127
+ output (bool): ``True`` if steno output is enabled, ``False`` otherwise.
128
+ _config (plover.config.Config): A :class:`Config<plover.config.Config>` object
129
+ containing the engine's configuration.
130
+ translator_state (plover.translation._State): A
131
+ :class:`_State<plover.translation._State>` object containing the current
132
+ state of the translator.
133
+ starting_stroke_state (StartingStrokeState): A :class:`StartingStrokeState`
134
+ representing the initial state of the formatter.
135
+ dictionaries (plover.steno_dictionary.StenoDictionaryCollection): A
136
+ :class:`StenoDictionaryCollection<plover.steno_dictionary.StenoDictionaryCollection>`
137
+ of all the dictionaries Plover has loaded for the current system. This
138
+ includes disabled dictionaries and dictionaries that failed to load.
139
+
140
+ """
141
+
142
+ HOOKS: List[str] = """
80
143
  stroked
81
144
  translated
82
145
  machine_state_changed
@@ -95,7 +158,7 @@ class StenoEngine:
95
158
  quit
96
159
  """.split()
97
160
 
98
- def __init__(self, config, controller, keyboard_emulation):
161
+ def __init__(self, config: Any, controller: Any, keyboard_emulation: Any):
99
162
  self._config = config
100
163
  self._controller = controller
101
164
  self._is_running = False
@@ -134,7 +197,14 @@ class StenoEngine:
134
197
  def __exit__(self, exc_type, exc_value, traceback):
135
198
  self._lock.__exit__(exc_type, exc_value, traceback)
136
199
 
137
- def _in_engine_thread(self):
200
+ def _in_engine_thread(self) -> bool:
201
+ """Returns whether we are currently in the same thread that the engine
202
+ is running on.
203
+
204
+ This is useful because event listeners for machines and others are run
205
+ on separate threads, and we want to be able to run engine events on the
206
+ same thread as the main engine.
207
+ """
138
208
  raise NotImplementedError()
139
209
 
140
210
  def _same_thread_hook(self, func, *args, **kwargs):
@@ -143,7 +213,8 @@ class StenoEngine:
143
213
  else:
144
214
  self._queue.put((func, args, kwargs))
145
215
 
146
- def run(self):
216
+ def run(self) -> None:
217
+ """Starts the steno engine, translating any strokes that are input."""
147
218
  while True:
148
219
  func, args, kwargs = self._queue.get()
149
220
  try:
@@ -423,26 +494,48 @@ class StenoEngine:
423
494
  self._keyboard_emulation.send_key_combination(c)
424
495
  self._trigger_hook("send_key_combination", c)
425
496
 
426
- def _send_engine_command(self, command):
497
+ def _send_engine_command(self, command: str) -> None:
498
+ """Runs the specified Plover command, which can be either a built-in
499
+ command like ``set_config`` or one from an external plugin.
500
+
501
+ ``command`` is a string containing the command and its argument (if any),
502
+ separated by a colon. For example, ``lookup`` sends the ``lookup`` command
503
+ (the same as stroking ``{PLOVER:LOOKUP}``), and ``run_shell:foo`` sends the
504
+ ``run_shell`` command with the argument ``foo``.
505
+ """
427
506
  suppress = not self._is_running
428
507
  suppress &= self._consume_engine_command(command)
429
508
  if suppress:
430
509
  self._machine.suppress_last_stroke(self._keyboard_emulation.send_backspaces)
431
510
 
432
511
  def toggle_output(self):
512
+ """Toggles steno mode.
513
+
514
+ See :attr:`output` to get the current state, or
515
+ :meth:`set_output` to set the state to a specific value.
516
+ """
433
517
  self._same_thread_hook(self._toggle_output)
434
518
 
435
- def set_output(self, enabled):
519
+ def set_output(self, enabled: bool) -> None:
520
+ """Enables or disables steno mode.
521
+
522
+ Set ``enabled`` to ``True`` to enable steno mode, or ``False`` to disable it.
523
+ """
436
524
  self._same_thread_hook(self._set_output, enabled)
437
525
 
438
526
  @property
439
527
  @with_lock
440
- def machine_state(self):
528
+ def machine_state(self) -> Optional[str]:
529
+ """The connection state of the current machine.
530
+
531
+ One of ``stopped``, ``initializing``, ``connected`` or ``disconnected``.
532
+ """
441
533
  return self._machine_state
442
534
 
443
535
  @property
444
536
  @with_lock
445
- def output(self):
537
+ def output(self) -> bool:
538
+ """``True`` if steno output is enabled, ``False`` otherwise."""
446
539
  return self._is_running
447
540
 
448
541
  @output.setter
@@ -451,7 +544,8 @@ class StenoEngine:
451
544
 
452
545
  @property
453
546
  @with_lock
454
- def config(self):
547
+ def config(self) -> Dict[str, Any]:
548
+ """A dictionary containing configuration options."""
455
549
  return self._config.as_dict()
456
550
 
457
551
  @config.setter
@@ -459,16 +553,24 @@ class StenoEngine:
459
553
  self._same_thread_hook(self._update, config_update=update)
460
554
 
461
555
  @with_lock
462
- def __getitem__(self, setting):
556
+ def __getitem__(self, setting: str) -> Any:
557
+ """Returns the value of the configuration property ``setting``."""
463
558
  return self._config[setting]
464
559
 
465
- def __setitem__(self, setting, value):
560
+ def __setitem__(self, setting: str, value: Any) -> None:
561
+ """Sets the configuration property ``setting`` to ``value``."""
466
562
  self.config = {setting: value}
467
563
 
468
- def reset_machine(self):
564
+ def reset_machine(self) -> None:
565
+ """Resets the machine state and Plover's connection with the machine, if
566
+ necessary, and loads all the configuration and dictionaries.
567
+ """
469
568
  self._same_thread_hook(self._update, reset_machine=True)
470
569
 
471
- def load_config(self):
570
+ def load_config(self) -> bool:
571
+ """Loads the Plover configuration file and returns ``True`` if it was
572
+ loaded successfully, ``False`` if not.
573
+ """
472
574
  try:
473
575
  self._config.load()
474
576
  except Exception:
@@ -479,62 +581,92 @@ class StenoEngine:
479
581
  return False
480
582
  return True
481
583
 
482
- def start(self):
584
+ def start(self) -> None:
585
+ """Starts the steno engine."""
483
586
  self._same_thread_hook(self._start)
484
587
 
485
- def quit(self, code=0):
588
+ def quit(self, code: int = 0) -> None:
589
+ """Quits the steno engine, ensuring that all pending tasks are completed
590
+ before exiting.
591
+ """
486
592
  # We need to go through the queue, even when already called
487
593
  # from the engine thread so _quit's return code does break
488
594
  # the thread out of its main loop.
489
595
  self._queue.put((self._quit, (code,), {}))
490
596
 
491
- def restart(self):
597
+ def restart(self) -> None:
598
+ """Quits and restarts the steno engine, ensuring that all pending tasks
599
+ are completed.
600
+ """
492
601
  self.quit(-1)
493
602
 
494
- def join(self):
603
+ def join(self) -> int:
604
+ """Joins any sub-threads if necessary and returns an exit code."""
495
605
  return self.code
496
606
 
497
607
  @with_lock
498
- def lookup(self, translation):
608
+ def lookup(self, translation: Tuple[str, ...]) -> str:
609
+ """Returns the first translation for the steno outline ``translation`` using
610
+ all the filters.
611
+ """
499
612
  return self._dictionaries.lookup(translation)
500
613
 
501
614
  @with_lock
502
- def raw_lookup(self, translation):
615
+ def raw_lookup(self, translation: Tuple[str, ...]) -> str:
616
+ """Like :meth:`lookup`, but without any of the filters."""
503
617
  return self._dictionaries.raw_lookup(translation)
504
618
 
505
619
  @with_lock
506
- def lookup_from_all(self, translation):
620
+ def lookup_from_all(self, translation: Tuple[str, ...]):
621
+ """Returns all translations for the steno outline ``translation`` using
622
+ all the filters.
623
+ """
507
624
  return self._dictionaries.lookup_from_all(translation)
508
625
 
509
626
  @with_lock
510
- def raw_lookup_from_all(self, translation):
627
+ def raw_lookup_from_all(self, translation: Tuple[str, ...]):
628
+ """Like :meth:`lookup_from_all`, but without any of the filters."""
511
629
  return self._dictionaries.raw_lookup_from_all(translation)
512
630
 
513
631
  @with_lock
514
- def reverse_lookup(self, translation):
515
- matches = self._dictionaries.reverse_lookup(translation)
516
- return [] if matches is None else matches
632
+ def reverse_lookup(self, translation: str):
633
+ """Returns the list of steno outlines that translate to ``translation``."""
634
+ return self._dictionaries.reverse_lookup(translation)
517
635
 
518
636
  @with_lock
519
- def casereverse_lookup(self, translation):
520
- matches = self._dictionaries.casereverse_lookup(translation)
521
- return set() if matches is None else matches
637
+ def casereverse_lookup(self, translation: str):
638
+ """Like :meth:`reverse_lookup`, but performs a case-insensitive lookup."""
639
+ return self._dictionaries.casereverse_lookup(translation)
522
640
 
523
641
  @with_lock
524
- def add_dictionary_filter(self, dictionary_filter):
642
+ def add_dictionary_filter(
643
+ self, dictionary_filter: Callable[[Tuple[str, ...], str], bool]
644
+ ) -> None:
645
+ """Adds ``dictionary_filter`` to the list of dictionary filters.
646
+
647
+ See :attr:`StenoDictionaryCollection.filters<plover.steno_dictionary.StenoDictionaryCollection.filters>`
648
+ for more information.
649
+ """
525
650
  self._dictionaries.add_filter(dictionary_filter)
526
651
 
527
652
  @with_lock
528
- def remove_dictionary_filter(self, dictionary_filter):
653
+ def remove_dictionary_filter(
654
+ self, dictionary_filter: Callable[[Tuple[str, ...], str], bool]
655
+ ) -> None:
656
+ """Removes ``dictionary_filter`` from the list of dictionary filters."""
529
657
  self._dictionaries.remove_filter(dictionary_filter)
530
658
 
531
659
  @with_lock
532
- def get_suggestions(self, translation):
660
+ def get_suggestions(self, translation: str):
661
+ """Returns a list of suggestions for the specified ``translation``."""
533
662
  return Suggestions(self._dictionaries).find(translation)
534
663
 
535
664
  @property
536
665
  @with_lock
537
666
  def translator_state(self):
667
+ """A :class:`_State<plover.translation._State>` object containing the
668
+ current state of the translator.
669
+ """
538
670
  return self._translator.get_state()
539
671
 
540
672
  @translator_state.setter
@@ -543,7 +675,13 @@ class StenoEngine:
543
675
  self._translator.set_state(state)
544
676
 
545
677
  @with_lock
546
- def clear_translator_state(self, undo=False):
678
+ def clear_translator_state(self, undo: bool = False) -> None:
679
+ """Resets the translator to an empty state, as if Plover had just started up,
680
+ clearing the entire translation stack.
681
+
682
+ If ``undo`` is ``True``, this also reverts all previous translations on the
683
+ stack (which could include a lot of backspaces).
684
+ """
547
685
  if undo:
548
686
  state = self._translator.get_state()
549
687
  if state.translations:
@@ -552,7 +690,10 @@ class StenoEngine:
552
690
 
553
691
  @property
554
692
  @with_lock
555
- def starting_stroke_state(self):
693
+ def starting_stroke_state(self) -> StartingStrokeState:
694
+ """A :class:`StartingStrokeState` representing the initial state of the
695
+ formatter.
696
+ """
556
697
  return StartingStrokeState(
557
698
  self._formatter.start_attached,
558
699
  self._formatter.start_capitalized,
@@ -567,7 +708,16 @@ class StenoEngine:
567
708
  self._formatter.space_char = state.space_char
568
709
 
569
710
  @with_lock
570
- def add_translation(self, strokes, translation, dictionary_path=None):
711
+ def add_translation(
712
+ self,
713
+ strokes: Tuple[str, ...],
714
+ translation: str,
715
+ dictionary_path: Optional[str] = None,
716
+ ) -> None:
717
+ """Adds a steno entry mapping the steno outline ``strokes`` to
718
+ ``translation`` in the dictionary at ``dictionary_path``, if specified,
719
+ or the first writable dictionary.
720
+ """
571
721
  if dictionary_path is None:
572
722
  dictionary_path = self._dictionaries.first_writable().path
573
723
  self._dictionaries.set(strokes, translation, path=dictionary_path)
@@ -576,6 +726,12 @@ class StenoEngine:
576
726
  @property
577
727
  @with_lock
578
728
  def dictionaries(self):
729
+ """A
730
+ :class:`StenoDictionaryCollection<plover.steno_dictionary.StenoDictionaryCollection>`
731
+ of all the dictionaries Plover has loaded for the current system.
732
+
733
+ This includes disabled dictionaries and dictionaries that failed to load.
734
+ """
579
735
  return self._dictionaries
580
736
 
581
737
  # Hooks.
@@ -588,9 +744,21 @@ class StenoEngine:
588
744
  log.error("hook %r callback %r failed", hook, callback, exc_info=True)
589
745
 
590
746
  @with_lock
591
- def hook_connect(self, hook, callback):
747
+ def hook_connect(self, hook: str, callback: Callable[..., Any]) -> None:
748
+ """Adds ``callback`` to the list of handlers that are called when the ``hook``
749
+ hook gets triggered. Raises a ``KeyError`` if ``hook`` is not in
750
+ :data:`HOOKS`.
751
+
752
+ The expected signature of the callback is depends on the hook; see
753
+ :ref:`engine-hooks` for more information.
754
+ """
592
755
  self._hooks[hook].append(callback)
593
756
 
594
757
  @with_lock
595
- def hook_disconnect(self, hook, callback):
758
+ def hook_disconnect(self, hook: str, callback: Callable[..., Any]) -> None:
759
+ """Removes ``callback`` from the list of handlers that are called when
760
+ the ``hook`` hook is triggered. Raises a ``KeyError`` if ``hook`` is not in
761
+ :data:`HOOKS`, and a ``ValueError`` if ``callback`` was never added as
762
+ a handler in the first place.
763
+ """
596
764
  self._hooks[hook].remove(callback)
plover/formatting.py CHANGED
@@ -370,7 +370,10 @@ class Formatter:
370
370
 
371
371
  def set_output(self, output):
372
372
  """Set the output class."""
373
- noop = lambda x: None
373
+
374
+ def noop(x):
375
+ return None
376
+
374
377
  output_type = self.output_type
375
378
  fields = output_type._fields
376
379
  self._output = output_type(*[getattr(output, f, noop) for f in fields])
@@ -921,7 +924,7 @@ def apply_case(text, case):
921
924
 
922
925
  def apply_mode(text, case, space_char, begin, last_action):
923
926
  # Should title case be applied to the beginning of the next string?
924
- lower_title_case = begin and not last_action.case in (
927
+ lower_title_case = begin and last_action.case not in (
925
928
  Case.CAP_FIRST_WORD,
926
929
  Case.UPPER_FIRST_WORD,
927
930
  )
@@ -4,7 +4,7 @@ _ = __import__(__package__.split(".", 1)[0])._
4
4
  ################################################################################
5
5
  ## Form generated from reading UI file 'about_dialog.ui'
6
6
  ##
7
- ## Created by: Qt User Interface Compiler version 6.10.0
7
+ ## Created by: Qt User Interface Compiler version 6.10.1
8
8
  ##
9
9
  ## WARNING! All changes made in this file will be lost when recompiling UI file!
10
10
  ################################################################################
@@ -4,7 +4,7 @@ _ = __import__(__package__.split(".", 1)[0])._
4
4
  ################################################################################
5
5
  ## Form generated from reading UI file 'add_translation_dialog.ui'
6
6
  ##
7
- ## Created by: Qt User Interface Compiler version 6.10.0
7
+ ## Created by: Qt User Interface Compiler version 6.10.1
8
8
  ##
9
9
  ## WARNING! All changes made in this file will be lost when recompiling UI file!
10
10
  ################################################################################
@@ -4,7 +4,7 @@ _ = __import__(__package__.split(".", 1)[0])._
4
4
  ################################################################################
5
5
  ## Form generated from reading UI file 'add_translation_widget.ui'
6
6
  ##
7
- ## Created by: Qt User Interface Compiler version 6.10.0
7
+ ## Created by: Qt User Interface Compiler version 6.10.1
8
8
  ##
9
9
  ## WARNING! All changes made in this file will be lost when recompiling UI file!
10
10
  ################################################################################