From: Daan De Meyer Date: Thu, 27 Mar 2025 10:43:23 +0000 (+0100) Subject: config: Cache lookups of fields X-Git-Tag: v26~292 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5886c4ca664fb3ac41f0bfdbeb20eec08490fb9e;p=thirdparty%2Fmkosi.git config: Cache lookups of fields We were calling inspect.signature() > 4000 times because it was evaluated over and over again in the list comprehensions. Move the fields lookup into a cached classmethod where possible to make sure we only do it once. This reduces the time needed to parse the systemd config from history from 1.18s => 188ms on my machine. --- diff --git a/mkosi/config.py b/mkosi/config.py index 5d207e99f..f0a9e6843 100644 --- a/mkosi/config.py +++ b/mkosi/config.py @@ -1731,9 +1731,14 @@ class Args: return args + @classmethod + @functools.lru_cache(maxsize=1) + def fields(cls) -> set[str]: + return {f.name for f in dataclasses.fields(cls)} + @classmethod def from_namespace(cls, ns: argparse.Namespace) -> "Args": - return cls(**{k: v for k, v in vars(ns).items() if k in inspect.signature(cls).parameters}) + return cls(**{k: v for k, v in vars(ns).items() if k in cls.fields()}) def to_dict(self) -> dict[str, Any]: return dataclasses.asdict(self, dict_factory=dict_with_capitalised_keys_factory) @@ -1763,7 +1768,7 @@ class Args: for k, v in j.items(): k = key_transformer(k) - if k not in inspect.signature(cls).parameters and (not isinstance(v, (dict, list, set)) or v): + if k not in cls.fields() and (not isinstance(v, (dict, list, set)) or v): die( f"Serialized JSON has unknown field {k} with value {v}", hint="Re-running mkosi once with -f should solve the issue by re-generating the JSON", @@ -1772,9 +1777,7 @@ class Args: value_transformer = json_type_transformer(cls) j = {(tk := key_transformer(k)): value_transformer(tk, v) for k, v in j.items()} - return dataclasses.replace( - cls.default(), **{k: v for k, v in j.items() if k in inspect.signature(cls).parameters} - ) + return dataclasses.replace(cls.default(), **{k: v for k, v in j.items() if k in cls.fields()}) PACKAGE_GLOBS = ( @@ -1842,9 +1845,8 @@ def make_simple_config_parser( for setting in settings: finalize_value(config, setting) - return valtype( - **{k: v for k, v in vars(config).items() if k in inspect.signature(valtype).parameters} - ) + parameters = inspect.signature(valtype).parameters + return valtype(**{k: v for k, v in vars(config).items() if k in parameters}) return parse_simple_config @@ -2140,9 +2142,14 @@ class Config: return config + @classmethod + @functools.lru_cache(maxsize=1) + def fields(cls) -> set[str]: + return {f.name for f in dataclasses.fields(cls)} + @classmethod def from_namespace(cls, ns: argparse.Namespace) -> "Config": - return cls(**{k: v for k, v in vars(ns).items() if k in inspect.signature(cls).parameters}) + return cls(**{k: v for k, v in vars(ns).items() if k in cls.fields()}) @property def output_with_format(self) -> str: @@ -2334,7 +2341,7 @@ class Config: for k, v in j.items(): k = key_transformer(k) - if k not in inspect.signature(cls).parameters and (not isinstance(v, (dict, list, set)) or v): + if k not in cls.fields() and (not isinstance(v, (dict, list, set)) or v): die( f"Serialized JSON has unknown field {k} with value {v}", hint="Re-running mkosi once with -f should solve the issue by re-generating the JSON", @@ -2343,9 +2350,7 @@ class Config: value_transformer = json_type_transformer(cls) j = {(tk := key_transformer(k)): value_transformer(tk, v) for k, v in j.items()} - return dataclasses.replace( - cls.default(), **{k: v for k, v in j.items() if k in inspect.signature(cls).parameters} - ) + return dataclasses.replace(cls.default(), **{k: v for k, v in j.items() if k in cls.fields()}) def find_binary(self, *names: PathString, tools: bool = True) -> Optional[Path]: return find_binary(*names, root=self.tools() if tools else Path("/"), extra=self.extra_search_paths)