from __future__ import print_function
from collections import OrderedDict, MutableMapping
from json import dumps
from pprint import pprint
from sys import version_info
from inspect import ismethod
# for debugging
def here(item=None):
out = 'here'
if item != None:
out += '({})'.format(item)
print(out)
class DotMap(MutableMapping, OrderedDict):
def __init__(self, *args, **kwargs):
self._map = OrderedDict()
self._dynamic = True
if kwargs:
if '_dynamic' in kwargs:
self._dynamic = kwargs['_dynamic']
if args:
d = args[0]
if isinstance(d, dict):
for k,v in self.__call_items(d):
if isinstance(v, dict):
v = DotMap(v, _dynamic=self._dynamic)
if type(v) is list:
l = []
for i in v:
n = i
if type(i) is dict:
n = DotMap(i, _dynamic=self._dynamic)
l.append(n)
v = l
self._map[k] = v
if kwargs:
for k,v in self.__call_items(kwargs):
if k is not '_dynamic':
self._map[k] = v
def __call_items(self, obj):
if hasattr(obj, 'iteritems') and ismethod(getattr(obj, 'iteritems')):
return obj.iteritems()
else:
return obj.items()
def items(self):
return self.iteritems()
def iteritems(self):
return self.__call_items(self._map)
def __iter__(self):
return self._map.__iter__()
def next(self):
return self._map.next()
def __setitem__(self, k, v):
self._map[k] = v
def __getitem__(self, k):
if k not in self._map and self._dynamic and k != '_ipython_canary_method_should_not_exist_':
# automatically extend to new DotMap
self[k] = DotMap()
return self._map[k]
def __setattr__(self, k, v):
if k in {'_map','_dynamic', '_ipython_canary_method_should_not_exist_'}:
super(DotMap, self).__setattr__(k,v)
else:
self[k] = v
def __getattr__(self, k):
if k == {'_map','_dynamic','_ipython_canary_method_should_not_exist_'}:
super(DotMap, self).__getattr__(k)
else:
return self[k]
def __delattr__(self, key):
return self._map.__delitem__(key)
def __contains__(self, k):
return self._map.__contains__(k)
def __str__(self):
items = []
for k,v in self.__call_items(self._map):
# bizarre recursive assignment situation (why someone would do this is beyond me)
if id(v) == id(self):
items.append('{0}=DotMap(...)'.format(k))
else:
items.append('{0}={1}'.format(k, repr(v)))
joined = ', '.join(items)
out = '{0}({1})'.format(self.__class__.__name__, joined)
return out
def __repr__(self):
return str(self)
def toDict(self):
d = {}
for k,v in self.items():
if type(v) is DotMap:
# bizarre recursive assignment support
if id(v) == id(self):
v = d
else:
v = v.toDict()
elif type(v) in (list, tuple):
l = []
for i in v:
n = i
if type(i) is DotMap:
n = i.toDict()
l.append(n)
if type(v) is tuple:
v = tuple(l)
else:
v = l
d[k] = v
return d
def pprint(self, pformat='dict'):
if pformat == 'json':
print(dumps(self.toDict(), indent=4, sort_keys=True))
else:
pprint(self.toDict())
def empty(self):
return (not any(self))
# proper dict subclassing
def values(self):
return self._map.values()
# ipython support
def __dir__(self):
return self.keys()
@classmethod
def parseOther(self, other):
if type(other) is DotMap:
return other._map
else:
return other
def __cmp__(self, other):
other = DotMap.parseOther(other)
return self._map.__cmp__(other)
def __eq__(self, other):
other = DotMap.parseOther(other)
if not isinstance(other, dict):
return False
return self._map.__eq__(other)
def __ge__(self, other):
other = DotMap.parseOther(other)
return self._map.__ge__(other)
def __gt__(self, other):
other = DotMap.parseOther(other)
return self._map.__gt__(other)
def __le__(self, other):
other = DotMap.parseOther(other)
return self._map.__le__(other)
def __lt__(self, other):
other = DotMap.parseOther(other)
return self._map.__lt__(other)
def __ne__(self, other):
other = DotMap.parseOther(other)
return self._map.__ne__(other)
def __delitem__(self, key):
return self._map.__delitem__(key)
def __len__(self):
return self._map.__len__()
def clear(self):
self._map.clear()
def copy(self):
return DotMap(self)
def __copy__(self):
return self.copy()
def __deepcopy__(self, memo=None):
return self.copy()
def get(self, key, default=None):
return self._map.get(key, default)
def has_key(self, key):
return key in self._map
def iterkeys(self):
return self._map.iterkeys()
def itervalues(self):
return self._map.itervalues()
def keys(self):
return self._map.keys()
def pop(self, key, default=None):
return self._map.pop(key, default)
def popitem(self):
return self._map.popitem()
def setdefault(self, key, default=None):
self._map.setdefault(key, default)
def update(self, *args, **kwargs):
if len(args) != 0:
self._map.update(*args)
self._map.update(kwargs)
def viewitems(self):
return self._map.viewitems()
def viewkeys(self):
return self._map.viewkeys()
def viewvalues(self):
return self._map.viewvalues()
@classmethod
def fromkeys(cls, seq, value=None):
d = DotMap()
d._map = OrderedDict.fromkeys(seq, value)
return d
def __getstate__(self): return self.__dict__
def __setstate__(self, d): self.__dict__.update(d)
# bannerStr
def _getListStr(self,items):
out = '['
mid = ''
for i in items:
mid += ' {}\n'.format(i)
if mid != '':
mid = '\n' + mid
out += mid
out += ']'
return out
def _getValueStr(self,k,v):
outV = v
multiLine = len(str(v).split('\n')) > 1
if multiLine:
# push to next line
outV = '\n' + v
if type(v) is list:
outV = self._getListStr(v)
out = '{} {}'.format(k,outV)
return out
def _getSubMapDotList(self, pre, name, subMap):
outList = []
if pre == '':
pre = name
else:
pre = '{}.{}'.format(pre,name)
def stamp(pre,k,v):
valStr = self._getValueStr(k,v)
return '{}.{}'.format(pre, valStr)
for k,v in subMap.items():
if isinstance(v,DotMap) and v != DotMap():
subList = self._getSubMapDotList(pre,k,v)
outList.extend(subList)
else:
outList.append(stamp(pre,k,v))
return outList
def _getSubMapStr(self, name, subMap):
outList = ['== {} =='.format(name)]
for k,v in subMap.items():
if isinstance(v,DotMap) and v != DotMap():
# break down to dots
subList = self._getSubMapDotList('',k,v)
# add the divit
# subList = ['> {}'.format(i) for i in subList]
outList.extend(subList)
else:
out = self._getValueStr(k,v)
# out = '> {}'.format(out)
out = '{}'.format(out)
outList.append(out)
finalOut = '\n'.join(outList)
return finalOut
def bannerStr(self):
lines = []
previous = None
for k,v in self.items():
if previous == 'DotMap':
lines.append('-')
out = ''
if isinstance(v,DotMap):
name = k
subMap = v
out = self._getSubMapStr(name,subMap)
lines.append(out)
previous = 'DotMap'
else:
out = self._getValueStr(k,v)
lines.append(out)
previous = 'other'
lines.append('--')
s = '\n'.join(lines)
return s
if __name__ == '__main__':
# basics
print('\n== basics ==')
d = {
'a':1,
'b':2,
'subD': {'c':3, 'd':4}
}
dd = DotMap(d)
print(dd)
print(len(dd))
print(dd.copy())
print(dd)
print(OrderedDict.fromkeys([1,2,3]))
print(DotMap.fromkeys([1,2,3], 'a'))
print(dd.get('a'))
print(dd.get('f',33))
print(dd.get('f'))
print(dd.has_key('a'))
dd.update([('rat',5),('bum',4)], dog=7,cat=9)
dd.update({'lol':1,'ba':2})
print(dd)
print
for k in dd:
print(k)
print('a' in dd)
print('c' in dd)
dd.c.a = 1
print(dd.toDict())
dd.pprint()
print
print(dd.values())
dm = DotMap(name='Steve', job='programmer')
print(dm)
print(issubclass(dm.__class__, dict))
am = DotMap()
am.some.deep.path.cuz.we = 'can'
print(am)
del am.some.deep
print(am)
parentDict = {
'name': 'Father1',
'children': [
{'name': 'Child1'},
{'name': 'Child2'},
{'name': 'Child3'},
]
}
parent = DotMap(parentDict)
print([x.name for x in parent.children])
# pickle
print('\n== pickle ==')
import pickle
s = pickle.dumps(parent)
d = pickle.loads(s)
print(d)
# init from DotMap
print('\n== init from DotMap ==')
e = DotMap(d)
print(e)
# empty
print('\n== empty() ==')
d = DotMap()
print(d.empty())
d.a = 1
print(d.empty())
print()
x = DotMap({'a': 'b'})
print(x.b.empty()) # True (and creates empty DotMap)
print(x.b) # DotMap()
print(x.b.empty()) # also True
# _dynamic
print('\n== _dynamic ==')
d = DotMap()
d.still.works
print(d)
d = DotMap(_dynamic=False)
try:
d.no.creation
print(d)
except KeyError:
print('KeyError caught')
d = {'sub':{'a':1}}
dm = DotMap(d)
print(dm)
dm.still.works
dm.sub.still.works
print(dm)
dm2 = DotMap(d,_dynamic=False)
try:
dm.sub.yes.creation
print(dm)
dm2.sub.no.creation
print(dm)
except KeyError:
print('KeyError caught')
# _dynamic
print('\n== toDict() ==')
conf = DotMap()
conf.dep = DotMap(facts=DotMap(operating_systems=DotMap(os_CentOS_7=True), virtual_data_centers=[DotMap(name='vdc1', members=['sp1'], options=DotMap(secret_key='badsecret', description='My First VDC')), DotMap(name='vdc2', members=['sp2'], options=DotMap(secret_key='badsecret', description='My Second VDC'))], install_node='192.168.2.200', replication_group_defaults=DotMap(full_replication=False, enable_rebalancing=False, description='Default replication group description', allow_all_namespaces=False), node_defaults=DotMap(ntp_servers=['192.168.2.2'], ecs_root_user='root', dns_servers=['192.168.2.2'], dns_domain='local', ecs_root_pass='badpassword'), storage_pools=[DotMap(name='sp1', members=['192.168.2.220'], options=DotMap(ecs_block_devices=['/dev/vdb'], description='My First SP')), DotMap(name='sp2', members=['192.168.2.221'], options=DotMap(protected=False, ecs_block_devices=['/dev/vdb'], description='My Second SP'))], storage_pool_defaults=DotMap(cold_storage_enabled=False, protected=False, ecs_block_devices=['/dev/vdc'], description='Default storage pool description'), virtual_data_center_defaults=DotMap(secret_key='badsecret', description='Default virtual data center description'), management_clients=['192.168.2.0/24'], replication_groups=[DotMap(name='rg1', members=['vdc1', 'vdc2'], options=DotMap(description='My RG'))]), lawyers=DotMap(license_accepted=True))
print(conf.dep.toDict()['facts']['replication_groups'])
# recursive assignment
print('\n== recursive assignment ==')
# dict
d = dict()
d['a'] = 5
print(id(d))
d['recursive'] = d
print(d)
print(d['recursive']['recursive']['recursive'])
# DotMap
m = DotMap()
m.a = 5
print(id(m))
m.recursive = m
print(m.recursive.recursive.recursive)
print(m)
print(m.toDict())
# kwarg
print('\n== kwarg ==')
def test(**kwargs):
print(kwargs)
class D:
def keys(self):
return ['a', 'b']
def __getitem__(self, key):
return 0
a = {'1':'a', '2':'b'}
b = DotMap(a, _dynamic=False)
o = OrderedDict(a)
test(**a)
test(**b.toDict())
test(**o)
test(**D())
# ordering
print('\n== ordering ==')
m = DotMap()
m.alpha = 1
m.bravo = 2
m.charlie = 3
m.delta = 4
for k,v in m.items():
print(k,v)
# subclassing
print('\n== subclassing ==')
d = DotMap()
o = OrderedDict()
print(isinstance(d, dict))
print(isinstance(o, dict))
e = DotMap(m)
print(e)
# deepcopy
print('\n== deepcopy ==')
import copy
t = DotMap()
t.a = 1
t.b = 3
f = copy.deepcopy(t)
t.a = 2
print(t)
print(f)
# copy order preservation
print('\n== copy order preservation ==')
t = DotMap()
t.a = 1
t.b = 2
t.c = 3
copies = []
print(id(t))
for i in range(3):
copyMap = copy.deepcopy(t)
copies.append(copyMap)
print(id(copyMap))
print()
for copyMap in copies:
for k,v in copyMap.items():
print(k,v)
print()
# bannerStr
print('\n== bannerStr ==')
t.cities.LA = 1
t.cities.DC = 2
t.cities.London.pop = 'many'
t.cities.London.weather = 'rain'
haiku = '\n'.join([
"Haikus are easy",
"But sometimes they don't make sense",
"Refrigerator",
])
t.haiku = haiku
t.teams.blue = 1
t.teams.red = 2
t.teams.green = 3
t.colors.blue = 1
t.colors.red = 2
t.colors.green = 3
t.numbers.short = list(range(4))
t.numbers.early = list(range(10))
t.numbers.backwards = list(range(10,-1,-1))
t.deepLog.deeper.Q = list(range(4))
print(t.bannerStr())
# sub-DotMap deepcopy
print('\n== sub-DotMap deepcopy ==')
import copy
l = []
d = {'d1': {'d2': ''}}
m = DotMap(d)
for i in range(3):
x = copy.deepcopy(m)
x.d1.d2 = i
l.append(x)
for m in l:
print(m)
# tuple toDict
print('\n== DotMap tuple toDict ==')
m = DotMap({'a': 1, 'b': (11, 22, DotMap({'c': 3}))})
d = m.toDict()
print(d)
# final print
print()