def check_config_file_version(filename): """Check to see if the version of the file name passed matches the config version MPF needs. Args: filename: The file with path to check. Raises: exception if the version of the file doesn't match what MPF needs. """ filename = FileManager.locate_file(filename) file_interface = FileManager.get_file_interface(filename) file_version = file_interface.get_config_file_version(filename) if file_version != int(__config_version__): log.error("Config file %s is version %s. MPF %s requires " "version %s", filename, file_version, __version__, __config_version__) log.error("Use the Config File Migrator to automatically " "migrate your config file to the latest version.") log.error("Migration tool: https://missionpinball.com/docs/tools/config-file-migrator/") log.error("More info on config version %s: " "https://missionpinball.com/docs/configuration-file" "-reference/important-config-file-concepts/config_version/config-version-%s/", __config_version__, __config_version__) return False else: return True
def migrate_files(self): """Migrate files in machine.""" # We need to migrate show files after config files since we need # to pull some things out of the configs for the shows. round2_files = list() for file in self.file_list: file_content = FileManager.load(file) if isinstance(file_content, CommentedMap): migrated_content = self.migrator.migrate_file( file, file_content) if migrated_content: self.num_config_files += 1 self.save_file(file, migrated_content) else: round2_files.append(file) for file in round2_files: file_content = FileManager.load(file) migrated_content = self.migrator.migrate_file(file, file_content) if migrated_content: self.num_show_files += 1 self.save_file(file, migrated_content) self.log.info( "DONE! Migrated %s config file(s) and %s show file(s) in" " %ss", self.num_config_files, self.num_show_files, round(time.time() - self.start_time, 2)) self.log.info("Detailed log file is in %s.", os.path.join(self.machine_path, 'logs')) self.log.info("Original YAML files are in %s", self.backup_folder)
def _writing_thread(self): # pragma: no cover while not self.machine.thread_stopper.is_set(): if not self._dirty.wait(1): continue self._dirty.clear() data = copy.deepcopy(self.data) self.debug_log("Writing %s to: %s", self.name, self.filename) # save data FileManager.save(self.filename, data)
def load_config_spec(self): """Load config spec.""" cache_file = os.path.join(self.get_cache_dir(), "config_spec.mpf_cache") config_spec_file = self._get_config_spec_file() stats_config_spec_file = os.stat(config_spec_file) if self._load_cache and os.path.isfile(cache_file) and \ os.path.getmtime(cache_file) == stats_config_spec_file.st_mtime: try: with open(cache_file, 'rb') as f: return pickle.load(f) # nosec except Exception: # noqa pass config = FileManager.load(config_spec_file, False, True) config = ConfigSpecLoader.process_config_spec(config, "root") config = ConfigSpecLoader.load_external_platform_config_specs(config) if self._store_cache: with open(cache_file, 'wb') as f: pickle.dump(config, f, protocol=4) os.utime(cache_file, ns=(stats_config_spec_file.st_atime_ns, stats_config_spec_file.st_mtime_ns)) self.log.info('Config spec file cache created: %s', cache_file) return config
def _load_config_file_and_return_loaded_files( self, filename, config_type: str, ignore_unknown_sections=False, config_spec=None) -> \ Tuple[dict, Dict[str, Tuple[float, int]]]: # pragma: no cover """Load a config file and return loaded files.""" # config_type is str 'machine' or 'mode', which specifies whether this # file being loaded is a machine config or a mode config file expected_version_str = ConfigProcessor.get_expected_version(config_type) config = FileManager.load(filename, expected_version_str, True) subfiles = {} if not config: return {}, {} self.log.info('Loading config: %s', filename) if config_type in ("machine", "mode"): self._check_sections(config_spec, config, config_type, filename, ignore_unknown_sections) try: if 'config' in config: path = os.path.split(filename)[0] for file in Util.string_to_event_list(config['config']): full_file = os.path.join(path, file) subfiles[str(full_file)] = (os.path.getmtime(full_file), os.path.getsize(full_file)) subconfig, subsubfiles = self._load_config_file_and_return_loaded_files(full_file, config_type, config_spec=config_spec) subfiles.update(subsubfiles) config = Util.dict_merge(config, subconfig) return config, subfiles except TypeError: return {}, {}
def _load(self): self.debug_log("Loading %s from %s", self.name, self.filename) if os.path.isfile(self.filename): self.data = FileManager.load(self.filename, halt_on_error=False) else: self.debug_log("Didn't find the %s file. No prob. We'll create " "it when we save.", self.name)
def _writing_thread(self): # pragma: no cover # prevent early writes at start-up time.sleep(self.min_wait_secs) while not self.machine.thread_stopper.is_set(): if not self._dirty.wait(1): continue self._dirty.clear() data = copy.deepcopy(self.data) self.debug_log("Writing %s to: %s", self.name, self.filename) # save data FileManager.save(self.filename, data) # prevent too many writes time.sleep(self.min_wait_secs) # if dirty write data one last time during shutdown if self._dirty.is_set(): FileManager.save(self.filename, data)
def _load_config_file_and_return_loaded_files( self, filename, config_type: str, ignore_unknown_sections=False ) -> Tuple[dict, List[str]]: # pragma: no cover """Load a config file and return loaded files.""" # config_type is str 'machine' or 'mode', which specifies whether this # file being loaded is a machine config or a mode config file expected_version_str = ConfigProcessor.get_expected_version( config_type) config = FileManager.load(filename, expected_version_str, True) subfiles = [] if not ConfigValidator.config_spec: ConfigValidator.load_config_spec() if not config: return dict(), [] self.log.info('Loading config: %s', filename) if config_type in ("machine", "mode"): if not isinstance(config, dict): raise ConfigFileError( "Config should be a dict: {}".format(config), self.log.name, "ConfigProcessor") for k in config.keys(): try: if config_type not in ConfigValidator.config_spec[k][ '__valid_in__']: raise ValueError( 'Found a "{}:" section in config file {}, ' 'but that section is not valid in {} config ' 'files.'.format(k, filename, config_type)) except KeyError: if not ignore_unknown_sections: raise ValueError( 'Found a "{}:" section in config file {}, ' 'but that section is not valid in {} config ' 'files.'.format(k, filename, config_type)) try: if 'config' in config: path = os.path.split(filename)[0] for file in Util.string_to_list(config['config']): full_file = os.path.join(path, file) subfiles.append(full_file) subconfig, subsubfiles = self._load_config_file_and_return_loaded_files( full_file, config_type) subfiles.extend(subsubfiles) config = Util.dict_merge(config, subconfig) return config, subfiles except TypeError: return dict(), []
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 check_config_file_version(filename: str) -> bool: """Check to see if the version of the file name passed matches the config version MPF needs. Args: filename: The file with path to check. Raises: exception if the version of the file doesn't match what MPF needs. """ filename = FileManager.locate_file(filename) file_interface = FileManager.get_file_interface(filename) file_version = file_interface.get_config_file_version(filename) if file_version != int(__config_version__): log.error( "Config file %s is version %s. MPF %s requires " "version %s", filename, file_version, __version__, __config_version__) # TODO remove this line when migrator is done log.error( "We have not created the config file migration tool yet" " for v5. In the meantime, see https://github.com/missionpinball/mpf/issues/897" " for a list of changes between config versions 4 and 5.") # TODO uncomment these and update links when migrator is done # log.error("Use the Config File Migrator to automatically " # "migrate your config file to the latest version.") # log.error("Migration tool: https://missionpinball.com/docs/tools/config-file-migrator/") # log.error("More info on config version %s: " # "http://docs.missionpinball.org/docs/configuration-file" # "-reference/important-config-file-concepts/config_version/config-version-%s/", # __config_version__, __config_version__) return False else: return True
def load_config_file( filename, config_type: str, ignore_unknown_sections=False) -> dict: # pragma: no cover """Load a config file.""" # config_type is str 'machine' or 'mode', which specifies whether this # file being loaded is a machine config or a mode config file expected_version_str = ConfigProcessor.get_expected_version( config_type) config = FileManager.load(filename, expected_version_str, True) if not ConfigValidator.config_spec: ConfigValidator.load_config_spec() if not config: return dict() for k in config.keys(): try: if config_type not in ConfigValidator.config_spec[k][ '__valid_in__']: raise ValueError( 'Found a "{}:" section in config file {}, ' 'but that section is not valid in {} config ' 'files.'.format(k, filename, config_type)) except KeyError: if not ignore_unknown_sections: raise ValueError( 'Found a "{}:" section in config file {}, ' 'but that section is not valid in {} config ' 'files.'.format(k, filename, config_type)) try: if 'config' in config: path = os.path.split(filename)[0] for file in Util.string_to_list(config['config']): full_file = os.path.join(path, file) config = Util.dict_merge( config, ConfigProcessor.load_config_file( full_file, config_type)) return config except TypeError: return dict()
def _load_config_file_and_return_loaded_files( self, filename, config_type: str, ignore_unknown_sections=False ) -> Tuple[dict, List[str]]: # pragma: no cover """Load a config file and return loaded files.""" # config_type is str 'machine' or 'mode', which specifies whether this # file being loaded is a machine config or a mode config file expected_version_str = ConfigProcessor.get_expected_version( config_type) config = FileManager.load(filename, expected_version_str, True) subfiles = [] if not config: return dict(), [] self.log.info('Loading config: %s', filename) if config_type in ("machine", "mode"): self._check_sections(config, config_type, filename, ignore_unknown_sections) try: if 'config' in config: path = os.path.split(filename)[0] for file in Util.string_to_list(config['config']): full_file = os.path.join(path, file) subfiles.append(full_file) subconfig, subsubfiles = self._load_config_file_and_return_loaded_files( full_file, config_type) subfiles.extend(subsubfiles) config = Util.dict_merge(config, subconfig) return config, subfiles except TypeError: return dict(), []
def save_file(self, file_name, file_contents): """Save file.""" self.log.info("Writing file: %s", file_name) FileManager.save(file_name, file_contents)