from types import MappingProxyType class NamespaceProxy(): """ Read-only proxy of a mapping (like a dict) allowing item access via attributes. Mapping keys that are not strings will be ignored, and attribute access to any names starting with "__" will still be passed to the actual object attributes. Being a proxy, attributes available and their values will change as the underlying backing dict is changed. Intended for sitatuations where a dynamic mapping needs to be passed out to client code but you'd like to heavily suggest that it not be modified. Note that only the top-level mapping is read only - if the attribute values themselves are mutable, they may still be modified via the NamespaceProxy. """ def __init__(self, backing_dict): """ Create a new NamespaceProxy, with attribute access to the underlying backing dict passed in. """ object.__setattr__(self, "_dict_proxy", MappingProxyType(backing_dict)) def __getattribute__(self, name): if name.startswith("__"): return object.__getattribute__(self, name) return object.__getattribute__(self, "_dict_proxy")[name] def __setattr__(self, *args): raise TypeError("NamespaceProxy does not allow attributes to be modified") def __delattr__(self, *args): raise TypeError("NamespaceProxy does not allow attributes to be modified") def __repr__(self): keys = sorted(object.__getattribute__(self, "_dict_proxy")) items = ("{}={!r}".format(k, object.__getattribute__( self, "_dict_proxy")[k]) for k in keys) return "{}({})".format(type(self).__name__, ", ".join(items)) def __eq__(self, other): if isinstance(other, self.__class__): return (object.__getattribute__(self, "_dict_proxy") == object.__getattribute__(other, "_dict_proxy")) return False