Beispiel #1
0
    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
Beispiel #2
0
    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)
Beispiel #3
0
    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)
Beispiel #4
0
    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
Beispiel #5
0
    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 {}, {}
Beispiel #6
0
    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)
Beispiel #7
0
    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)
Beispiel #8
0
    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(), []
Beispiel #9
0
    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)
Beispiel #10
0
    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
Beispiel #11
0
    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()
Beispiel #12
0
    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(), []
Beispiel #13
0
 def save_file(self, file_name, file_contents):
     """Save file."""
     self.log.info("Writing file: %s", file_name)
     FileManager.save(file_name, file_contents)