# -*- coding: utf-8 -*-
"""
Structure or dictionary like classes for miscellaneous usage.
"""
from __future__ import print_function, absolute_import, division, unicode_literals
import six
from bronx.compat.moves import collections_abc
[docs]class Foo(object):
"""
Protected C-struct like class... for gathering anything.
Internal dict methods could be called through i_*methodname* protection.
"""
def __init__(self, **kw):
self.__dict__.update(kw)
[docs] def as_dict(self):
"""Returns the object's content as a dictionary."""
return self.__dict__
def __getattr__(self, attr):
if attr.startswith('i_'):
return getattr(self.__dict__, attr.split('_', 1)[1])
else:
raise AttributeError
def __str__(self):
return str(self.__dict__)
[docs]class ReadOnlyDict(collections_abc.Mapping):
"""A type of read-only dictionary.
Example::
>>> rodict = ReadOnlyDict()
>>> len(rodict)
0
>>> rodict['a'] = 1
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'ReadOnlyDict' object does not support item assignment
>>> rodict = ReadOnlyDict(dict(a=1, b=2))
>>> len(rodict)
2
>>> rodict['a']
1
>>> print(','.join(rodict))
a,b
>>> print(','.join([str(x) for k, x in sorted(rodict.items())]))
1,2
"""
def __init__(self, data=None):
if data is None:
self._data = dict()
else:
self._data = dict(data)
def __getitem__(self, key):
return self._data[key]
def __len__(self):
return len(self._data)
def __iter__(self):
return iter(self._data)
def __repr__(self):
return repr(self._data)
def __str__(self):
return str(self._data)
[docs]class SpecialDict(dict):
"""
Add some special features to std dict especially the ability to remap
keys on the fly.
This class behaves like a usual dictionary, to be useful, it should be
subclassed and the method :meth:`remap` should be redefined in order to
implement a customised key remaping
"""
def __init__(self, *kargs, **kwargs):
tmpdict = dict(*kargs, **kwargs)
# Check the dictionnary keys. If necessary change them
for k, v in [(k, v) for k, v in six.iteritems(tmpdict) if k != self.remap(k)]:
del tmpdict[k]
tmpdict[self.remap(k)] = v
super(SpecialDict, self).__init__(tmpdict)
[docs] def show(self, ljust=24):
"""Print the actual values of the dictionary."""
for k in sorted(self.keys()):
print('+', k.ljust(ljust), '=', self.get(k))
[docs] def update(self, *args, **kw):
"""Extended dictionary update with args as dict and extra keywords."""
args = list(args)
args.append(kw)
for objiter in args:
for k, v in objiter.items():
self.__setitem__(k, v)
def __call__(self, **kw):
"""Calling a special dict is equivalent to updating."""
self.update(**kw)
[docs] def remap(self, key):
"""Return a new value for the actual ``key``. Default is identity."""
return key
def __getitem__(self, key):
"""Force remapped key retrieval."""
return dict.__getitem__(self, self.remap(key))
def __setitem__(self, key, value):
"""Force remapped key setting."""
dict.__setitem__(self, self.remap(key), value)
def __delitem__(self, key):
"""Force remapped key deletion."""
dict.__delitem__(self, self.remap(key))
def __contains__(self, key):
"""Force remapped key ``in`` checking."""
return (dict.__contains__(self, key) or # Try with out a remap first... just in case
dict.__contains__(self, self.remap(key)))
[docs]class LowerCaseDict(SpecialDict):
"""A dictionary with only lower case keys.
Example::
>>> lcdict = LowerCaseDict()
>>> len(lcdict)
0
>>> lcdict['Ab'] = 1
>>> lcdict['cD'] = 2
>>> 'ab' in lcdict
True
>>> 'AB' in lcdict
True
>>> 'Ab' in lcdict
True
>>> 'abc' in lcdict
False
>>> print(','.join(sorted(lcdict.keys())))
ab,cd
>>> lcdict.show()
+ ab = 1
+ cd = 2
"""
[docs] def remap(self, key):
"""Return a lower case value of the actual key."""
return key.lower()
[docs]class UpperCaseDict(SpecialDict):
"""A dictionary with only upper case keys."""
[docs] def remap(self, key):
"""Return a upper case value of the actual key."""
return key.upper()
if __name__ == '__main__':
import doctest
doctest.testmod()