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_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_files_with_cache( self, filenames: List[str], config_type: str, load_from_cache=True, store_to_cache=True, ignore_unknown_sections=False) -> dict: # pragma: no cover """Load multiple configs with a combined cache.""" config = dict() # type: Any # Step 1: Check timestamps of the filelist vs cache cache_file = self.get_cache_filename(filenames) if load_from_cache: cache_time = self._get_mtime_or_negative(cache_file) if cache_time < 0: load_from_cache = False if load_from_cache: for configfile in filenames: if not os.path.isfile(configfile) or os.path.getmtime( configfile) > cache_time: load_from_cache = False self.log.warning('Config file in cache changed: %s', configfile) break # Step 2: Get cache content if load_from_cache: config, loaded_files = self._load_config_from_cache(cache_file) if not config: load_from_cache = False else: loaded_files = None # Step 3: Check timestamps of included files vs cache if loaded_files: for configfile in loaded_files: if not os.path.isfile(configfile) or os.path.getmtime( configfile) > cache_time: load_from_cache = False self.log.warning('Config file in cache changed: %s', configfile) break # Step 4: Return cache if load_from_cache: return config # Step 5: If we did not get it from cache load it from files if not ConfigValidator.config_spec: ConfigValidator.load_config_spec() config = dict() loaded_files = [] for configfile in filenames: self.log.info('Loading config from file %s.', configfile) file_config, file_subfiles = self._load_config_file_and_return_loaded_files( configfile, config_type, ignore_unknown_sections) loaded_files.extend(file_subfiles) config = Util.dict_merge(config, file_config) # Step 6: Store to cache if store_to_cache: with open(cache_file, 'wb') as f: pickle.dump((config, loaded_files), f, protocol=4) self.log.info('Config file cache created: %s', cache_file) return config
def __init__(self, options, config, machine_path, thread_stopper=None, **kwargs): self.log = logging.getLogger('mpfmc') self.log.info("Mission Pinball Framework Media Controller v%s", __version__) self.log.info("Mission Pinball Framework Game Engine v%s", __mpfversion__) if (__version__.split('.')[0] != __mpfversion__.split('.')[0] or __version__.split('.')[1] != __mpfversion__.split('.')[1]): self.log.error("MPF MC and MPF Game engines must be same " "major.minor versions. You have MPF v{} and MPF-MC" " v{}".format(__mpfversion__, __version__)) raise ValueError("MPF MC and MPF Game engines must be same " "major.minor versions. You have MPF v{} and MPF-MC" " v{}".format(__mpfversion__, __version__)) super().__init__(**kwargs) self.options = options self.machine_config = config self.log.info("Machine path: %s", machine_path) self.machine_path = machine_path self.clock = Clock # pylint: disable-msg=protected-access self.log.info("Starting clock at %sHz", Clock._max_fps) self._boot_holds = set() self.mpf_path = os.path.dirname(mpf.__file__) self.modes = CaseInsensitiveDict() self.player_list = list() self.player = None self.num_players = 0 self.bcp_client_connected = False self.placeholder_manager = McPlaceholderManager(self) self.settings = McSettingsController(self) self.animation_configs = dict() self.active_slides = dict() self.scriptlets = list() self.register_boot_hold('init') self.displays = CaseInsensitiveDict() self.machine_vars = CaseInsensitiveDict() self.machine_var_monitor = False self.monitors = dict() self.targets = dict() """Dict which contains all the active slide frames in the machine that a slide can target. Will always contain an entry called 'default' which will be used if a slide doesn't specify targeting. """ self.keyboard = None self.physical_dmds = [] self.physical_rgb_dmds = [] self.crash_queue = queue.Queue() self.ticks = 0 self.start_time = 0 self.is_init_done = False if thread_stopper: self.thread_stopper = thread_stopper else: self.thread_stopper = threading.Event() # Core components self.config_validator = ConfigValidator(self) self.events = EventManager(self) self.mode_controller = ModeController(self) create_config_collections(self, self.machine_config['mpf-mc']['config_collections']) ConfigValidator.load_config_spec() self.config_processor = ConfigProcessor(self) self.transition_manager = TransitionManager(self) self._set_machine_path() self._load_font_paths() # Initialize the sound system (must be done prior to creating the AssetManager). # If the sound system is not available, do not load any other sound-related modules. if SoundSystem is None: self.sound_system = None else: self.sound_system = SoundSystem(self) self.asset_manager = ThreadedAssetManager(self) self.bcp_processor = BcpProcessor(self) # Asset classes ImageAsset.initialize(self) VideoAsset.initialize(self) self._initialise_sound_system() self.clock.schedule_interval(self._check_crash_queue, 1) self.events.add_handler("client_connected", self._create_physical_dmds) self.events.add_handler("player_turn_start", self.player_start_turn)