From b4dc333ee2a010f3629002932d06a8b8a10df1d3 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 2 Jan 2009 00:46:45 -0700 Subject: Removed depreciated code in ipalib.plugable that has been moving into ipalib.base --- ipalib/plugable.py | 251 +------------------------------------ tests/test_ipalib/test_plugable.py | 191 ---------------------------- 2 files changed, 2 insertions(+), 440 deletions(-) diff --git a/ipalib/plugable.py b/ipalib/plugable.py index 0120f9729..923b72ad6 100644 --- a/ipalib/plugable.py +++ b/ipalib/plugable.py @@ -36,104 +36,9 @@ import subprocess import errors from errors import check_type, check_isinstance from config import Env -from constants import DEFAULT_CONFIG import util - - - -class ReadOnly(object): - """ - Base class for classes with read-only attributes. - - Be forewarned that Python does not offer true read-only user defined - classes. In particular, do not rely upon the read-only-ness of this - class for security purposes. - - The point of this class is not to make it impossible to set or delete - attributes, but to make it impossible to accidentally do so. The plugins - are not thread-safe: in the server, they are loaded once and the same - instances will be used to process many requests. Therefore, it is - imperative that they not set any instance attributes after they have - been initialized. This base class enforces that policy. - - For example: - - >>> ro = ReadOnly() # Initially unlocked, can setattr, delattr - >>> ro.name = 'John Doe' - >>> ro.message = 'Hello, world!' - >>> del ro.message - >>> ro.__lock__() # Now locked, cannot setattr, delattr - >>> ro.message = 'How are you?' - Traceback (most recent call last): - File "", line 1, in - File ".../ipalib/plugable.py", line 93, in __setattr__ - (self.__class__.__name__, name) - AttributeError: read-only: cannot set ReadOnly.message - >>> del ro.name - Traceback (most recent call last): - File "", line 1, in - File "/home/jderose/projects/freeipa2/ipalib/plugable.py", line 104, in __delattr__ - (self.__class__.__name__, name) - AttributeError: read-only: cannot del ReadOnly.name - """ - - __locked = False - - def __lock__(self): - """ - Put this instance into a read-only state. - - After the instance has been locked, attempting to set or delete an - attribute will raise AttributeError. - """ - assert self.__locked is False, '__lock__() can only be called once' - self.__locked = True - - def __islocked__(self): - """ - Return True if instance is locked, otherwise False. - """ - return self.__locked - - def __setattr__(self, name, value): - """ - If unlocked, set attribute named ``name`` to ``value``. - - If this instance is locked, AttributeError will be raised. - """ - if self.__locked: - raise AttributeError('read-only: cannot set %s.%s' % - (self.__class__.__name__, name) - ) - return object.__setattr__(self, name, value) - - def __delattr__(self, name): - """ - If unlocked, delete attribute named ``name``. - - If this instance is locked, AttributeError will be raised. - """ - if self.__locked: - raise AttributeError('read-only: cannot del %s.%s' % - (self.__class__.__name__, name) - ) - return object.__delattr__(self, name) - - -def lock(readonly): - """ - Lock a `ReadOnly` instance. - - This is mostly a convenience function to call `ReadOnly.__lock__()`. It - also verifies that the locking worked using `ReadOnly.__islocked__()` - - :param readonly: An instance of the `ReadOnly` class. - """ - if not isinstance(readonly, ReadOnly): - raise ValueError('not a ReadOnly instance: %r' % readonly) - readonly.__lock__() - assert readonly.__islocked__(), 'Ouch! The locking failed?' - return readonly +from base import ReadOnly, NameSpace, lock, islocked, check_name +from constants import DEFAULT_CONFIG class SetProxy(ReadOnly): @@ -512,158 +417,6 @@ class PluginProxy(SetProxy): ) -def check_name(name): - """ - Verify that ``name`` is suitable for a `NameSpace` member name. - - Raises `errors.NameSpaceError` if ``name`` is not a valid Python - identifier suitable for use as the name of `NameSpace` member. - - :param name: Identifier to test. - """ - check_type(name, str, 'name') - regex = r'^[a-z][_a-z0-9]*[a-z0-9]$' - if re.match(regex, name) is None: - raise errors.NameSpaceError(name, regex) - return name - - -class NameSpace(ReadOnly): - """ - A read-only namespace with handy container behaviours. - - Each member of a NameSpace instance must have a ``name`` attribute whose - value: - - 1. Is unique among the members - 2. Passes the `check_name()` function - - Beyond that, no restrictions are placed on the members: they can be - classes or instances, and of any type. - - The members can be accessed as attributes on the NameSpace instance or - through a dictionary interface. For example: - - >>> class obj(object): - ... name = 'my_obj' - ... - >>> namespace = NameSpace([obj]) - >>> obj is getattr(namespace, 'my_obj') # As attribute - True - >>> obj is namespace['my_obj'] # As dictionary item - True - - Here is a more detailed example: - - >>> class Member(object): - ... def __init__(self, i): - ... self.i = i - ... self.name = 'member_%d' % i - ... def __repr__(self): - ... return 'Member(%d)' % self.i - ... - >>> namespace = NameSpace(Member(i) for i in xrange(3)) - >>> namespace.member_0 is namespace['member_0'] - True - >>> len(namespace) # Returns the number of members in namespace - 3 - >>> list(namespace) # As iterable, iterates through the member names - ['member_0', 'member_1', 'member_2'] - >>> list(namespace()) # Calling a NameSpace iterates through the members - [Member(0), Member(1), Member(2)] - >>> 'member_1' in namespace # Does namespace contain 'member_1'? - True - """ - - def __init__(self, members, sort=True): - """ - :param members: An iterable providing the members. - :param sort: Whether to sort the members by member name. - """ - self.__sort = check_type(sort, bool, 'sort') - if self.__sort: - self.__members = tuple(sorted(members, key=lambda m: m.name)) - else: - self.__members = tuple(members) - self.__names = tuple(m.name for m in self.__members) - self.__map = dict() - for member in self.__members: - name = check_name(member.name) - assert name not in self.__map, 'already has key %r' % name - self.__map[name] = member - assert not hasattr(self, name), 'already has attribute %r' % name - setattr(self, name, member) - lock(self) - - def __len__(self): - """ - Return the number of members. - """ - return len(self.__members) - - def __iter__(self): - """ - Iterate through the member names. - - If this instance was created with ``sort=True``, the names will be in - alphabetical order; otherwise the names will be in the same order as - the members were passed to the constructor. - - This method is like an ordered version of dict.iterkeys(). - """ - for name in self.__names: - yield name - - def __call__(self): - """ - Iterate through the members. - - If this instance was created with ``sort=True``, the members will be - in alphabetical order by name; otherwise the members will be in the - same order as they were passed to the constructor. - - This method is like an ordered version of dict.itervalues(). - """ - for member in self.__members: - yield member - - def __contains__(self, name): - """ - Return True if namespace has a member named ``name``. - """ - return name in self.__map - - def __getitem__(self, spec): - """ - Return a member by name or index, or returns a slice of members. - - :param spec: The name or index of a member, or a slice object. - """ - if type(spec) is str: - return self.__map[spec] - if type(spec) in (int, slice): - return self.__members[spec] - raise TypeError( - 'spec: must be %r, %r, or %r; got %r' % (str, int, slice, spec) - ) - - def __repr__(self): - """ - Return a pseudo-valid expression that could create this instance. - """ - return '%s(<%d members>, sort=%r)' % ( - self.__class__.__name__, - len(self), - self.__sort, - ) - - def __todict__(self): - """ - Return a copy of the private dict mapping name to member. - """ - return dict(self.__map) - - class Registrar(DictProxy): """ Collects plugin classes as they are registered. diff --git a/tests/test_ipalib/test_plugable.py b/tests/test_ipalib/test_plugable.py index b05943235..9eb102ff4 100644 --- a/tests/test_ipalib/test_plugable.py +++ b/tests/test_ipalib/test_plugable.py @@ -28,100 +28,6 @@ from tests.util import ClassChecker, create_test_api from ipalib import plugable, errors -class test_ReadOnly(ClassChecker): - """ - Test the `ipalib.plugable.ReadOnly` class - """ - _cls = plugable.ReadOnly - - def test_class(self): - """ - Test the `ipalib.plugable.ReadOnly` class - """ - assert self.cls.__bases__ == (object,) - assert callable(self.cls.__lock__) - assert callable(self.cls.__islocked__) - - def test_lock(self): - """ - Test the `ipalib.plugable.ReadOnly.__lock__` method. - """ - o = self.cls() - assert o._ReadOnly__locked is False - o.__lock__() - assert o._ReadOnly__locked is True - e = raises(AssertionError, o.__lock__) # Can only be locked once - assert str(e) == '__lock__() can only be called once' - assert o._ReadOnly__locked is True # This should still be True - - def test_lock(self): - """ - Test the `ipalib.plugable.ReadOnly.__islocked__` method. - """ - o = self.cls() - assert o.__islocked__() is False - o.__lock__() - assert o.__islocked__() is True - - def test_setattr(self): - """ - Test the `ipalib.plugable.ReadOnly.__setattr__` method. - """ - o = self.cls() - o.attr1 = 'Hello, world!' - assert o.attr1 == 'Hello, world!' - o.__lock__() - for name in ('attr1', 'attr2'): - e = raises(AttributeError, setattr, o, name, 'whatever') - assert str(e) == 'read-only: cannot set ReadOnly.%s' % name - assert o.attr1 == 'Hello, world!' - - def test_delattr(self): - """ - Test the `ipalib.plugable.ReadOnly.__delattr__` method. - """ - o = self.cls() - o.attr1 = 'Hello, world!' - o.attr2 = 'How are you?' - assert o.attr1 == 'Hello, world!' - assert o.attr2 == 'How are you?' - del o.attr1 - assert not hasattr(o, 'attr1') - o.__lock__() - e = raises(AttributeError, delattr, o, 'attr2') - assert str(e) == 'read-only: cannot del ReadOnly.attr2' - assert o.attr2 == 'How are you?' - - -def test_lock(): - """ - Test the `ipalib.plugable.lock` function. - """ - f = plugable.lock - - # Test on a ReadOnly instance: - o = plugable.ReadOnly() - assert not o.__islocked__() - assert f(o) is o - assert o.__islocked__() - - # Test on something not subclassed from ReadOnly: - class not_subclass(object): - def __lock__(self): - pass - def __islocked__(self): - return True - o = not_subclass() - raises(ValueError, f, o) - - # Test that it checks __islocked__(): - class subclass(plugable.ReadOnly): - def __islocked__(self): - return False - o = subclass() - raises(AssertionError, f, o) - - class test_SetProxy(ClassChecker): """ Test the `ipalib.plugable.SetProxy` class. @@ -472,7 +378,6 @@ class test_Plugin(ClassChecker): assert e.argv == ('/bin/false',) - class test_PluginProxy(ClassChecker): """ Test the `ipalib.plugable.PluginProxy` class. @@ -595,102 +500,6 @@ class test_PluginProxy(ClassChecker): assert read_only(c, 'name') == 'another_name' -def test_check_name(): - """ - Test the `ipalib.plugable.check_name` function. - """ - f = plugable.check_name - okay = [ - 'user_add', - 'stuff2junk', - 'sixty9', - ] - nope = [ - '_user_add', - '__user_add', - 'user_add_', - 'user_add__', - '_user_add_', - '__user_add__', - '60nine', - ] - for name in okay: - assert name is f(name) - e = raises(TypeError, f, unicode(name)) - assert str(e) == errors.TYPE_FORMAT % ('name', str, unicode(name)) - for name in nope: - raises(errors.NameSpaceError, f, name) - for name in okay: - raises(errors.NameSpaceError, f, name.upper()) - -class DummyMember(object): - def __init__(self, i): - assert type(i) is int - self.name = 'member_%02d' % i - - -class test_NameSpace(ClassChecker): - """ - Test the `ipalib.plugable.NameSpace` class. - """ - _cls = plugable.NameSpace - - def test_class(self): - """ - Test the `ipalib.plugable.NameSpace` class. - """ - assert self.cls.__bases__ == (plugable.ReadOnly,) - - def test_init(self): - """ - Test the `ipalib.plugable.NameSpace.__init__` method. - """ - o = self.cls(tuple()) - assert list(o) == [] - assert list(o()) == [] - for cnt in (10, 25): - members = tuple(DummyMember(cnt - i) for i in xrange(cnt)) - for sort in (True, False): - o = self.cls(members, sort=sort) - if sort: - ordered = tuple(sorted(members, key=lambda m: m.name)) - else: - ordered = members - names = tuple(m.name for m in ordered) - assert o.__todict__() == dict((o.name, o) for o in ordered) - - # Test __len__: - assert len(o) == cnt - - # Test __contains__: - for name in names: - assert name in o - assert ('member_00') not in o - - # Test __iter__, __call__: - assert tuple(o) == names - assert tuple(o()) == ordered - - # Test __getitem__, getattr: - for (i, member) in enumerate(ordered): - assert o[i] is member - name = member.name - assert o[name] is member - assert read_only(o, name) is member - - # Test negative indexes: - for i in xrange(1, cnt + 1): - assert o[-i] is ordered[-i] - - # Test slices: - assert o[2:cnt-5] == ordered[2:cnt-5] - assert o[::3] == ordered[::3] - - # Test __repr__: - assert repr(o) == \ - 'NameSpace(<%d members>, sort=%r)' % (cnt, sort) - - def test_Registrar(): """ Test the `ipalib.plugable.Registrar` class -- cgit