Adjustments/retabbing for attrdict.py

Redid some of the logic behind AttrDict. It now raises AttributeError
when necessary, instead of giving the incorrect KeyError response.

This has restored compatibility with copy.deepcopy().
This commit is contained in:
karxi 2016-12-30 20:59:47 -05:00
parent d43220535f
commit 02019c7dc5

View file

@ -1,49 +1,67 @@
# Modified version of the code featured at the given link # Modified version of the code featured at the given link
## {{{ http://code.activestate.com/recipes/473786/ (r1) ## {{{ http://code.activestate.com/recipes/473786/ (r1)
class AttrDict(dict): class AttrDict(dict):
"""A dictionary with attribute-style access. It maps attribute access to """A dictionary with attribute-style access. It maps attribute access to
the real dictionary.""" the real dictionary."""
def __init__(self, init={}): super(AttrDict, self).__init__(init) def __init__(self, init={}): super(AttrDict, self).__init__(init)
def __getstate__(self): return self.__dict__.items() def __getstate__(self): return self.__dict__.items()
def __setstate__(self, items): def __setstate__(self, items):
for key, val in items: self.__dict__[key] = val for key, val in items: self.__dict__[key] = val
def __repr__(self): def __repr__(self):
return "%s(%s)" % ( return "{}({})".format(
type(self).__name__, type(self).__name__,
super(AttrDict, self).__repr__() super(AttrDict, self).__repr__()
) )
def __setitem__(self, key, value): def __setitem__(self, key, value):
return super(AttrDict, self).__setitem__(key, value) return super(AttrDict, self).__setitem__(key, value)
def __getitem__(self, name): def __getitem__(self, name):
return super(AttrDict, self).__getitem__(name) return super(AttrDict, self).__getitem__(name)
def __delitem__(self, name): def __delitem__(self, name):
return super(AttrDict, self).__delitem__(name) return super(AttrDict, self).__delitem__(name)
__getattr__ = __getitem__ def __getattr__(self, name):
__setattr__ = __setitem__ # Basically, fall back on __getitem__ first
__delattr__ = __delitem__ # Try to access ourselves as a dict. Failing that, check for attributes
def copy(self): return type(self)(self) # with the same name. Failing *that*, throw AttributeError to keep
# other code happy.
try: retval = self[name]
except KeyError as err:
try: retval = self.__dict__[name]
except KeyError:
# Raising KeyError here will confuse __deepcopy__, so don't do
# that.
raise AttributeError("No key/attr {!r}".format(name))
return retval
__setattr__ = __setitem__
def __delattr__(self, name):
try: del self[name]
except KeyError as err:
try: del self.__dict__[name]
except KeyError:
raise AttributeError(str(err))
def copy(self): return type(self)(self)
__copy__ = copy
## end of http://code.activestate.com/recipes/473786/ }}} ## end of http://code.activestate.com/recipes/473786/ }}}
class DefAttrDict(AttrDict): class DefAttrDict(AttrDict):
def __init__(self, default_factory=None, *args, **kwargs): def __init__(self, default_factory=None, *args, **kwargs):
self.__dict__["default_factory"] = default_factory self.__dict__["default_factory"] = default_factory
super(DefAttrDict, self).__init__(*args, **kwargs) super(DefAttrDict, self).__init__(*args, **kwargs)
def __repr__(self): def __repr__(self):
return "%s(%r, %s)" % ( return "%s(%r, %s)" % (
type(self).__name__, type(self).__name__,
self.default_factory, self.default_factory,
super(AttrDict, self).__repr__() super(AttrDict, self).__repr__()
) )
def __getitem__(self, name): def __getitem__(self, name):
try: try:
return super(DefAttrDict, self).__getitem__(name) return super(DefAttrDict, self).__getitem__(name)
except KeyError: except KeyError:
##if self.default_factory is None: return None ##if self.default_factory is None: return None
##return self.default_factory() ##return self.default_factory()
result = None result = None
if self.default_factory is not None: if self.default_factory is not None:
result = self.default_factory() result = self.default_factory()
self[name] = result self[name] = result
return result return result
__getattr__ = __getitem__ __getattr__ = __getitem__
def copy(self): return type(self)(self.default_factory, self) def copy(self): return type(self)(self.default_factory, self)