Removed depreciated code in ipalib.plugable that has been moving into ipalib.base

This commit is contained in:
Jason Gerard DeRose 2009-01-02 00:46:45 -07:00
parent ea7f9594df
commit b4dc333ee2
2 changed files with 2 additions and 440 deletions

View File

@ -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 "<stdin>", line 1, in <module>
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 "<stdin>", line 1, in <module>
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.

View File

@ -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