From 5ddcf0ccc33bece1806272083f217ec265ab33ee Mon Sep 17 00:00:00 2001 From: Felix Schwarz Date: Sat, 11 Jul 2026 11:00:47 +0200 Subject: [PATCH] remove manual verification block in the code All important tests should covered by the test suite now. --- dotmap/__init__.py | 336 --------------------------------------------- dotmap/test.py | 45 ++++++ 2 files changed, 45 insertions(+), 336 deletions(-) diff --git a/dotmap/__init__.py b/dotmap/__init__.py index c4a74bf..6822e8e 100755 --- a/dotmap/__init__.py +++ b/dotmap/__init__.py @@ -392,339 +392,3 @@ class StaticDotMap(DotMap): 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() diff --git a/dotmap/test.py b/dotmap/test.py index 63a41e8..a7663d2 100644 --- a/dotmap/test.py +++ b/dotmap/test.py @@ -2,6 +2,8 @@ import pickle import unittest from collections import OrderedDict +from contextlib import redirect_stdout +from io import StringIO from dotmap import DotMap, StaticDotMap @@ -346,6 +348,25 @@ def test(self): self.assertIsInstance(m.c[0], DotMap) +class TestFormatting(unittest.TestCase): + def test_values_preserve_order(self): + m = DotMap() + m.alpha = 1 + m.bravo = 2 + m.charlie = 3 + + self.assertEqual(list(m.values()), [1, 2, 3]) + + def test_pprint_outputs_plain_dict(self): + m = DotMap({'a': 1, 'sub': {'b': 2}}) + buf = StringIO() + + with redirect_stdout(buf): + m.pprint() + + self.assertEqual(buf.getvalue(), "{'a': 1, 'sub': {'b': 2}}\n") + + class TestEmptyAdd(unittest.TestCase): def test_base(self): m = DotMap() @@ -418,6 +439,19 @@ def badAddition(): class TestMethodMasking(unittest.TestCase): + def test_prevent_method_masking_rejects_reserved_keys(self): + with self.assertRaises(KeyError): + DotMap(a=1, get='mango', _prevent_method_masking=True) + + with self.assertRaises(KeyError): + DotMap((('a', 1), ('get', 'mango')), _prevent_method_masking=True) + + with self.assertRaises(KeyError): + DotMap({'a': 1, 'get': 'mango'}, _prevent_method_masking=True) + + with self.assertRaises(KeyError): + DotMap({'a': 1, 'b': {'get': 'mango'}}, _prevent_method_masking=True) + def test_dynamic_key_is_not_reserved(self): m = DotMap({'a': 1, '_dynamic': 2}, _prevent_method_masking=True) self.assertEqual(m.a, 1) @@ -641,3 +675,14 @@ def test(self): # 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) + + +class TestInitFromDotMap(unittest.TestCase): + def test_nested_dotmaps_are_copied(self): + source = DotMap({'a': 1, 'sub': {'b': 2}}) + copied = DotMap(source) + + self.assertEqual(copied.a, 1) + self.assertEqual(copied.sub.b, 2) + self.assertIsNot(copied, source) + self.assertIsNot(copied.sub, source.sub)