def _migrate_light_scripts(self): if 'light_scripts' not in self.fc: return YamlInterface.rename_key('light_scripts', 'shows', self.fc, self.log) for show_contents in self.fc['shows'].values(): self._convert_tocks_to_time(show_contents) for step in show_contents: if 'color' in step: step['color'] = self._get_color(step['color']) if len(str(step['color'])) > 2: YamlInterface.rename_key('color', '(leds)', step, self.log) step['leds'] = CommentedMap() YamlInterface.copy_with_comments(step, '(leds)', step['leds'], '(leds)', True, self.log) else: YamlInterface.rename_key('color', '(lights)', step, self.log) step['lights'] = CommentedMap() YamlInterface.copy_with_comments(step, '(lights)', step['lights'], '(lights)', True, self.log)
def _remove_key(self, first_key, key): # actually removes the key from the dict, with nested dicts only # (no lists in there) if not key: # single item if first_key in self.fc: YamlInterface.del_key_with_comments(self.fc, first_key, self.log) return True try: if self.fc[first_key].mlget(key, list_ok=True) is not None: # mlget just verifies with a nested dict / list # index that the key is found. final_key = key.pop(-1) dic = self.fc[first_key] while key: # Loop to get the parent container of the # lowest level key dic = dic[key.pop(0)] YamlInterface.del_key_with_comments(dic, final_key, self.log) return True except (KeyError, IndexError): pass return False
def _create_window_slide(self): if 'window' in self.fc and 'elements' in self.fc['window']: elements = self.fc['window']['elements'] if isinstance(elements, dict): elements = [elements] if 'slides' not in self.fc: self.log.debug("Creating 'slides:' section") self.fc['slides'] = CommentedMap() slide_name = V4Migrator._get_slide_name('window') self.log.debug("Creating slide: %s with %s display widget(s) from " "the old window: config", slide_name, len(elements)) self.log.debug("Adding '%s' slide", slide_name) self.fc['slides'][slide_name] = CommentedMap() self.fc['slides'][slide_name] = ( self._migrate_elements(elements, 'window')) YamlInterface.del_key_with_comments(self.fc['window'], 'elements', self.log) if 'slide_player' not in self.fc: self.fc['slide_player'] = CommentedMap() self.log.debug("Creating slide_player: section") self.log.debug("Creating slide_player:machine_reset_phase3: entry" "to show slide '%s' on boot", slide_name) self.fc['slide_player']['machine_reset_phase_3'] = CommentedMap() self.fc['slide_player']['machine_reset_phase_3'][slide_name] = \ CommentedMap() self.fc['slide_player']['machine_reset_phase_3'][slide_name][ 'target'] = 'window'
def _migrate_show_file(self): self.log.debug("Migrating show file: %s", self.file_name) show_name_stub = os.path.splitext(os.path.split(self.file_name)[1])[0] self._add_show_version() # Convert tocks to time self._convert_tocks_to_time(self.fc) # migrate the components in each step self.log.debug("Converting settings for each show step") slide_num = 0 for i, step in enumerate(self.fc): self._remove_tags(step) if 'display' in step: self.log.debug("Show step %s: Converting 'display' section", i + 1) found_transition = False for widget in step['display']: if 'transition' in widget: found_transition = True break if found_transition: step['display'] = CommentedMap( widgets=self._migrate_elements(step['display'])) for widget in step['display']['widgets']: self._convert_tokens(widget) if 'transition' in widget: YamlInterface.copy_with_comments( widget, 'transition', step['display'], 'transition', True, self.log) else: step['display'] = self._migrate_elements(step['display']) self._convert_tokens(step['display']) YamlInterface.rename_key('display', 'slides', step) slide_num += 1 old_slides = step['slides'] step['slides'] = CommentedMap() step['slides']['{}_slide_{}'.format(show_name_stub, slide_num)] = old_slides return True
def _convert_show_call_to_tokens(cls, settings): token_list = ['light', 'lights', 'leds', 'led'] for token in token_list: if token in settings: if 'show_tokens' not in settings: settings['show_tokens'] = CommentedMap() YamlInterface.copy_with_comments(settings, token, settings['show_tokens'], token, True)
def _recursive_rename(self, old, new, target): if isinstance(target, list): for item in target: self._recursive_rename(old, new, item) elif isinstance(target, dict): if old in target: YamlInterface.rename_key(old, new, target, self.log) for item in target.values(): self._recursive_rename(old, new, item)
def _migrate_shot_profiles(self): if 'shot_profiles' not in self.fc: return for settings in self.fc['shot_profiles'].values(): if 'states' in settings: for dummy_i, state_settings in enumerate(settings['states']): if 'loops' in state_settings and state_settings['loops']: state_settings['loops'] = -1 YamlInterface.rename_key('light_script', 'show', state_settings)
def _remove_tags(self, dic): found = False for v in dic.values(): if isinstance(v, dict): for k1 in v.keys(): if k1.startswith('tag|'): YamlInterface.rename_key(k1, k1.strip('tag|'), v) found = True break if found: self._remove_tags(dic)
def _migrate_mode_timers(self): if 'timers' in self.fc: for timer_name in self.fc['timers']: if "start_paused" in self.fc['timers'][timer_name]: if not self.fc['timers'][timer_name]['start_paused']: self.fc['timers'][timer_name]["start_running"] = True elif ("start_running" not in self.fc['timers'][timer_name] or not self.fc['timers'][timer_name]["start_running"]): self.fc['timers'][timer_name]["start_running"] = False else: raise ValueError("Both start_paused and start_running are true. This is impossible") YamlInterface.del_key_with_comments(self.fc['timers'][timer_name], 'start_paused', self.log)
def _migrate_fonts(self): # Fonts to widget_styles was already renamed, now update contents if 'widget_styles' in self.fc: self.log.debug("Converting widget_styles: from the old fonts: " "settings") for settings in self.fc['widget_styles'].values(): YamlInterface.rename_key('size', 'font_size', settings, self.log) YamlInterface.rename_key('file', 'font_name', settings, self.log) YamlInterface.rename_key('crop_top', 'adjust_top', settings, self.log) YamlInterface.rename_key('crop_bottom', 'adjust_bottom', settings, self.log) if 'font_name' in settings: self.log.debug("Converting font_name: from file to name") settings['font_name'] = os.path.splitext( settings['font_name'])[0] if self.base_name == V4Migrator.MAIN_CONFIG_FILE: if 'widget_styles' not in self.fc: self.log.debug("Creating old default font settings as " "widget_styles: section") self.fc['widget_styles'] = self._get_old_default_widget_styles() else: for k, v in self._get_old_default_widget_styles().items(): if k not in self.fc['widget_styles']: self.fc['widget_styles'][k] = v self.log.debug("Merging old built-in font settings '%s' " "into widget_styles: section", k)
def _migrate_animation_assets(self): if 'animations' in self.fc: self.log.debug("Converting assets:animations to assets:images") if 'images' in self.fc: self.log.debug("Merging animations: into current " "asset:images:") YamlInterface.copy_with_comments(self.fc, 'animations', self.fc, 'images', True, self.log) else: YamlInterface.rename_key('animations', 'images', self.fc, self.log)
def send_slide_to_mc(self, value): del value try: settings = YamlInterface.process(self.slide_player_code.text) except Exception as e: msg = str(e).replace('"', '\n') Popup(title='Error in your config', content=Label(text=msg, size=(750, 350)), size_hint=(None, None), size=(Window.width, 400)).open() return try: settings = (self.slide_player.validate_config_entry( settings, 'slides')) except Exception as e: msg = str(e).replace('"', '\n') Popup(title='Error in your config', content=Label(text=msg, size=(750, 350)), size_hint=(None, None), size=(Window.width, 400)).open() return if self._initialized: self.slide_player.clear_context('imc') else: self._initialized = True self.slide_player.play(settings, 'imc', 100) self.clock.loop.run_until_complete( asyncio.sleep(.1, loop=self.clock.loop))
def _migrate_animation(self, element): self.log.debug("Converting 'animation' display_element to animated " "'image' widget") element['type'] = 'image' YamlInterface.rename_key('play_now', 'auto_play', element, self.log) YamlInterface.rename_key('animation', 'image', element, self.log) element.pop('drop_frames', None) self.log.debug('Converting animated image loops: setting') if element['loops']: element['loops'] = -1 else: element['loops'] = 0 return element
def load_config_spec(self): """Load config spec.""" if ConfigValidator.class_cache: self.config_spec = ConfigValidator.class_cache return cache_file = os.path.join(self.get_cache_dir(), "config_spec.mpf_cache") config_spec_file = os.path.abspath( os.path.join(mpf.core.__path__[0], os.pardir, "config_spec.yaml")) if os.path.isfile(cache_file) and os.path.getmtime( cache_file) >= os.path.getmtime(config_spec_file): try: with open(cache_file, 'rb') as f: self.config_spec = pickle.load(f) # nosec ConfigValidator.class_cache = deepcopy(self.config_spec) return except Exception: # noqa pass with open(config_spec_file, 'rb') as f: config_str = f.read() config = YamlInterface.process(config_str) config = self._process_config_spec(config, "root") self.config_spec = config self.machine.load_external_platform_config_specs() with open(cache_file, 'wb') as f: pickle.dump(config, f, protocol=4) self.log.info('Config spec file cache created: %s', cache_file) ConfigValidator.class_cache = deepcopy(self.config_spec)
def __init__(self, uri, config_type, source=None, version=None, local=True, extra_sys_path=None): self.uri = uri self.version = version self.path = uris.to_fs_path(uri) self.filename = os.path.basename(self.path) self._local = local self._source = source self._extra_sys_path = extra_sys_path or [] self._config_simple = {} self._config_roundtrip = {} self._last_config_simple = {} self._last_config_roundtrip = {} self._loader_roundtrip = YamlRoundtrip() self._loader_simple = YamlInterface() self.config_type = config_type
def test_yaml_patches(self): # tests our patches to the yaml processor config = """ str_1: +1 str_2: 032 str_3: on str_4: off str_5: 123e45 bool_1: yes bool_2: no bool_3: true bool_4: false str_6: hi int_1: 123 float_1: 1.0 """ parsed_config = YamlInterface.process(config, True) for k, v in parsed_config.items(): if not type(v) is eval(k.split('_')[0]): raise AssertionError('YAML value "{}" is {}, not {}'.format( v, type(v), eval(k.split('_')[0])))
def load_mode_config_spec(cls, mode_string, config_spec): """Load config specs for a mode.""" if '_mode_settings' not in cls.config_spec: cls.config_spec['_mode_settings'] = {} if mode_string not in cls.config_spec['_mode_settings']: cls.config_spec['_mode_settings'][ mode_string] = YamlInterface.process(config_spec)
def load_mode_config_spec(self, mode_string, config_spec): """Load config specs for a mode.""" if '_mode_settings' not in self.config_spec: self.config_spec['_mode_settings'] = {} if mode_string not in self.config_spec['_mode_settings']: config = YamlInterface.process(config_spec) self.config_spec['_mode_settings'][mode_string] = self._process_config_spec(config, mode_string)
def _migrate_switches(self): if 'switches' not in self.fc: return for switch_settings in self.fc['switches'].values(): YamlInterface.rename_key('activation_events', 'events_when_activated', switch_settings, self.log) YamlInterface.rename_key('deactivation_events', 'events_when_deactivated', switch_settings, self.log) if 'debounce' in switch_settings: if switch_settings['debounce']: switch_settings['debounce'] = 'normal' else: switch_settings['debounce'] = 'quick'
def _do_rename(self): for rename in self.renames: if len(rename['old']) > 1: # searching for nested key found_section = Util.get_from_dict(self.fc, rename['old'][:-1]) if not found_section: continue self.log.debug('Renaming key: %s: -> %s:', ':'.join(rename['old']), rename['new']) YamlInterface.rename_key(rename['old'][-1], rename['new'], found_section) else: # searching for a single key anywhere self._recursive_rename(rename['old'][0], rename['new'], self.fc)
def load_external_platform_config_specs(self, config): """Load config spec for external platforms.""" for platform_entry in iter_entry_points(group='mpf.platforms'): config_spec = platform_entry.load().get_config_spec() if config_spec: # add specific config spec if platform has any config[config_spec[1]] = self._process_config_spec( YamlInterface.process(config_spec[0]), config_spec[1]) return config
def _do_lowercase(self, dic=None): # recurcisely converts all keys in dicts and nested dicts if not dic: dic = self.fc key_list = list(dic.keys()) for key in key_list: try: YamlInterface.rename_key(key, key.lower(), dic, self.log) except AttributeError: pass try: if isinstance(dic[key.lower()], dict): self._do_lowercase(dic[key.lower()]) except AttributeError: if isinstance(dic[key], dict): self._do_lowercase(dic[key])
def load_show_from_disk(self): """Load show from disk.""" show_version = YamlInterface.get_show_file_version(self.file) if show_version != int(__show_version__): # pragma: no cover raise ValueError("Show file {} cannot be loaded. MPF v{} requires " "#show_version={}".format(self.file, __version__, __show_version__)) return FileManager.load(self.file)
def load_device_config_specs(config_spec, machine_config): """Load device config specs.""" for device_type in machine_config['mpf']['device_modules'].values(): device_cls = Util.string_to_class(device_type) # type: Device if device_cls.get_config_spec(): # add specific config spec if device has any config_spec[device_cls.config_section] = ConfigSpecLoader.process_config_spec( YamlInterface.process(device_cls.get_config_spec()), device_cls.config_section) return config_spec
def _convert_tocks_to_time(self, show_steps): self.log.debug('Converting "tocks:" to "time:" and cascading entries ' 'to the next step (since time: is for the current ' 'step versus tocks: being for the previous step)') previous_tocks = 0 for i, step in enumerate(show_steps): previous_tocks = step['tocks'] if not i: step['tocks'] = 0 else: step['tocks'] = '+{}'.format(previous_tocks) YamlInterface.rename_key('tocks', 'time', step, self.log) if len(show_steps) > 1: show_steps.append(CommentedMap()) show_steps[-1]['time'] = '+{}'.format(previous_tocks) return show_steps
def load_device_config_specs(self, config_spec, machine_config): """Load device config specs.""" for device_type in machine_config['mpf']['device_modules']: device_cls = Util.string_to_class(device_type) if device_cls.get_config_spec(): # add specific config spec if device has any config_spec[device_cls.config_section] = self._process_config_spec( YamlInterface.process(device_cls.get_config_spec()), device_cls.config_section) return config_spec
def load_mode_config_spec(self, mode_string, config_spec): """Load config specs for a mode.""" if '_mode_settings' not in self.config_spec: self.config_spec['_mode_settings'] = {} if mode_string not in self.config_spec['_mode_settings']: if isinstance(config_spec, dict): self.config_spec['_mode_settings'][mode_string] = \ ConfigSpecLoader.process_config_spec(config_spec, mode_string) else: config = YamlInterface.process(config_spec) self.config_spec['_mode_settings'][mode_string] = \ ConfigSpecLoader.process_config_spec(config, mode_string)
def _migrate_assets(self, section_name): if section_name in self.fc: keys_to_keep = set(self.mpf_config_spec[section_name].keys()) empty_entries = set() self.log.debug("Converting %s: section", section_name) if self.fc[section_name]: for name, settings in self.fc[section_name].items(): self.log.debug("Converting %s:%s:", section_name, name) if isinstance(settings, dict): keys = set(settings.keys()) keys_to_remove = keys - keys_to_keep for key in keys_to_remove: YamlInterface.del_key_with_comments(settings, key, self.log) if not settings: self.log.debug("%s:%s: is now empty. Will remove it.", section_name, name) empty_entries.add(name) for name in empty_entries: YamlInterface.del_key_with_comments(self.fc[section_name], name, self.log) if len(self.fc[section_name]) == 0: self.log.debug("%s: is now empty. Will remove it.", section_name) YamlInterface.del_key_with_comments(self.fc, section_name, self.log)
def _migrate_element_y_and_anchor(self, element, display, height): if 'y' in element: old_y = element['y'] element['y'] *= -1 else: old_y = 'None' if 'anchor_y' not in element and 'y' in element: element['anchor_y'] = 'top' try: if 'anchor_y' not in element or element['anchor_y'] == 'bottom': element['y'] = self._format_anchor_and_value('bottom', element['y']) elif element['anchor_y'] in ('middle', 'center'): element['y'] = self._format_anchor_and_value('middle', element['y']) elif element['anchor_y'] == 'top': element['y'] = self._format_anchor_and_value('top', element['y']) self.log.debug("Changing y:%s to y:%s (Based on anchor_y:%s" "and %s height:%s)", old_y, element['y'], element['anchor_y'], display, height) except KeyError: pass try: if element['anchor_y'] in ('middle', 'center'): YamlInterface.del_key_with_comments(element, 'anchor_y', self.log) except KeyError: pass if ('anchor_y' in element and 'y' not in element and element['anchor_y'] != 'middle'): element['y'] = element['anchor_y'] return element
def _migrate_element_x_and_anchor(self, element, display, width): if 'x' in element: old_x = element['x'] else: old_x = 'None' if 'anchor_x' not in element and 'x' in element: element['anchor_x'] = 'left' try: if 'anchor_x' not in element or element['anchor_x'] == 'left': element['x'] = self._format_anchor_and_value('left', element['x']) elif element['anchor_x'] in ('middle', 'center'): element['x'] = self._format_anchor_and_value('center', element['x']) elif element['anchor_x'] == 'right': element['x'] = self._format_anchor_and_value('right', element['x']) self.log.debug("Changing x:%s to x:%s (Based on anchor_x:%s" "and %s width:%s)", old_x, element['x'], element['anchor_x'], display, width) except KeyError: pass try: if element['anchor_x'] in ('middle', 'center'): YamlInterface.del_key_with_comments(element, 'anchor_x', self.log) except KeyError: pass if ('anchor_x' in element and 'x' not in element and element['anchor_x'] != 'center'): element['x'] = element['anchor_x'] return element