Source code for klotho.utils.data_structures.dictionaries


[docs] class SafeDict(dict): """ A dictionary with a fixed set of keys that cannot be added to or removed from. Values for existing keys can be updated, but new keys cannot be inserted and existing keys cannot be deleted. Supports key aliases that resolve to canonical keys. Parameters ---------- *args Positional arguments passed to ``dict``. aliases : dict, optional Mapping of alias keys to canonical keys. Each alias must resolve to a key present in the initial data. **kwargs Keyword arguments passed to ``dict``. Raises ------ KeyError If any alias target is not a key in the initial data. Examples -------- >>> d = SafeDict({'a': 1, 'b': 2}, aliases={'x': 'a'}) >>> d['x'] 1 >>> d['a'] = 10 >>> d['a'] 10 """
[docs] def __init__(self, *args, aliases=None, **kwargs): super().__init__(*args, **kwargs) self._allowed_keys = set(super().keys()) self._aliases = dict(aliases) if aliases else {} for alias, target in self._aliases.items(): if target not in self._allowed_keys: raise KeyError(target)
def _resolve_key(self, key): """ Resolve an alias to its canonical key. Parameters ---------- key : hashable The key or alias to resolve. Returns ------- hashable The canonical key. """ return self._aliases.get(key, key)
[docs] def __getitem__(self, key): """ Retrieve the value for *key*, resolving aliases. Parameters ---------- key : hashable The key or alias to look up. Returns ------- object or None The associated value, or ``None`` if the resolved key is not present. """ resolved = self._resolve_key(key) if resolved in self: return super().__getitem__(resolved) return None
[docs] def __setitem__(self, key, value): """ Set *value* for *key* only if the resolved key is in the allowed set. Parameters ---------- key : hashable The key or alias to set. value : object The value to assign. """ resolved = self._resolve_key(key) if resolved in self._allowed_keys: super().__setitem__(resolved, value)
[docs] def __delitem__(self, key): """ Prevent deletion of keys. Parameters ---------- key : hashable The key to delete. Raises ------ KeyError Always raised; keys cannot be removed from a ``SafeDict``. """ resolved = self._resolve_key(key) if resolved in self._allowed_keys: raise KeyError(key)
[docs] def clear(self): """ Prevent clearing all entries. Raises ------ KeyError Always raised when the dictionary has allowed keys. """ if self._allowed_keys: raise KeyError("SafeDict keys are fixed")
[docs] def pop(self, key, default=None): """ Prevent popping a key from the dictionary. Parameters ---------- key : hashable The key to pop. default : object, optional Fallback value (returned for non-allowed keys). Returns ------- object *default* if the key is not in the allowed set. Raises ------ KeyError If the resolved key is in the allowed set. """ resolved = self._resolve_key(key) if resolved in self._allowed_keys: raise KeyError(key) return default
[docs] def popitem(self): """ Prevent popping an arbitrary item. Raises ------ KeyError Always raised; keys are fixed. """ raise KeyError("SafeDict keys are fixed")
[docs] def setdefault(self, key, default=None): """ Return the value for *key* if present, else set and return *default*. Only sets the value if the resolved key is in the allowed set. Parameters ---------- key : hashable The key or alias. default : object, optional Value to set if absent. Returns ------- object The current or newly set value, or *default* if the key is not allowed. """ resolved = self._resolve_key(key) if resolved in self._allowed_keys: return super().setdefault(resolved, default) return default
[docs] def update(self, *args, **kwargs): """ Update allowed keys with values from another mapping or keywords. Keys not in the allowed set are silently ignored. Parameters ---------- *args A mapping or iterable of key-value pairs. **kwargs Keyword arguments to merge. """ updates = dict(*args, **kwargs) for key in updates.keys(): resolved = self._resolve_key(key) if resolved in self._allowed_keys: super().__setitem__(resolved, updates[key])