diff options
Diffstat (limited to 'ipalib/plugable.py')
-rw-r--r-- | ipalib/plugable.py | 251 |
1 files changed, 2 insertions, 249 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 "<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. |