An attribute-based dict that can do smart merges. Accessing a field on a config object for the first time populates the key with either a nested Config object for keys starting with capitals or :class:`.LazyConfigValue` for lowercase keys, allowing quick assignments such as::
| 222 | |
| 223 | |
| 224 | class Config(dict): # type:ignore[type-arg] |
| 225 | """An attribute-based dict that can do smart merges. |
| 226 | |
| 227 | Accessing a field on a config object for the first time populates the key |
| 228 | with either a nested Config object for keys starting with capitals |
| 229 | or :class:`.LazyConfigValue` for lowercase keys, |
| 230 | allowing quick assignments such as:: |
| 231 | |
| 232 | c = Config() |
| 233 | c.Class.int_trait = 5 |
| 234 | c.Class.list_trait.append("x") |
| 235 | |
| 236 | """ |
| 237 | |
| 238 | def __init__(self, *args: t.Any, **kwds: t.Any) -> None: |
| 239 | dict.__init__(self, *args, **kwds) |
| 240 | self._ensure_subconfig() |
| 241 | |
| 242 | def _ensure_subconfig(self) -> None: |
| 243 | """ensure that sub-dicts that should be Config objects are |
| 244 | |
| 245 | casts dicts that are under section keys to Config objects, |
| 246 | which is necessary for constructing Config objects from dict literals. |
| 247 | """ |
| 248 | for key in self: |
| 249 | obj = self[key] |
| 250 | if _is_section_key(key) and isinstance(obj, dict) and not isinstance(obj, Config): |
| 251 | setattr(self, key, Config(obj)) |
| 252 | |
| 253 | def _merge(self, other: t.Any) -> None: |
| 254 | """deprecated alias, use Config.merge()""" |
| 255 | self.merge(other) |
| 256 | |
| 257 | def merge(self, other: t.Any) -> None: |
| 258 | """merge another config object into this one""" |
| 259 | to_update = {} |
| 260 | for k, v in other.items(): |
| 261 | if k not in self: |
| 262 | to_update[k] = v |
| 263 | else: # I have this key |
| 264 | if isinstance(v, Config) and isinstance(self[k], Config): |
| 265 | # Recursively merge common sub Configs |
| 266 | self[k].merge(v) |
| 267 | elif isinstance(v, LazyConfigValue): |
| 268 | self[k] = v.merge_into(self[k]) |
| 269 | else: |
| 270 | # Plain updates for non-Configs |
| 271 | to_update[k] = v |
| 272 | |
| 273 | self.update(to_update) |
| 274 | |
| 275 | def collisions(self, other: Config) -> dict[str, t.Any]: |
| 276 | """Check for collisions between two config objects. |
| 277 | |
| 278 | Returns a dict of the form {"Class": {"trait": "collision message"}}`, |
| 279 | indicating which values have been ignored. |
| 280 | |
| 281 | An empty dict indicates no collisions. |
no outgoing calls
searching dependent graphs…