././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1649262403.8910184 dotmap-1.3.30/0000755000000000000240000000000000000000000013147 5ustar00rootstaff00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1591284836.0 dotmap-1.3.30/LICENSE.txt0000644000000000000240000000207100000000000014772 0ustar00rootstaff00000000000000The MIT License (MIT) Copyright (c) 2015 Chris Redford Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1639146526.0 dotmap-1.3.30/MANIFEST.in0000644000000000000240000000003600000000000014704 0ustar00rootstaff00000000000000include LICENSE.txt README.md ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1649262403.891283 dotmap-1.3.30/PKG-INFO0000644000000000000240000001045700000000000014253 0ustar00rootstaff00000000000000Metadata-Version: 2.1 Name: dotmap Version: 1.3.30 Summary: ordered, dynamically-expandable dot-access dictionary Home-page: https://github.com/drgrib/dotmap Author: Chris Redford Author-email: credford@gmail.com License: MIT Download-URL: https://github.com/drgrib/dotmap/tarball/1.0 Description: # DotMap [![Build Status](https://travis-ci.com/drgrib/dotmap.svg?branch=master)](https://travis-ci.com/drgrib/dotmap) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate?business=N2GLXLS5KBFBY&item_name=Chris+Redford¤cy_code=USD) # Install ``` pip3 install dotmap ``` ## Upgrade Get updates for current installation ``` pip3 install --upgrade dotmap ``` # Features `DotMap` is a dot-access `dict` subclass that - has dynamic hierarchy creation (autovivification) - can be initialized with keys - easily initializes from `dict` - easily converts to `dict` - is ordered by insertion The key feature is exactly what you want: dot-access ```python from dotmap import DotMap m = DotMap() m.name = 'Joe' print('Hello ' + m.name) # Hello Joe ``` However, `DotMap` is a `dict` and you can treat it like a `dict` as needed ```python print(m['name']) # Joe m.name += ' Smith' m['name'] += ' Jr' print(m.name) # Joe Smith Jr ``` It also has fast, automatic hierarchy (which can be deactivated by initializing with `DotMap(_dynamic=False)`) ```python m = DotMap() m.people.steve.age = 31 ``` And key initialization ```python m = DotMap(a=1, b=2) ``` You can initialize it from `dict` and convert it to `dict` ```python d = {'a':1, 'b':2} m = DotMap(d) print(m) # DotMap(a=1, b=2) print(m.toDict()) # {'a': 1, 'b': 2} ``` And it has iteration that is ordered by insertion ```python m = DotMap() m.people.john.age = 32 m.people.john.job = 'programmer' m.people.mary.age = 24 m.people.mary.job = 'designer' m.people.dave.age = 55 m.people.dave.job = 'manager' for k, v in m.people.items(): print(k, v) print # john DotMap(age=32, job='programmer') # mary DotMap(age=24, job='designer') # dave DotMap(age=55, job='manager') ``` It also has automatic counter initialization ```python m = DotMap() for i in range(7): m.counter += 1 print(m.counter) # 7 ``` And automatic addition initializations of any other type ```python m = DotMap() m.quote += 'lions' m.quote += ' and tigers' m.quote += ' and bears' m.quote += ', oh my' print(m.quote) # lions and tigers and bears, oh my ``` There is also built-in `pprint` as `dict` or `json` for debugging a large `DotMap` ```python m.pprint() # {'people': {'dave': {'age': 55, 'job': 'manager'}, # 'john': {'age': 32, 'job': 'programmer'}, # 'mary': {'age': 24, 'job': 'designer'}}} m.pprint(pformat='json') # { # "people": { # "dave": { # "age": 55, # "job": "manager" # }, # "john": { # "age": 32, # "job": "programmer" # }, # "mary": { # "age": 24, # "job": "designer" # } # } # } ``` And many other features involving dots and dictionaries that will be immediately intuitive when used. Keywords: dict,dot,map,order,ordered,ordereddict,access,dynamic Platform: UNKNOWN Description-Content-Type: text/markdown ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1635785635.0 dotmap-1.3.30/README.md0000644000000000000240000000536200000000000014434 0ustar00rootstaff00000000000000# DotMap [![Build Status](https://travis-ci.com/drgrib/dotmap.svg?branch=master)](https://travis-ci.com/drgrib/dotmap) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate?business=N2GLXLS5KBFBY&item_name=Chris+Redford¤cy_code=USD) # Install ``` pip3 install dotmap ``` ## Upgrade Get updates for current installation ``` pip3 install --upgrade dotmap ``` # Features `DotMap` is a dot-access `dict` subclass that - has dynamic hierarchy creation (autovivification) - can be initialized with keys - easily initializes from `dict` - easily converts to `dict` - is ordered by insertion The key feature is exactly what you want: dot-access ```python from dotmap import DotMap m = DotMap() m.name = 'Joe' print('Hello ' + m.name) # Hello Joe ``` However, `DotMap` is a `dict` and you can treat it like a `dict` as needed ```python print(m['name']) # Joe m.name += ' Smith' m['name'] += ' Jr' print(m.name) # Joe Smith Jr ``` It also has fast, automatic hierarchy (which can be deactivated by initializing with `DotMap(_dynamic=False)`) ```python m = DotMap() m.people.steve.age = 31 ``` And key initialization ```python m = DotMap(a=1, b=2) ``` You can initialize it from `dict` and convert it to `dict` ```python d = {'a':1, 'b':2} m = DotMap(d) print(m) # DotMap(a=1, b=2) print(m.toDict()) # {'a': 1, 'b': 2} ``` And it has iteration that is ordered by insertion ```python m = DotMap() m.people.john.age = 32 m.people.john.job = 'programmer' m.people.mary.age = 24 m.people.mary.job = 'designer' m.people.dave.age = 55 m.people.dave.job = 'manager' for k, v in m.people.items(): print(k, v) print # john DotMap(age=32, job='programmer') # mary DotMap(age=24, job='designer') # dave DotMap(age=55, job='manager') ``` It also has automatic counter initialization ```python m = DotMap() for i in range(7): m.counter += 1 print(m.counter) # 7 ``` And automatic addition initializations of any other type ```python m = DotMap() m.quote += 'lions' m.quote += ' and tigers' m.quote += ' and bears' m.quote += ', oh my' print(m.quote) # lions and tigers and bears, oh my ``` There is also built-in `pprint` as `dict` or `json` for debugging a large `DotMap` ```python m.pprint() # {'people': {'dave': {'age': 55, 'job': 'manager'}, # 'john': {'age': 32, 'job': 'programmer'}, # 'mary': {'age': 24, 'job': 'designer'}}} m.pprint(pformat='json') # { # "people": { # "dave": { # "age": 55, # "job": "manager" # }, # "john": { # "age": 32, # "job": "programmer" # }, # "mary": { # "age": 24, # "job": "designer" # } # } # } ``` And many other features involving dots and dictionaries that will be immediately intuitive when used. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1649262403.8868747 dotmap-1.3.30/dotmap/0000755000000000000240000000000000000000000014433 5ustar00rootstaff00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649262382.0 dotmap-1.3.30/dotmap/__init__.py0000755000000000000240000005106000000000000016551 0ustar00rootstaff00000000000000from __future__ import print_function from collections import OrderedDict try: from collections.abc import MutableMapping, Iterable except ImportError: from collections import MutableMapping, Iterable 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) __all__ = ['DotMap'] class DotMap(MutableMapping, OrderedDict): def __init__(self, *args, **kwargs): self._map = OrderedDict() self._dynamic = kwargs.pop('_dynamic', True) self._prevent_method_masking = kwargs.pop('_prevent_method_masking', False) _key_convert_hook = kwargs.pop('_key_convert_hook', None) trackedIDs = kwargs.pop('_trackedIDs', {}) if args: d = args[0] # for recursive assignment handling trackedIDs[id(d)] = self src = [] if isinstance(d, MutableMapping): src = self.__call_items(d) elif isinstance(d, Iterable): src = d for k,v in src: if self._prevent_method_masking and k in reserved_keys: raise KeyError('"{}" is reserved'.format(k)) if _key_convert_hook: k = _key_convert_hook(k) if isinstance(v, dict): idv = id(v) if idv in trackedIDs: v = trackedIDs[idv] else: trackedIDs[idv] = v v = self.__class__(v, _dynamic=self._dynamic, _prevent_method_masking = self._prevent_method_masking, _key_convert_hook =_key_convert_hook, _trackedIDs = trackedIDs) if type(v) is list: l = [] for i in v: n = i if isinstance(i, dict): idi = id(i) if idi in trackedIDs: n = trackedIDs[idi] else: trackedIDs[idi] = i n = self.__class__(i, _dynamic=self._dynamic, _key_convert_hook =_key_convert_hook, _prevent_method_masking = self._prevent_method_masking) l.append(n) v = l self._map[k] = v if kwargs: for k,v in self.__call_items(kwargs): if self._prevent_method_masking and k in reserved_keys: raise KeyError('"{}" is reserved'.format(k)) if _key_convert_hook: k = _key_convert_hook(k) 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] = self.__class__() return self._map[k] def __setattr__(self, k, v): if k in {'_map','_dynamic', '_ipython_canary_method_should_not_exist_', '_prevent_method_masking'}: super(DotMap, self).__setattr__(k,v) elif self._prevent_method_masking and k in reserved_keys: raise KeyError('"{}" is reserved'.format(k)) else: self[k] = v def __getattr__(self, k): if k.startswith('__') and k.endswith('__'): raise AttributeError(k) if k in {'_map','_dynamic','_ipython_canary_method_should_not_exist_'}: return super(DotMap, self).__getattr__(k) try: v = super(self.__class__, self).__getattribute__(k) return v except AttributeError: pass try: return self[k] except KeyError: raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{k}'") from None def __delattr__(self, key): return self._map.__delitem__(key) def __contains__(self, k): return self._map.__contains__(k) def __add__(self, other): if self.empty(): return other else: self_type = type(self).__name__ other_type = type(other).__name__ msg = "unsupported operand type(s) for +: '{}' and '{}'" raise TypeError(msg.format(self_type, other_type)) def __str__(self, seen = None): items = [] seen = {id(self)} if seen is None else seen for k,v in self.__call_items(self._map): # circular assignment case if isinstance(v, self.__class__): if id(v) in seen: items.append('{0}={1}(...)'.format(k, self.__class__.__name__)) else: seen.add(id(v)) items.append('{0}={1}'.format(k, v.__str__(seen))) 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, seen = None): if seen is None: seen = {} d = {} seen[id(self)] = d for k,v in self.items(): if issubclass(type(v), DotMap): idv = id(v) if idv in seen: v = seen[idv] else: v = v.toDict(seen = seen) elif type(v) in (list, tuple): l = [] for i in v: n = i if issubclass(type(i), DotMap): idv = id(n) if idv in seen: n = seen[idv] else: n = i.toDict(seen = seen) 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 issubclass(type(other), 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 self.__class__(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): return 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 = cls() 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, self.__class__) and v != self.__class__(): # 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 == self.__class__.__name__: lines.append('-') out = '' if isinstance(v, self.__class__): name = k subMap = v out = self._getSubMapStr(name,subMap) lines.append(out) previous = self.__class__.__name__ else: out = self._getValueStr(k,v) lines.append(out) previous = 'other' lines.append('--') s = '\n'.join(lines) return s reserved_keys = {i for i in dir(DotMap) if not i.startswith('__') and not i.endswith('__')} 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 AttributeError: print('AttributeError 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 AttributeError: print('AttributeError 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) # unpacking tests ''' print('\n== Unpacking ==') d = {'a':1} print({**d}) m = DotMap(a=1) print({**m.toDict()}) m = DotMap(a=1) print({**m}) ''' print('\n== DotMap subclass ==') class MyDotMap(DotMap): def __getitem__(self, k): return super(MyDotMap, self).__getitem__(k) my = MyDotMap() my.x.y.z = 3 print(my) # subclass with existing property class PropertyDotMap(MyDotMap): def __init__(self, *args, **kwargs): super(MyDotMap, self).__init__(*args, **kwargs) self._myprop = MyDotMap({'nested': 123}) @property def first(self): return self._myprop p = PropertyDotMap() print(p.first) print(p.first.nested) p.first.second.third = 456 print(p.first.second.third) print('\n== DotMap method masking ==') # method masking tests d = DotMap(a=1,get='mango') d = DotMap((('a',1),('get','mango'))) d = DotMap({'a':1, 'get': 'mango'}) d = DotMap({'a':1, 'b': {'get': 'mango'}}) d.a = {'get':'mongo'} try: d = DotMap(a=1,get='mango', _prevent_method_masking = True) raise RuntimeError("this should fail with KeyError") except KeyError: print('kwargs method masking ok') try: d = DotMap((('a',1),('get','mango')), _prevent_method_masking = True) raise RuntimeError("this should fail with KeyError") except KeyError: print('iterable method masking ok') try: d = DotMap({'a':1, 'get': 'mango'}, _prevent_method_masking = True) raise RuntimeError("this should fail with KeyError") except KeyError: print('dict method masking ok') try: d = DotMap({'a':1, 'b': {'get': 'mango'}}, _prevent_method_masking = True) raise RuntimeError("this should fail with KeyError") except KeyError: print('nested dict method masking ok') try: d = DotMap({'a':1, 'b': {}}, _prevent_method_masking = True) d.b.get = 7 raise RuntimeError("this should fail with KeyError") except KeyError: print('nested dict attrib masking ok') print('\n== DotMap __init__, toDict, and __str__ with circular references ==') a = { 'name': 'a'} b = { 'name': 'b'} c = { 'name': 'c', 'list': []} # Create circular reference a['b'] = b b['c'] = c c['a'] = a c['list'].append(b) print(a) x = DotMap(a) print(x) y = x.toDict() assert id(y['b']['c']['a']) == id(y) assert id(y['b']['c']['list'][0]) == id(y['b']) print(y) # final print print() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649262382.0 dotmap-1.3.30/dotmap/test.py0000644000000000000240000003157100000000000015773 0ustar00rootstaff00000000000000import unittest from dotmap import DotMap class TestReadme(unittest.TestCase): def test_basic_use(self): m = DotMap() self.assertIsInstance(m, DotMap) m.name = 'Joe' self.assertEqual(m.name, 'Joe') self.assertEqual('Hello ' + m.name, 'Hello Joe') self.assertIsInstance(m, dict) self.assertTrue(issubclass(m.__class__, dict)) self.assertEqual(m['name'], 'Joe') m.name += ' Smith' m['name'] += ' Jr' self.assertEqual(m.name, 'Joe Smith Jr') def test_automatic_hierarchy(self): m = DotMap() m.people.steve.age = 31 self.assertEqual(m.people.steve.age, 31) def test_key_init(self): m = DotMap(a=1, b=2) self.assertEqual(m.a, 1) self.assertEqual(m.b, 2) def test_dict_conversion(self): d = {'a': 1, 'b': 2, 'c': {'d': 3, 'e': 4}} m = DotMap(d) self.assertEqual(m.a, 1) self.assertEqual(m.b, 2) d2 = m.toDict() self.assertIsInstance(d2, dict) self.assertNotIsInstance(d2, DotMap) self.assertEqual(len(d2), 3) self.assertEqual(d2['a'], 1) self.assertEqual(d2['b'], 2) self.assertNotIsInstance(d2['c'], DotMap) self.assertEqual(len(d2['c']), 2) self.assertEqual(d2['c']['d'], 3) self.assertEqual(d2['c']['e'], 4) def test_ordered_iteration(self): m = DotMap() m.people.john.age = 32 m.people.john.job = 'programmer' m.people.mary.age = 24 m.people.mary.job = 'designer' m.people.dave.age = 55 m.people.dave.job = 'manager' expected = [ ('john', 32, 'programmer'), ('mary', 24, 'designer'), ('dave', 55, 'manager'), ] for i, (k, v) in enumerate(m.people.items()): self.assertEqual(expected[i][0], k) self.assertEqual(expected[i][1], v.age) self.assertEqual(expected[i][2], v.job) class TestBasic(unittest.TestCase): def setUp(self): self.d = { 'a': 1, 'b': 2, 'subD': {'c': 3, 'd': 4} } def test_dict_init(self): m = DotMap(self.d) self.assertIsInstance(m, DotMap) self.assertEqual(m.a, 1) self.assertEqual(m.b, 2) self.assertIsInstance(m.subD, DotMap) self.assertEqual(m.subD.c, 3) self.assertEqual(m.subD.d, 4) def test_copy(self): m = DotMap(self.d) dm_copy = m.copy() self.assertIsInstance(dm_copy, DotMap) self.assertEqual(dm_copy.a, 1) self.assertEqual(dm_copy.b, 2) self.assertIsInstance(dm_copy.subD, DotMap) self.assertEqual(dm_copy.subD.c, 3) self.assertEqual(dm_copy.subD.d, 4) def test_fromkeys(self): m = DotMap.fromkeys([1, 2, 3], 'a') self.assertEqual(len(m), 3) self.assertEqual(m[1], 'a') self.assertEqual(m[2], 'a') self.assertEqual(m[3], 'a') def test_dict_functionality(self): m = DotMap(self.d) self.assertEqual(m.get('a'), 1) self.assertEqual(m.get('f', 33), 33) self.assertIsNone(m.get('f')) self.assertTrue(m.has_key('a')) self.assertFalse(m.has_key('f')) m.update([('rat', 5), ('bum', 4)], dog=7, cat=9) self.assertEqual(m.rat, 5) self.assertEqual(m.bum, 4) self.assertEqual(m.dog, 7) self.assertEqual(m.cat, 9) m.update({'lol': 1, 'ba': 2}) self.assertEqual(m.lol, 1) self.assertEqual(m.ba, 2) self.assertTrue('a' in m) self.assertFalse('c' in m) self.assertTrue('c' in m.subD) self.assertTrue(len(m.subD), 2) del m.subD.c self.assertFalse('c' in m.subD) self.assertTrue(len(m.subD), 1) def test_list_comprehension(self): parentDict = { 'name': 'Father1', 'children': [ {'name': 'Child1'}, {'name': 'Child2'}, {'name': 'Child3'}, ] } parent = DotMap(parentDict) ordered_names = ['Child1', 'Child2', 'Child3'] comp = [x.name for x in parent.children] self.assertEqual(ordered_names, comp) class TestPickle(unittest.TestCase): def setUp(self): self.d = { 'a': 1, 'b': 2, 'subD': {'c': 3, 'd': 4} } def test(self): import pickle pm = DotMap(self.d) s = pickle.dumps(pm) m = pickle.loads(s) self.assertIsInstance(m, DotMap) self.assertEqual(m.a, 1) self.assertEqual(m.b, 2) self.assertIsInstance(m.subD, DotMap) self.assertEqual(m.subD.c, 3) self.assertEqual(m.subD.d, 4) class TestEmpty(unittest.TestCase): def test(self): m = DotMap() self.assertTrue(m.empty()) m.a = 1 self.assertFalse(m.empty()) self.assertTrue(m.b.empty()) self.assertIsInstance(m.b, DotMap) class TestDynamic(unittest.TestCase): def test(self): m = DotMap() m.still.works m.sub.still.works nonDynamic = DotMap(_dynamic=False) def assignNonDynamicAttribute(): nonDynamic.no self.assertRaises(AttributeError, assignNonDynamicAttribute) def assignNonDynamicKey(): nonDynamic['no'] self.assertRaises(KeyError, assignNonDynamicKey) nonDynamicWithInit = DotMap(m, _dynamic=False) nonDynamicWithInit.still.works nonDynamicWithInit.sub.still.works def assignNonDynamicAttributeWithInit(): nonDynamicWithInit.no.creation self.assertRaises(AttributeError, assignNonDynamicAttributeWithInit) def assignNonDynamicKeyWithInit(): nonDynamicWithInit['no'].creation self.assertRaises(KeyError, assignNonDynamicKeyWithInit) class TestRecursive(unittest.TestCase): def test(self): m = DotMap() m.a = 5 m_id = id(m) m.recursive = m self.assertEqual(id(m.recursive.recursive.recursive), m_id) outStr = str(m) self.assertIn('''a=5''', outStr) self.assertIn('''recursive=DotMap(...)''', outStr) d = m.toDict() d_id = id(d) d['a'] = 5 d['recursive'] = d d['recursive']['recursive']['recursive'] self.assertEqual(id(d['recursive']['recursive']['recursive']), d_id) outStr = str(d) self.assertIn(''''a': 5''', outStr) self.assertIn('''recursive': {...}''', outStr) m2 = DotMap(d) m2_id = id(m2) self.assertEqual(id(m2.recursive.recursive.recursive), m2_id) outStr2 = str(m2) self.assertIn('''a=5''', outStr2) self.assertIn('''recursive=DotMap(...)''', outStr2) class Testkwarg(unittest.TestCase): def test(self): a = {'1': 'a', '2': 'b'} b = DotMap(a, _dynamic=False) def capture(**kwargs): return kwargs self.assertEqual(a, capture(**b.toDict())) class TestDeepCopy(unittest.TestCase): def test(self): import copy original = DotMap() original.a = 1 original.b = 3 shallowCopy = original deepCopy = copy.deepcopy(original) self.assertEqual(original, shallowCopy) self.assertEqual(id(original), id(shallowCopy)) self.assertEqual(original, deepCopy) self.assertNotEqual(id(original), id(deepCopy)) original.a = 2 self.assertEqual(original, shallowCopy) self.assertNotEqual(original, deepCopy) def test_order_preserved(self): import copy original = DotMap() original.a = 1 original.b = 2 original.c = 3 deepCopy = copy.deepcopy(original) orderedPairs = [] for k, v in original.iteritems(): orderedPairs.append((k, v)) for i, (k, v) in enumerate(deepCopy.iteritems()): self.assertEqual(k, orderedPairs[i][0]) self.assertEqual(v, orderedPairs[i][1]) class TestDotMapTupleToDict(unittest.TestCase): def test(self): m = DotMap({'a': 1, 'b': (11, 22, DotMap({'c': 3}))}) d = m.toDict() self.assertEqual(d, {'a': 1, 'b': (11, 22, {'c': 3})}) class TestOrderedDictInit(unittest.TestCase): def test(self): from collections import OrderedDict o = OrderedDict([('a', 1), ('b', 2), ('c', [OrderedDict([('d', 3)])])]) m = DotMap(o) self.assertIsInstance(m, DotMap) self.assertIsInstance(m.c[0], DotMap) class TestEmptyAdd(unittest.TestCase): def test_base(self): m = DotMap() for i in range(7): m.counter += 1 self.assertNotIsInstance(m.counter, DotMap) self.assertIsInstance(m.counter, int) self.assertEqual(m.counter, 7) def test_various(self): m = DotMap() m.a.label = 'test' m.a.counter += 2 self.assertIsInstance(m.a, DotMap) self.assertEqual(m.a.label, 'test') self.assertNotIsInstance(m.a.counter, DotMap) self.assertIsInstance(m.a.counter, int) self.assertEqual(m.a.counter, 2) m.a.counter += 1 self.assertEqual(m.a.counter, 3) def test_proposal(self): my_counters = DotMap() pages = [ 'once upon a time', 'there was like this super awesome prince', 'and there was this super rad princess', 'and they had a mutually respectful, egalitarian relationship', 'the end' ] for stuff in pages: my_counters.page += 1 self.assertIsInstance(my_counters, DotMap) self.assertNotIsInstance(my_counters.page, DotMap) self.assertIsInstance(my_counters.page, int) self.assertEqual(my_counters.page, 5) def test_string_addition(self): m = DotMap() m.quote += 'lions' m.quote += ' and tigers' m.quote += ' and bears' m.quote += ', oh my' self.assertEqual(m.quote, 'lions and tigers and bears, oh my') def test_strange_addition(self): m = DotMap() m += "I'm a string now" self.assertIsInstance(m, str) self.assertNotIsInstance(m, DotMap) self.assertEqual(m, "I'm a string now") m2 = DotMap() + "I'll replace that DotMap" self.assertEqual(m2, "I'll replace that DotMap") def test_protected_hierarchy(self): m = DotMap() m.protected_parent.key = 'value' def protectedFromAddition(): m.protected_parent += 1 self.assertRaises(TypeError, protectedFromAddition) def test_type_error_raised(self): m = DotMap() def badAddition(): m.a += 1 m.a += ' and tigers' self.assertRaises(TypeError, badAddition) # Test classes for SubclassTestCase below # class that overrides __getitem__ class MyDotMap(DotMap): def __getitem__(self, k): return super(MyDotMap, self).__getitem__(k) # subclass with existing property class PropertyDotMap(MyDotMap): def __init__(self, *args, **kwargs): super(MyDotMap, self).__init__(*args, **kwargs) self._myprop = None @property def my_prop(self): if not self._myprop: self._myprop = PropertyDotMap({'nested_prop': 123}) return self._myprop class SubclassTestCase(unittest.TestCase): def test_nested_subclass(self): my = MyDotMap() my.x.y.z = 123 self.assertEqual(my.x.y.z, 123) self.assertIsInstance(my.x, MyDotMap) self.assertIsInstance(my.x.y, MyDotMap) def test_subclass_with_property(self): p = PropertyDotMap() self.assertIsInstance(p.my_prop, PropertyDotMap) self.assertEqual(p.my_prop.nested_prop, 123) p.my_prop.second.third = 456 self.assertIsInstance(p.my_prop.second, PropertyDotMap) self.assertEqual(p.my_prop.second.third, 456) class TestKeyConvertHook(unittest.TestCase): def fix_illegal_key(self, key): return key.replace(".", "_").replace("-","_") def test(self): # by default key_convert_hook = None d = DotMap({"dot.map":123}, _dynamic=False) self.assertRaises(AttributeError, lambda: d.dot.map) # replace @ with _ d = DotMap({"dot@map":"dot_map"}, _key_convert_hook = lambda k: k.replace('@','_')) self.assertEqual(d.dot_map, "dot_map") # replace multiple keys, apply to nested fields d = DotMap({"dot@map":{"dot!map":"dot_map"}}, _key_convert_hook = lambda k: k.replace('@','_').replace('!','_')) self.assertEqual(d.dot_map.dot_map, "dot_map") # replace multiple keys with a _key_convert_hook function d = DotMap({"dot-map":123}, _key_convert_hook = self.fix_illegal_key) self.assertEqual(d.dot_map, 123) # replace the entire key with another one d = DotMap({"dot!map":456}, _key_convert_hook = lambda k: 'DOTMAP' if k == 'dot!map' else k) self.assertEqual(d.DOTMAP, 456) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1649262403.8903496 dotmap-1.3.30/dotmap.egg-info/0000755000000000000240000000000000000000000016125 5ustar00rootstaff00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649262403.0 dotmap-1.3.30/dotmap.egg-info/PKG-INFO0000644000000000000240000001045700000000000017231 0ustar00rootstaff00000000000000Metadata-Version: 2.1 Name: dotmap Version: 1.3.30 Summary: ordered, dynamically-expandable dot-access dictionary Home-page: https://github.com/drgrib/dotmap Author: Chris Redford Author-email: credford@gmail.com License: MIT Download-URL: https://github.com/drgrib/dotmap/tarball/1.0 Description: # DotMap [![Build Status](https://travis-ci.com/drgrib/dotmap.svg?branch=master)](https://travis-ci.com/drgrib/dotmap) [![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.com/donate?business=N2GLXLS5KBFBY&item_name=Chris+Redford¤cy_code=USD) # Install ``` pip3 install dotmap ``` ## Upgrade Get updates for current installation ``` pip3 install --upgrade dotmap ``` # Features `DotMap` is a dot-access `dict` subclass that - has dynamic hierarchy creation (autovivification) - can be initialized with keys - easily initializes from `dict` - easily converts to `dict` - is ordered by insertion The key feature is exactly what you want: dot-access ```python from dotmap import DotMap m = DotMap() m.name = 'Joe' print('Hello ' + m.name) # Hello Joe ``` However, `DotMap` is a `dict` and you can treat it like a `dict` as needed ```python print(m['name']) # Joe m.name += ' Smith' m['name'] += ' Jr' print(m.name) # Joe Smith Jr ``` It also has fast, automatic hierarchy (which can be deactivated by initializing with `DotMap(_dynamic=False)`) ```python m = DotMap() m.people.steve.age = 31 ``` And key initialization ```python m = DotMap(a=1, b=2) ``` You can initialize it from `dict` and convert it to `dict` ```python d = {'a':1, 'b':2} m = DotMap(d) print(m) # DotMap(a=1, b=2) print(m.toDict()) # {'a': 1, 'b': 2} ``` And it has iteration that is ordered by insertion ```python m = DotMap() m.people.john.age = 32 m.people.john.job = 'programmer' m.people.mary.age = 24 m.people.mary.job = 'designer' m.people.dave.age = 55 m.people.dave.job = 'manager' for k, v in m.people.items(): print(k, v) print # john DotMap(age=32, job='programmer') # mary DotMap(age=24, job='designer') # dave DotMap(age=55, job='manager') ``` It also has automatic counter initialization ```python m = DotMap() for i in range(7): m.counter += 1 print(m.counter) # 7 ``` And automatic addition initializations of any other type ```python m = DotMap() m.quote += 'lions' m.quote += ' and tigers' m.quote += ' and bears' m.quote += ', oh my' print(m.quote) # lions and tigers and bears, oh my ``` There is also built-in `pprint` as `dict` or `json` for debugging a large `DotMap` ```python m.pprint() # {'people': {'dave': {'age': 55, 'job': 'manager'}, # 'john': {'age': 32, 'job': 'programmer'}, # 'mary': {'age': 24, 'job': 'designer'}}} m.pprint(pformat='json') # { # "people": { # "dave": { # "age": 55, # "job": "manager" # }, # "john": { # "age": 32, # "job": "programmer" # }, # "mary": { # "age": 24, # "job": "designer" # } # } # } ``` And many other features involving dots and dictionaries that will be immediately intuitive when used. Keywords: dict,dot,map,order,ordered,ordereddict,access,dynamic Platform: UNKNOWN Description-Content-Type: text/markdown ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649262403.0 dotmap-1.3.30/dotmap.egg-info/SOURCES.txt0000644000000000000240000000031600000000000020011 0ustar00rootstaff00000000000000LICENSE.txt MANIFEST.in README.md setup.cfg setup.py dotmap/__init__.py dotmap/test.py dotmap.egg-info/PKG-INFO dotmap.egg-info/SOURCES.txt dotmap.egg-info/dependency_links.txt dotmap.egg-info/top_level.txt././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649262403.0 dotmap-1.3.30/dotmap.egg-info/dependency_links.txt0000644000000000000240000000000100000000000022173 0ustar00rootstaff00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649262403.0 dotmap-1.3.30/dotmap.egg-info/top_level.txt0000644000000000000240000000000700000000000020654 0ustar00rootstaff00000000000000dotmap ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1649262403.8974864 dotmap-1.3.30/setup.cfg0000644000000000000240000000012000000000000014761 0ustar00rootstaff00000000000000[metadata] description-file = README.rst [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1649262393.0 dotmap-1.3.30/setup.py0000644000000000000240000000136500000000000014666 0ustar00rootstaff00000000000000from setuptools import setup with open("README.md", "r") as fh: long_description = fh.read() setup( version = '1.3.30', name='dotmap', packages=['dotmap'], # this must be the same as the name above description='ordered, dynamically-expandable dot-access dictionary', author='Chris Redford', author_email='credford@gmail.com', url='https://github.com/drgrib/dotmap', # use the URL to the github repo download_url='https://github.com/drgrib/dotmap/tarball/1.0', keywords=['dict', 'dot', 'map', 'order', 'ordered', 'ordereddict', 'access', 'dynamic'], # arbitrary keywords classifiers=[], long_description=long_description, long_description_content_type="text/markdown", license="MIT", )