Created
April 24, 2014 15:43
-
-
Save dingus9/11259354 to your computer and use it in GitHub Desktop.
Overridable implementation of an attrdict type
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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