Skip to content

Instantly share code, notes, and snippets.

@dingus9
Created April 24, 2014 15:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dingus9/11259354 to your computer and use it in GitHub Desktop.
Save dingus9/11259354 to your computer and use it in GitHub Desktop.
Overridable implementation of an attrdict type
# Why cls_state... cls_state is a portable attrdict
class EncoreClsDict(dict):
"""This is an attrdict implementation that provides:
* property access for dict elements
* overrides for properties
*
* Override getitem, setitem, getattr, and setattr to provide the following behaviors:
@property decorators take precidence over dict items... and are always called.
@prop.setter decorators take precidence and can call self['key'] to set dict vals.
self.cls_properties['key'] to prevent key from being auto saved self['key']
self.key == self['key'] without self.cls_properties or @property
reserved keys are:
* cls_properties - use to always treat properties['name'] as a property only.
* _cls_seen_flags - flag list to prevent inf looping
* ENCORECLSDICT_PROPERTIES
"""
# CLS_ITEMS kept from iter and dict, but not properties
ENCORECLSDICT_PROPERTIES = ['cls_properties', '_cls_seen_flags']
def __init__(self, *args, **kwargs):
"""Build a fancy attrdict like object
arg0 dict The diction object to instatiate with
:recurse: True"""
# Order here is critical
# 1st
if not hasattr(self, '_cls_seen_flags'):
self._cls_seen_flags = []
# 2nd
if not hasattr(self, 'cls_properties'):
self.cls_properties = []
if 'cls_properties' in kwargs and isinstance(kwargs['cls_properties'], list):
self.cls_properties = self.cls_properties + kwargs['cls_properties']
recurse = kwargs['recurse'] if 'recurse' in kwargs else True
obj = args[0] if len(args) > 0 else {}
"""Recusrively call self to capture all itterable dict and lists"""
if not recurse:
for k, v in obj.iteritems():
self[k] = v
else: # recursive option recurses till it hits a non dict or non dict in a list
# E.G. list in list or object in list or string in list.
# a dict in a list would still recurse, but not a dict in a list in a list.
# [{}] > yes
# [[{}]] > no
# ['str'] > no
# [{key:'val'},'str'] > yes, no
if isinstance(obj, dict):
for k, v in obj.iteritems():
if isinstance(v, dict):
self[k] = EncoreClsDict(v)
elif isinstance(v, list): # list in dict
nl = []
for item in v:
if isinstance(item, dict):
nl.append(EncoreClsDict(item))
else: # if list in list or string or other... stop recursing
nl.append(item)
self[k] = nl
else:
self[k] = v
def __getattribute__(self, key):
# prevent recursion loops
ENCORECLSDICT_PROPERTIES = super(EncoreClsDict,
self).__getattribute__('ENCORECLSDICT_PROPERTIES')
if (key == 'cls_properties' or
key == '_cls_seen_flags' or
key in ENCORECLSDICT_PROPERTIES):
return super(EncoreClsDict, self).__getattribute__(key)
else:
# prevent recursion loops -- local vars for easier use later
_cls_seen_flags = super(EncoreClsDict, self).__getattribute__('_cls_seen_flags')
cls_properties = super(EncoreClsDict, self).__getattribute__('cls_properties')
__class__ = super(EncoreClsDict, self).__getattribute__('__class__')
if (key not in _cls_seen_flags and
(hasattr(self, 'cls_properties') and key in cls_properties) or
key in __class__.__dict__):
_cls_seen_flags.append(key)
val = super(EncoreClsDict, self).__getattribute__(key)
_cls_seen_flags.remove(key)
return val
else:
try:
return super(EncoreClsDict, self).__getattribute__(key)
except:
return self[key]
def __setattr__(self, key, val):
if (key == 'cls_properties' or
key == '_cls_seen_flags' or
key in self.ENCORECLSDICT_PROPERTIES):
super(EncoreClsDict, self).__setattr__(key, val)
else:
if (key not in self._cls_seen_flags and
(hasattr(self, 'cls_properties') and key in self.cls_properties or
key in self.__class__.__dict__)):
self._cls_seen_flags.append(key)
super(EncoreClsDict, self).__setattr__(key, val)
self._cls_seen_flags.remove(key)
else:
self[key] = val
def __getitem__(self, key):
if (key == 'cls_properties' or
key in self.ENCORECLSDICT_PROPERTIES or
key in self.__class__.__dict__ or
hasattr(self, 'cls_properties') and key in self.cls_properties):
if key not in self._cls_seen_flags:
return getattr(self, key)
else:
return super(EncoreClsDict, self).__getitem__(key)
else:
return super(EncoreClsDict, self).__getitem__(key)
def __setitem__(self, key, val):
if (key == 'cls_properties' or
key == '_cls_seen_flags' or
key in self.ENCORECLSDICT_PROPERTIES):
setattr(self, key, val)
else:
if (key not in self._cls_seen_flags and
(key in self.__class__.__dict__ or
hasattr(self, 'cls_properties') and key in self.cls_properties)):
setattr(self, key, val)
else:
super(EncoreClsDict, self).__setitem__(key, val)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment