def test_widget_save_merge_1(widget_test): ''' ☑ ★ favorite.json ☑ 🗘 loading.json ☑ ⎕ normal.json ☑ 🛇 read-only.ro ☑ ! invalid.bad ''' # Setup. merge_name = 'favorite + normal + read-only' widget_test.file_dialog.getSaveFileName.return_value = [ expand_path('merge.json') ] # Execution. widget_test.select(range(5)) widget_test.widget.action_MergeDictionaries.trigger() # Check. assert widget_test.file_dialog.mock_calls == [ mock.call.getSaveFileName( parent=widget_test.widget, caption='Merge %s as...' % merge_name, directory=expand_path(merge_name + '.json'), filter=FILE_PICKER_SAVE_FILTER, ) ] assert widget_test.create_dictionary.mock_calls == [ mock.call(expand_path('merge.json'), threaded_save=False) ] steno_dict = widget_test.create_dictionary.return_value assert steno_dict.mock_calls == [ mock.call.update(widget_test.dictionaries.dicts[2]), mock.call.update(widget_test.dictionaries.dicts[1]), mock.call.update(widget_test.dictionaries.dicts[0]), mock.call.save(), ]
def test_model_add_existing(model_test): ''' ☑ ★ user.json ☑ ⎕ commands.json ☐ ⎕ main.json ''' model_test.model.add([expand_path('main.json')]) model_test.check(model_test.initial_state, config_change='reload')
def test_widget_save_copy_1(widget_test): ''' ☑ ★ favorite.json ☑ 🗘 loading.json ☑ ⎕ normal.json ☑ 🛇 read-only.ro ☑ ! invalid.bad ''' # Setup. copy_names = ( expand_path('favorite_copy.json'), '', expand_path('read-only_copy.json'), ) widget_test.file_dialog.getSaveFileName.side_effect = [ [name] for name in copy_names ] steno_dict_copies = ( mock.create_autospec(StenoDictionary), mock.create_autospec(StenoDictionary), ) widget_test.create_dictionary.side_effect = steno_dict_copies # Execution. widget_test.select(range(5)) widget_test.widget.action_CopyDictionaries.trigger() # Check. assert widget_test.file_dialog.mock_calls == [ mock.call.getSaveFileName( parent=widget_test.widget, caption='Save a copy of %s as...' % name, directory=expand_path('%s - Copy.json' % Path(name).stem), filter=FILE_PICKER_SAVE_FILTER, ) for name in ['favorite.json', 'normal.json', 'read-only.ro'] ] assert widget_test.create_dictionary.mock_calls == [ mock.call(name, threaded_save=False) for name in copy_names if name ] assert steno_dict_copies[0].mock_calls == [ mock.call.update(widget_test.dictionaries.dicts[0]), mock.call.save(), ] assert steno_dict_copies[1].mock_calls == [ mock.call.update(widget_test.dictionaries.dicts[2]), mock.call.save(), ]
def accept(self): self._unfocus() strokes = self._strokes() translation = self._translation() if strokes and translation: dictionary = expand_path(self.dictionary.currentText()) self._engine.add_translation(strokes, translation, dictionary=dictionary) super(AddTranslation, self).accept()
def get_dictionary_file_names(self): filenames = [] if self._config.has_section(DICTIONARY_CONFIG_SECTION): options = [x for x in self._config.options(DICTIONARY_CONFIG_SECTION) if x.startswith(DICTIONARY_FILE_OPTION)] options.sort(key=_dict_entry_key) filenames = [self._config.get(DICTIONARY_CONFIG_SECTION, o) for o in options] if not filenames: filenames = DEFAULT_DICTIONARIES filenames = [expand_path(path) for path in filenames] return filenames
def get_dictionary_file_names(self): filenames = [] if self._config.has_section(DICTIONARY_CONFIG_SECTION): options = [ x for x in self._config.options(DICTIONARY_CONFIG_SECTION) if x.startswith(DICTIONARY_FILE_OPTION) ] options.sort(key=_dict_entry_key) filenames = [ self._config.get(DICTIONARY_CONFIG_SECTION, o) for o in options ] if not filenames: filenames = DEFAULT_DICTIONARIES filenames = [expand_path(path) for path in filenames] return filenames
def test_model_add_new_1(model_test): ''' ☑ ★ user.json ☐ ⎕ commands.json ☑ 🗘 main.json ''' model_test.model.add([expand_path('read-only.ro')]) model_test.check( ''' ☑ 🗘 read-only.ro ☑ ★ user.json ☐ ⎕ commands.json ☑ 🗘 main.json ''', config_change='update', layout_change=True, undo_change=True, )
def steno_dictionaries_from_state(state_str, existing_dictionaries=None): new_dictionaries = [] for enabled, icon, path in parse_state(state_str): if icon == 'loading': continue path = expand_path(path) if existing_dictionaries is None: steno_dict = None else: steno_dict = existing_dictionaries.get(path) if steno_dict is None: if icon == 'error' or path.endswith('.bad'): steno_dict = ErroredDictionary(path, INVALID_EXCEPTION) else: steno_dict = StenoDictionary() steno_dict.path = path steno_dict.readonly = (icon == 'readonly' or path.endswith('.ro') or path.startswith('asset:')) steno_dict.enabled = enabled new_dictionaries.append(steno_dict) return new_dictionaries
def setData(self, index, value, role=Qt.EditRole, record=True): assert role == Qt.EditRole row = index.row() column = index.column() old_item = self._entries[row] strokes = old_item.strokes steno, translation, dictionary = old_item if column == _COL_STENO: strokes = normalize_steno(value.strip()) steno = '/'.join(strokes) if not steno or steno == old_item.steno: return False elif column == _COL_TRANS: translation = unescape_translation(value.strip()) if translation == old_item.translation: return False elif column == _COL_DICT: path = expand_path(value) for dictionary in self._dictionary_list: if dictionary.path == path: break if dictionary == old_item.dictionary: return False try: del old_item.dictionary[old_item.strokes] except KeyError: pass if not old_item.strokes and not old_item.translation: # Merge operations when editing a newly added row. if self._operations and self._operations[-1] == [(None, old_item)]: self._operations.pop() old_item = None new_item = DictionaryItem(steno, translation, dictionary) self._entries[row] = new_item dictionary[strokes] = translation if record: self._operations.append((old_item, new_item)) self.dataChanged.emit(index, index) return True
def get_dictionary_file_names(self): system_name = self.get_system_name() try: system = registry.get_plugin('system', system_name).resolve() except: log.error("invalid system name: %s", system_name, exc_info=True) return [] section = SYSTEM_CONFIG_SECTION % system_name option = SYSTEM_DICTIONARIES_OPTION filenames = self._get(section, option, None) if filenames is None: filenames = self._legacy_get_dictionary_file_names() if filenames is None: filenames = system.DEFAULT_DICTIONARIES else: try: filenames = tuple(json.loads(filenames)) except ValueError as e: log.error("invalid system dictionaries, resetting to default", exc_info=True) self.set_dictionary_file_names(None) filenames = system.DEFAULT_DICTIONARIES return [expand_path(path) for path in filenames]
def setData(self, index, value, role=Qt.EditRole, record=True): assert role == Qt.EditRole row = index.row() column = index.column() old_item = self._entries[row] strokes, translation, dictionary = old_item if column == _COL_STENO: strokes = normalize_steno(value.strip()) if not strokes or strokes == old_item.strokes: return False elif column == _COL_TRANS: translation = unescape_translation(value.strip()) if translation == old_item.translation: return False elif column == _COL_DICT: path = expand_path(value) for dictionary in self._dictionary_list: if dictionary.get_path() == path: break if dictionary == old_item.dictionary: return False try: del old_item.dictionary[old_item.strokes] except KeyError: pass if not old_item.strokes and not old_item.translation: # Merge operations when editing a newly added row. if self._operations and self._operations[-1] == [(None, old_item)]: self._operations.pop() old_item = None new_item = DictionaryItem(strokes, translation, dictionary) self._entries[row] = new_item dictionary[strokes] = translation if record: self._operations.append((old_item, new_item)) self.dataChanged.emit(index, index) return True
def test_widget_save_merge_2(widget_test): ''' ☑ ★ favorite.json ☑ 🗘 loading.json ☑ ⎕ normal.json ☑ 🛇 read-only.ro ☑ ! invalid.bad ''' # Setup. merge_name = 'favorite + normal' widget_test.file_dialog.getSaveFileName.return_value = [''] # Execution. widget_test.select([0, 2]) widget_test.widget.action_MergeDictionaries.trigger() # Check. assert widget_test.file_dialog.mock_calls == [ mock.call.getSaveFileName( parent=widget_test.widget, caption='Merge %s as...' % merge_name, directory=expand_path(merge_name + '.json'), filter=FILE_PICKER_SAVE_FILTER, ) ] assert widget_test.create_dictionary.mock_calls == []
if sys.platform.startswith('win32'): ABS_PATH = os.path.normcase(r'c:/foo/bar') else: ABS_PATH = '/foo/bar' DEFAULT_KEYMAP = Keymap(Keyboard.get_keys(), english_stenotype.KEYS + Keyboard.get_actions()) DEFAULT_KEYMAP.set_mappings(english_stenotype.KEYMAPS['Keyboard']) DEFAULTS = { 'space_placement': 'Before Output', 'start_attached': False, 'start_capitalized': False, 'undo_levels': config.DEFAULT_UNDO_LEVELS, 'log_file_name': expand_path('strokes.log'), 'enable_stroke_logging': False, 'enable_translation_logging': False, 'start_minimized': False, 'show_stroke_display': False, 'show_suggestions_display': False, 'translation_frame_opacity': 100, 'classic_dictionaries_display_order': False, 'enabled_extensions': set(), 'auto_start': False, 'machine_type': 'Keyboard', 'machine_specific_options': { 'arpeggiate': False }, 'system_name': config.DEFAULT_SYSTEM_NAME, 'system_keymap': DEFAULT_KEYMAP, 'dictionaries': [DictionaryConfig(p) for p in english_stenotype.DEFAULT_DICTIONARIES] }
def get_log_file_name(self): filename = self._get(LOGGING_CONFIG_SECTION, LOG_FILE_OPTION, DEFAULT_LOG_FILE) return expand_path(filename)
def getter(config, key): return expand_path(config._config[section][option])
def __new__(cls, path, enabled=True): return super(DictionaryConfig, cls).__new__(cls, expand_path(path), enabled)
def __new__(cls, path, enabled=True): return super().__new__(cls, expand_path(path), enabled)
class Config: def __init__(self, path=None): self._config = None self._cache = {} # A convenient place for other code to store a file name. self.path = path self.clear() def load(self): self.clear() with open(self.path, encoding='utf-8') as fp: try: self._config.read_file(fp) except configparser.Error as e: raise InvalidConfigurationError(str(e)) def clear(self): self._config = configparser.RawConfigParser() self._cache.clear() def save(self): with resource_update(self.path) as temp_path: with open(temp_path, mode='w', encoding='utf-8') as fp: self._config.write(fp) def _set(self, section, option, value): if not self._config.has_section(section): self._config.add_section(section) self._config.set(section, option, value) # Note: order matters, e.g. machine_type comes before # machine_specific_options and system_keymap because # the latter depend on the former. _OPTIONS = OrderedDict((opt.name, opt) for opt in [ # Output. choice_option('space_placement', ( 'Before Output', 'After Output'), OUTPUT_CONFIG_SECTION), boolean_option('start_attached', False, OUTPUT_CONFIG_SECTION), boolean_option('start_capitalized', False, OUTPUT_CONFIG_SECTION), int_option('undo_levels', DEFAULT_UNDO_LEVELS, MINIMUM_UNDO_LEVELS, None, OUTPUT_CONFIG_SECTION), int_option( 'time_between_key_presses', DEFAULT_TIME_BETWEEN_KEY_PRESSES, MINIMUM_TIME_BETWEEN_KEY_PRESSES, None, OUTPUT_CONFIG_SECTION), # Logging. path_option('log_file_name', expand_path('strokes.log'), LOGGING_CONFIG_SECTION, 'log_file'), boolean_option('enable_stroke_logging', False, LOGGING_CONFIG_SECTION), boolean_option('enable_translation_logging', False, LOGGING_CONFIG_SECTION), # GUI. boolean_option('start_minimized', False, 'Startup', 'Start Minimized'), boolean_option('show_stroke_display', False, 'Stroke Display', 'show'), boolean_option('show_suggestions_display', False, 'Suggestions Display', 'show'), opacity_option('translation_frame_opacity', 'Translation Frame', 'opacity'), boolean_option('classic_dictionaries_display_order', False, 'GUI'), # Plugins. enabled_extensions_option(), # Machine. boolean_option('auto_start', False, MACHINE_CONFIG_SECTION), plugin_option('machine_type', 'machine', 'Keyboard', MACHINE_CONFIG_SECTION), machine_specific_options(), # System. plugin_option('system_name', 'system', DEFAULT_SYSTEM_NAME, 'System', 'name'), system_keymap_option(), dictionaries_option(), ]) def _lookup(self, key): name = key[0] if isinstance(key, tuple) else key opt = self._OPTIONS[name] if opt.full_key is not None: key = opt.full_key(self, key) return key, opt def __getitem__(self, key): key, opt = self._lookup(key) if key in self._cache: return self._cache[key] try: value = opt.validate(self, key, opt.getter(self, key)) except (configparser.NoOptionError, KeyError): value = opt.default(self, key) except InvalidConfigOption as e: log.error('invalid value for %r option', opt.name, exc_info=True) value = e.fixed_value self._cache[key] = value return value def __setitem__(self, key, value): key, opt = self._lookup(key) value = opt.validate(self._config, key, value) opt.setter(self, key, value) self._cache[key] = value def as_dict(self): return {opt.name: self[opt.name] for opt in self._OPTIONS.values()} def update(self, **kwargs): new_settings = [] new_config = ChainMap({}, self) for opt in self._OPTIONS.values(): if opt.name in kwargs: key = opt.name if opt.full_key is not None: key = opt.full_key(new_config, key) value = opt.validate(new_config, key, kwargs[opt.name]) new_settings.append((opt, key, value)) new_config[opt.name] = value for opt, key, value in new_settings: opt.setter(self, key, value) self._cache[key] = value
def on_path_edited(self): self.valueChanged.emit(expand_path(self.path.text()))
def get_ime_words_csv_file(self): filename = self._get(IME_CONFIG_SECTION, IME_WORDS_CSV_FILE_OPTION, DEFAULT_IME_WORDS_CSV_FILE) return expand_path(filename)
def get_ime_exe_file(self): filename = self._get(IME_CONFIG_SECTION, IME_EXE_FILE_OPTION, DEFAULT_IME_EXE_FILE) return expand_path(filename)
DEFAULT_KEYMAP = Keymap(Keyboard.get_keys(), english_stenotype.KEYS + Keyboard.get_actions()) DEFAULT_KEYMAP.set_mappings(english_stenotype.KEYMAPS['Keyboard']) DEFAULTS = { 'space_placement': 'Before Output', 'start_attached': False, 'start_capitalized': False, 'undo_levels': config.DEFAULT_UNDO_LEVELS, 'log_file_name': expand_path('strokes.log'), 'enable_stroke_logging': False, 'enable_translation_logging': False, 'start_minimized': False, 'show_stroke_display': False, 'show_suggestions_display': False, 'translation_frame_opacity': 100, 'classic_dictionaries_display_order': False, 'enabled_extensions':