diff --git a/mod/config.rpy b/mod/config.rpy index 8c86044..d5bd94c 100644 --- a/mod/config.rpy +++ b/mod/config.rpy @@ -41,6 +41,11 @@ init 90 python in _fom_presence_config: ui_message_report="Could not load some presence configs, see log/submod_log.log." ) + _ERROR_CONFIG_OVERRIDE = error.Error( + log_message_report="Presence config {0} overrides nonexistent config ID: {1}.", + ui_message_report="Could not load some presence configs, see log/submod_log.log." + ) + _WARNING_CONFIG_CLASH = error.Error( log_message_report="Config from file {0} has conflicting name with some other config: {1}.", ui_message_report="There were some warnings during loading some of the presence configs, see log/submod_log.log.", @@ -324,6 +329,7 @@ init 90 python in _fom_presence_config: self.dynamic = parser.get_value("Presence", "Dynamic", _parse_bool, True) self.id = parser.get_value("Presence", "ID", str, None) self.inherit_id = parser.get_value("Presence", "Inherit", str, None) + self.override_id = parser.get_value("Presence", "Override", str, None) self.app_id = parser.get_value("Client", "ApplicationID", int) @@ -339,7 +345,7 @@ init 90 python in _fom_presence_config: self.stop_ts = parser.get_value("Timestamps", "End", _parse_ts_supplier, _none_supplier) self._activity = None - self._inherit_applied = False + self._file = None @staticmethod def from_file(path): @@ -358,28 +364,32 @@ init 90 python in _fom_presence_config: c = configparser.ConfigParser() with _open_with_encoding(path, "r", encoding="utf-8") as f: c.readfp(f, path.replace("\\", "/").split("/")[:-1]) - return Config(_ParserWrapper(c)) - def inherit(self, config, force=False): + config = Config(_ParserWrapper(c)) + config._file = path + return config + + @property + def file(self): + """ + Returns path to file this config was loaded from. + + OUT: + str: + Path to config file. + """ + + return self._file + + def copy_from(self, config): """ Copies values from another config (only omitted, None or - _none_supplier values) over to this config. Unless force parameter - is set to True, does nothing on next call. + _none_supplier values) over to this config. IN: config -> Config: - Config to copy values from. Inherit method is not called, - inheritance is not done recursively by this method, users - should care about this themselves. - - force -> bool, default False: - If True, skips inheritance status checks and applies values - over again. + Config to copy values from. """ - - if self._inherit_applied and not force: - return - if self.app_id is None: self.app_id = config.app_id if self.details is _none_supplier: @@ -399,23 +409,6 @@ init 90 python in _fom_presence_config: if self.stop_ts is _none_supplier: self.stop_ts = config.stop_ts - self._inherit_applied = True - - @property - def inherited(self): - """ - Returns inheritance status flag value. - - OUT: - True: - If this config has inherited from other config. - - False: - If this config has not inherited from another config. - """ - - return self._inherit_applied - def to_activity(self): """ Creates Activity instance from the values stored in Config. @@ -469,8 +462,11 @@ init 90 python in _fom_presence_config: error context. Reported errors are not resolved on successful loads. """ - del _configs[:] - _config_id_map.clear() + configs = dict() + id_map = dict() + + inherited = set() + overridden = set() for _dir, _, files in os.walk(_config_dir): for _file in files: @@ -481,46 +477,86 @@ init 90 python in _fom_presence_config: ): continue + _file = os.path.join(_dir, _file) + rel_file = _file[len(_config_dir) + 1:] + try: - _file = os.path.join(_dir, _file) config = Config.from_file(_file) if config.condition is not None: eval(config.condition, dict(), store.__dict__) - - _configs.append((_file, config)) - if config.id is not None: - if config.id in _config_id_map: - _WARNING_CONFIG_CLASH.report(_file[len(_config_dir) + 1:], config.id) - _config_id_map[config.id] = config + config._file = rel_file except Exception as e: - _ERROR_CONFIG_LOADING.report(_file[len(_config_dir) + 1:], e) + _ERROR_CONFIG_LOADING.report(file_rel, e) + continue + + configs[rel_file] = config + if config.id is None: + config.id = rel_file + + ov = id_map.get(config.id) + if ov is not None: + _WARNING_CONFIG_CLASH.report(rel_file, config.id) + id_map[config.id] = config - # Once configs are loaded, we now copy inherited values. def inherit(config): # Prevent loops and infinite recursions. - if config.inherited: + if config in inherited: return True if config.inherit_id is not None: - parent = _config_id_map.get(config.inherit_id) + parent = id_map.get(config.inherit_id) if parent is None: - _ERROR_CONFIG_INHERITANCE.report(_file[len(_config_dir) + 1:], config.inherit_id) + _ERROR_CONFIG_INHERITANCE.report(config.file, config.inherit_id) return False # Inheritance is done recursively. if not inherit(parent): return False - config.inherit(parent) + config.copy_from(parent) + + # Add to list of applied inheritances. + inherited.add(config) + return True + + def override(config): + # Prevent loops and infinite recursions. + if config in overridden: + return True + + if config.override_id is not None: + ov = id_map.get(config.override_id) + if ov is None: + _ERROR_CONFIG_OVERRIDE.report(config.file, config.override_id) + return False + + if not override(ov): + return False + + remove_config(ov) + config.id = ov.id + id_map[ov.id] = config + overridden.add(config) return True - idx = 0 - while idx < len(_configs): - _file, config = _configs[idx] - if not inherit(config): - del _configs[idx] - else: - idx += 1 + def remove_config(config): + del configs[config.file] + if config.id is not None: + del id_map[config.id] + + for rel_file, config in list(configs.items()): + if rel_file not in configs: + continue + + if not (inherit(config) and override(config)): + remove_config(config) + continue + + del _configs[:] + _config_id_map.clear() + + _configs.extend(list(configs.items())) + _config_id_map.update(id_map) # Sort configs on reload to save precious time on every loop. _configs.sort(key=lambda it: it[1].priority, reverse=True)