From 5470a0d29a9131a5b95e6092df898ee579600e07 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Sat, 19 Jul 2008 00:56:09 +0000 Subject: 3: Finished NameSpace and cerresponding unit tests --- ipalib/base.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 7 deletions(-) (limited to 'ipalib/base.py') diff --git a/ipalib/base.py b/ipalib/base.py index 70cfe5673..eb84dd12a 100644 --- a/ipalib/base.py +++ b/ipalib/base.py @@ -18,10 +18,10 @@ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ -Base classes in for plug-in architecture and generative API. +Base classes for plug-in architecture and generative API. """ -from exceptions import NameSpaceError +from exceptions import SetAttributeError class Command(object): @@ -47,26 +47,90 @@ class Argument(object): class NameSpace(object): + """ + A read-only namespace of (key, value) pairs that can be accessed + both as instance attributes and as dictionary items. For example: + + >>> ns = NameSpace(dict(my_message='Hello world!')) + >>> ns.my_message + 'Hello world!' + >>> ns['my_message'] + 'Hello world!' + + Keep in mind that Python doesn't offer true ready-only attributes. A + NameSpace is read-only in that it prevents programmers from + *accidentally* setting its attributes, but a motivated programmer can + still set them. + + For example, setting an attribute the normal way will raise an exception: + + >>> ns.my_message = 'some new value' + (raises ipalib.exceptions.SetAttributeError) + + But a programmer could still set the attribute like this: + + >>> ns.__dict__['my_message'] = 'some new value' + + You should especially not implement a security feature that relies upon + NameSpace being strictly read-only. + """ + + __locked = False # Whether __setattr__ has been locked + def __init__(self, kw): + """ + The single constructor argument `kw` is a dict of the (key, value) + pairs to be in this NameSpace instance. + """ assert isinstance(kw, dict) self.__kw = dict(kw) for (key, value) in self.__kw.items(): assert not key.startswith('_') setattr(self, key, value) self.__keys = sorted(self.__kw) + self.__locked = True + + def __setattr__(self, name, value): + """ + Raises an exception if trying to set an attribute after the + NameSpace has been locked; otherwise calls object.__setattr__(). + """ + if self.__locked: + raise SetAttributeError(name) + super(NameSpace, self).__setattr__(name, value) def __getitem__(self, key): + """ + Returns item from namespace named `key`. + """ return self.__kw[key] - def __iter__(self): - for key in self.__keys: - yield key - - + def __hasitem__(self, key): + """ + Returns True if namespace has an item named `key`. + """ + return key in self.__kw + def __iter__(self): + """ + Yields the names in this NameSpace in ascending order. + For example: + >>> ns = NameSpace(dict(attr_b='world', attr_a='hello')) + >>> list(ns) + ['attr_a', 'attr_b'] + >>> [ns[k] for k in ns] + ['hello', 'world'] + """ + for key in self.__keys: + yield key + def __len__(self): + """ + Returns number of items in this NameSpace. + """ + return len(self.__keys) class API(object): -- cgit