# Copyright 2026 jCloud Services GbR # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import copy from types import FunctionType from typing import Any __all__ = [ 'Configuration', 'set_mutability', 'is_mutable', ] class Configuration: def __init__(self, _config: dict = {}, mutable: bool = True) -> None: '''A base class for configurations.''' self._config = copy.deepcopy(_config) self._mutable = mutable def _check_mutability(self) -> None: return self._mutable def _mutate(self, func: FunctionType) -> FunctionType: def wrapper(*args, **kwargs) -> Any: if self._check_mutability(): return func(*args, **kwargs) else: raise TypeError('configuration is immutable') return wrapper def _set(self, name: str, value: str) -> None: @self._mutate def _set(name: str, value: str) -> None: self._config[name] = value _set(name, value) def _delete(self, name: str) -> None: @self._mutate def _delete(name: str) -> None: del self._config[name] _delete(name) def _get(self, name: str) -> str: return self._config[name] def __getattribute__(self, name: str): if name.startswith('_') or name in self.__dir__(): return super().__getattribute__(name) else: if name not in self._config: raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{name}'") return self._get(name) def __setattr__(self, name, value): if name.startswith('_') or name in self.__dir__(): super().__setattr__(name, value) else: self._set(name, value) def __delattr__(self, name): if name.startswith('_') or name in self.__dir__(): super().__delattr__(name) else: self._delete(name) def __setitem__(self, key, value): self._set(key, value) def __getitem__(self, key): return self._config[key] def __delitem__(self, key): self._delete(key) def __iter__(self): return iter(self._config.items()) def __contains__(self, item): return item in self._config def set_mutability(configuration: Configuration, mutable: bool, *, operate_on_original_object: bool = True) -> Configuration: ''' Sets the mutability of a configuration. :param configuration: The configuration object. :type configuration: Configuration :param mutable: The new mutability. :type mutable: bool :param operate_on_original_object: If ``True``, the mutability of the original object will be set. If ``False``, the original object will be unchanged. The new configuration will always be returned. :type operate_on_original_object: bool :return: The configuration object with the new mutability :rtype: Configuration ''' if not operate_on_original_object: configuration = copy.deepcopy(configuration) configuration._mutable = mutable return configuration def is_mutable(configuration: Configuration) -> Configuration: ''' Checks whether a configuration is mutable. :param configuration: The configuration. :type configuration: Configuration :return: The mutability :rtype: bool ''' return configuration._mutable