"""
note that this requires python v3.6 or higher to preserve the order of the fields input
this is b/c keyword arguments in this version or higher are now defaulted to
ordered dicts
"""
import sys
import warnings
if sys.version_info.major < 3 or sys.version_info.minor < 6:
warnings.warn('requires python 3.6+ to preserve key order of inputs')
class Vocab:
"""
>>> TERM = Vocab(a='a', b=2, c='c')
>>> TERM.a
'a'
>>> TERM.b
2
>>> 2 in TERM
True
>>> TERM.fields
['a', 'b', 'c']
>>> TERM.values()
['a', 2, 'c']
>>> TERM['b']
2
>>> TERM.reverse(2)
'b'
"""
def __init__(self, **kw):
"""
for a set of keyword arguments returns an object with
the key names as attributes set to their input values
can test for membership
"""
if 'fields' in kw:
raise AttributeError('fields cannot be specified. It is a reserved name')
fields = []
for name, value in kw.items():
fields.append(name)
setattr(self, name, value)
if sys.version_info.major < 3 or sys.version_info.minor < 6:
self.fields = sorted(fields)
else:
self.fields = fields
def __contains__(self, value):
"""
test for membership
"""
for f in self.fields:
if value == getattr(self, f):
return True
return False
def __getitem__(self, key):
if not hasattr(self, key):
raise KeyError(key)
return getattr(self, key)
def values(self):
values = []
for f in self.fields:
values.append( getattr(self, f) )
return values
def enforce(self, value):
if value not in self:
raise KeyError('value {0} is not a valid member of the vocabulary object'.format(value), self.values() )
return value
def items(self):
items = []
for f in self.fields:
items.append((f, getattr(self, f)))
return items
def reverse(self, value, unique=True):
result = []
for f, v in self.items():
if v == value:
result.append(f)
if len(result) < 1:
raise KeyError('could not reverse mapping. the value {0} is not valid'.format(value))
if unique:
if len(result) > 1:
raise KeyError('mapping to value is not unique. specify unique=False to return a list')
return result[0]
return result
if __name__ == '__main__':
import doctest
doctest.testmod()