Esempio n. 1
0
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(),
    ]
Esempio n. 2
0
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')
Esempio n. 3
0
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(),
    ]
Esempio n. 4
0
 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()
Esempio n. 5
0
 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()
Esempio n. 6
0
 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
Esempio n. 7
0
 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
Esempio n. 8
0
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,
    )
Esempio n. 9
0
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
Esempio n. 10
0
 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
Esempio n. 11
0
 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]
Esempio n. 12
0
 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]
Esempio n. 13
0
 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
Esempio n. 14
0
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 == []
Esempio n. 15
0

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]
}
Esempio n. 16
0
 def get_log_file_name(self):
     filename = self._get(LOGGING_CONFIG_SECTION, LOG_FILE_OPTION,
                          DEFAULT_LOG_FILE)
     return expand_path(filename)
Esempio n. 17
0
 def get_log_file_name(self):
     filename = self._get(LOGGING_CONFIG_SECTION, LOG_FILE_OPTION,
                          DEFAULT_LOG_FILE)
     return expand_path(filename)
Esempio n. 18
0
 def getter(config, key):
     return expand_path(config._config[section][option])
Esempio n. 19
0
 def __new__(cls, path, enabled=True):
     return super(DictionaryConfig, cls).__new__(cls, expand_path(path),
                                                 enabled)
Esempio n. 20
0
 def __new__(cls, path, enabled=True):
     return super().__new__(cls, expand_path(path), enabled)
Esempio n. 21
0
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
Esempio n. 22
0
 def __new__(cls, path, enabled=True):
     return super().__new__(cls, expand_path(path), enabled)
Esempio n. 23
0
 def on_path_edited(self):
     self.valueChanged.emit(expand_path(self.path.text()))
Esempio n. 24
0
 def getter(config, key):
     return expand_path(config._config[section][option])
Esempio n. 25
0
 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)
Esempio n. 26
0
 def get_ime_exe_file(self):
     filename = self._get(IME_CONFIG_SECTION, IME_EXE_FILE_OPTION,
                          DEFAULT_IME_EXE_FILE)
     return expand_path(filename)
Esempio n. 27
0
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':
Esempio n. 28
0
 def on_path_edited(self):
     self.valueChanged.emit(expand_path(self.path.text()))
Esempio n. 29
0
 def __new__(cls, path, enabled=True):
     return super(DictionaryConfig, cls).__new__(cls, expand_path(path), enabled)