2008-07-27 23:34:25 -05:00
|
|
|
# Authors:
|
|
|
|
# Jason Gerard DeRose <jderose@redhat.com>
|
|
|
|
#
|
|
|
|
# Copyright (C) 2008 Red Hat
|
|
|
|
# see file 'COPYING' for use and warranty information
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or
|
|
|
|
# modify it under the terms of the GNU General Public License as
|
|
|
|
# published by the Free Software Foundation; version 2 only
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
|
|
|
|
"""
|
2008-08-14 15:32:35 -05:00
|
|
|
Plugin framework.
|
2008-08-14 13:59:12 -05:00
|
|
|
|
|
|
|
The classes in this module make heavy use of Python container emulation. If
|
|
|
|
you are unfamiliar with this Python feature, see
|
|
|
|
http://docs.python.org/ref/sequence-types.html
|
2008-07-27 23:34:25 -05:00
|
|
|
"""
|
|
|
|
|
2008-08-05 01:33:09 -05:00
|
|
|
import re
|
2008-10-31 19:17:08 -05:00
|
|
|
import sys
|
2008-07-27 23:34:25 -05:00
|
|
|
import inspect
|
2008-10-30 02:11:33 -05:00
|
|
|
import threading
|
2008-10-31 14:27:42 -05:00
|
|
|
import logging
|
|
|
|
import os
|
|
|
|
from os import path
|
2008-07-31 13:57:10 -05:00
|
|
|
import errors
|
2008-09-02 12:29:01 -05:00
|
|
|
from errors import check_type, check_isinstance
|
2008-10-27 02:35:40 -05:00
|
|
|
from config import Environment, Env
|
2008-10-31 19:55:32 -05:00
|
|
|
from constants import DEFAULT_CONFIG
|
2008-10-27 01:23:43 -05:00
|
|
|
import util
|
2008-07-27 23:34:25 -05:00
|
|
|
|
|
|
|
|
2008-10-31 19:17:08 -05:00
|
|
|
|
2008-08-08 16:49:09 -05:00
|
|
|
class ReadOnly(object):
|
|
|
|
"""
|
|
|
|
Base class for classes with read-only attributes.
|
2008-08-08 23:35:06 -05:00
|
|
|
|
|
|
|
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
|
2008-08-08 23:37:37 -05:00
|
|
|
attributes, but to make it impossible to accidentally do so. The plugins
|
2008-08-08 23:35:06 -05:00
|
|
|
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:
|
|
|
|
|
2008-09-18 16:45:25 -05:00
|
|
|
>>> 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>
|
2008-10-18 02:02:31 -05:00
|
|
|
File ".../ipalib/plugable.py", line 93, in __setattr__
|
2008-09-18 16:45:25 -05:00
|
|
|
(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
|
2008-08-08 16:49:09 -05:00
|
|
|
"""
|
2008-09-18 16:45:25 -05:00
|
|
|
|
2008-08-08 16:49:09 -05:00
|
|
|
__locked = False
|
|
|
|
|
|
|
|
def __lock__(self):
|
2008-08-08 17:45:09 -05:00
|
|
|
"""
|
2008-09-18 17:01:04 -05:00
|
|
|
Put this instance into a read-only state.
|
|
|
|
|
|
|
|
After the instance has been locked, attempting to set or delete an
|
|
|
|
attribute will raise AttributeError.
|
2008-08-08 17:45:09 -05:00
|
|
|
"""
|
|
|
|
assert self.__locked is False, '__lock__() can only be called once'
|
2008-08-08 16:49:09 -05:00
|
|
|
self.__locked = True
|
|
|
|
|
2008-08-08 23:35:06 -05:00
|
|
|
def __islocked__(self):
|
|
|
|
"""
|
2008-09-18 17:39:48 -05:00
|
|
|
Return True if instance is locked, otherwise False.
|
2008-08-08 23:35:06 -05:00
|
|
|
"""
|
|
|
|
return self.__locked
|
|
|
|
|
2008-08-08 16:49:09 -05:00
|
|
|
def __setattr__(self, name, value):
|
|
|
|
"""
|
2008-09-18 17:01:04 -05:00
|
|
|
If unlocked, set attribute named ``name`` to ``value``.
|
|
|
|
|
|
|
|
If this instance is locked, AttributeError will be raised.
|
2008-08-08 16:49:09 -05:00
|
|
|
"""
|
|
|
|
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):
|
|
|
|
"""
|
2008-09-18 17:01:04 -05:00
|
|
|
If unlocked, delete attribute named ``name``.
|
|
|
|
|
|
|
|
If this instance is locked, AttributeError will be raised.
|
2008-08-08 16:49:09 -05:00
|
|
|
"""
|
|
|
|
if self.__locked:
|
|
|
|
raise AttributeError('read-only: cannot del %s.%s' %
|
|
|
|
(self.__class__.__name__, name)
|
|
|
|
)
|
|
|
|
return object.__delattr__(self, name)
|
|
|
|
|
|
|
|
|
2008-08-14 15:32:35 -05:00
|
|
|
def lock(readonly):
|
2008-08-14 13:50:21 -05:00
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Lock a `ReadOnly` instance.
|
2008-08-14 15:32:35 -05:00
|
|
|
|
|
|
|
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.
|
2008-08-14 13:50:21 -05:00
|
|
|
"""
|
2008-08-14 15:32:35 -05:00
|
|
|
if not isinstance(readonly, ReadOnly):
|
|
|
|
raise ValueError('not a ReadOnly instance: %r' % readonly)
|
|
|
|
readonly.__lock__()
|
|
|
|
assert readonly.__islocked__(), 'Ouch! The locking failed?'
|
|
|
|
return readonly
|
2008-08-14 13:50:21 -05:00
|
|
|
|
|
|
|
|
2008-08-14 20:04:19 -05:00
|
|
|
class SetProxy(ReadOnly):
|
2008-08-14 20:24:51 -05:00
|
|
|
"""
|
2008-08-14 22:24:37 -05:00
|
|
|
A read-only container with set/sequence behaviour.
|
2008-08-14 20:24:51 -05:00
|
|
|
|
2008-08-14 22:24:37 -05:00
|
|
|
This container acts as a proxy to an actual set-like object (a set,
|
|
|
|
frozenset, or dict) that is passed to the constructor. To the extent
|
|
|
|
possible in Python, this underlying set-like object cannot be modified
|
|
|
|
through the SetProxy... which just means you wont do it accidentally.
|
2008-08-14 20:24:51 -05:00
|
|
|
"""
|
2008-08-14 20:04:19 -05:00
|
|
|
def __init__(self, s):
|
2008-08-14 22:24:37 -05:00
|
|
|
"""
|
|
|
|
:param s: The target set-like object (a set, frozenset, or dict)
|
|
|
|
"""
|
2008-08-14 20:04:19 -05:00
|
|
|
allowed = (set, frozenset, dict)
|
|
|
|
if type(s) not in allowed:
|
|
|
|
raise TypeError('%r not in %r' % (type(s), allowed))
|
|
|
|
self.__s = s
|
|
|
|
lock(self)
|
|
|
|
|
|
|
|
def __len__(self):
|
2008-08-14 22:24:37 -05:00
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Return the number of items in this container.
|
2008-08-14 22:24:37 -05:00
|
|
|
"""
|
2008-08-14 20:04:19 -05:00
|
|
|
return len(self.__s)
|
|
|
|
|
|
|
|
def __iter__(self):
|
2008-08-14 22:24:37 -05:00
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Iterate (in ascending order) through keys.
|
2008-08-14 22:24:37 -05:00
|
|
|
"""
|
2008-08-14 20:04:19 -05:00
|
|
|
for key in sorted(self.__s):
|
|
|
|
yield key
|
|
|
|
|
|
|
|
def __contains__(self, key):
|
2008-08-14 22:24:37 -05:00
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Return True if this container contains ``key``.
|
2008-08-14 22:24:37 -05:00
|
|
|
|
2008-10-18 02:02:31 -05:00
|
|
|
:param key: The key to test for membership.
|
2008-08-14 22:24:37 -05:00
|
|
|
"""
|
2008-08-14 20:04:19 -05:00
|
|
|
return key in self.__s
|
|
|
|
|
|
|
|
|
|
|
|
class DictProxy(SetProxy):
|
2008-08-14 20:24:51 -05:00
|
|
|
"""
|
2008-08-14 22:24:37 -05:00
|
|
|
A read-only container with mapping behaviour.
|
|
|
|
|
|
|
|
This container acts as a proxy to an actual mapping object (a dict) that
|
|
|
|
is passed to the constructor. To the extent possible in Python, this
|
|
|
|
underlying mapping object cannot be modified through the DictProxy...
|
|
|
|
which just means you wont do it accidentally.
|
|
|
|
|
|
|
|
Also see `SetProxy`.
|
2008-08-14 20:24:51 -05:00
|
|
|
"""
|
2008-08-14 20:04:19 -05:00
|
|
|
def __init__(self, d):
|
2008-08-14 22:24:37 -05:00
|
|
|
"""
|
|
|
|
:param d: The target mapping object (a dict)
|
|
|
|
"""
|
2008-08-14 20:04:19 -05:00
|
|
|
if type(d) is not dict:
|
|
|
|
raise TypeError('%r is not %r' % (type(d), dict))
|
|
|
|
self.__d = d
|
|
|
|
super(DictProxy, self).__init__(d)
|
|
|
|
|
|
|
|
def __getitem__(self, key):
|
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Return the value corresponding to ``key``.
|
2008-08-14 22:24:37 -05:00
|
|
|
|
|
|
|
:param key: The key of the value you wish to retrieve.
|
2008-08-14 20:04:19 -05:00
|
|
|
"""
|
|
|
|
return self.__d[key]
|
|
|
|
|
2008-08-15 00:19:02 -05:00
|
|
|
def __call__(self):
|
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Iterate (in ascending order by key) through values.
|
2008-08-15 00:19:02 -05:00
|
|
|
"""
|
|
|
|
for key in self:
|
|
|
|
yield self.__d[key]
|
|
|
|
|
2008-08-14 20:04:19 -05:00
|
|
|
|
2008-08-14 20:24:51 -05:00
|
|
|
class MagicDict(DictProxy):
|
|
|
|
"""
|
2008-10-17 21:50:34 -05:00
|
|
|
A mapping container whose values can be accessed as attributes.
|
2008-08-14 22:24:37 -05:00
|
|
|
|
2008-10-17 21:50:34 -05:00
|
|
|
For example:
|
2008-08-14 20:24:51 -05:00
|
|
|
|
2008-10-17 21:50:34 -05:00
|
|
|
>>> magic = MagicDict({'the_key': 'the value'})
|
|
|
|
>>> magic['the_key']
|
|
|
|
'the value'
|
|
|
|
>>> magic.the_key
|
|
|
|
'the value'
|
2008-08-14 20:24:51 -05:00
|
|
|
|
2008-08-14 22:24:37 -05:00
|
|
|
This container acts as a proxy to an actual mapping object (a dict) that
|
|
|
|
is passed to the constructor. To the extent possible in Python, this
|
|
|
|
underlying mapping object cannot be modified through the MagicDict...
|
|
|
|
which just means you wont do it accidentally.
|
|
|
|
|
|
|
|
Also see `DictProxy` and `SetProxy`.
|
2008-08-14 20:24:51 -05:00
|
|
|
"""
|
|
|
|
|
|
|
|
def __getattr__(self, name):
|
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Return the value corresponding to ``name``.
|
2008-08-14 22:24:37 -05:00
|
|
|
|
|
|
|
:param name: The name of the attribute you wish to retrieve.
|
2008-08-14 20:24:51 -05:00
|
|
|
"""
|
|
|
|
try:
|
|
|
|
return self[name]
|
|
|
|
except KeyError:
|
2008-08-14 22:24:37 -05:00
|
|
|
raise AttributeError('no magic attribute %r' % name)
|
2008-08-14 20:24:51 -05:00
|
|
|
|
|
|
|
|
2008-08-14 02:10:07 -05:00
|
|
|
class Plugin(ReadOnly):
|
|
|
|
"""
|
|
|
|
Base class for all plugins.
|
|
|
|
"""
|
|
|
|
__public__ = frozenset()
|
2008-09-23 19:44:41 -05:00
|
|
|
__proxy__ = True
|
2008-08-14 02:10:07 -05:00
|
|
|
__api = None
|
|
|
|
|
|
|
|
def __get_name(self):
|
|
|
|
"""
|
|
|
|
Convenience property to return the class name.
|
|
|
|
"""
|
|
|
|
return self.__class__.__name__
|
|
|
|
name = property(__get_name)
|
|
|
|
|
|
|
|
def __get_doc(self):
|
|
|
|
"""
|
|
|
|
Convenience property to return the class docstring.
|
|
|
|
"""
|
|
|
|
return self.__class__.__doc__
|
|
|
|
doc = property(__get_doc)
|
|
|
|
|
|
|
|
def __get_api(self):
|
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Return `API` instance passed to `finalize()`.
|
|
|
|
|
|
|
|
If `finalize()` has not yet been called, None is returned.
|
2008-08-14 02:10:07 -05:00
|
|
|
"""
|
|
|
|
return self.__api
|
|
|
|
api = property(__get_api)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def implements(cls, arg):
|
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Return True if this class implements ``arg``.
|
2008-08-14 02:10:07 -05:00
|
|
|
|
2008-10-17 21:50:34 -05:00
|
|
|
There are three different ways this method can be called:
|
2008-08-14 02:10:07 -05:00
|
|
|
|
2008-08-14 04:01:02 -05:00
|
|
|
With a <type 'str'> argument, e.g.:
|
2008-08-14 02:10:07 -05:00
|
|
|
|
2008-10-17 21:50:34 -05:00
|
|
|
>>> class base(Plugin):
|
|
|
|
... __public__ = frozenset(['attr1', 'attr2'])
|
|
|
|
...
|
|
|
|
>>> base.implements('attr1')
|
|
|
|
True
|
|
|
|
>>> base.implements('attr2')
|
2008-08-14 02:10:07 -05:00
|
|
|
True
|
2008-10-17 21:50:34 -05:00
|
|
|
>>> base.implements('attr3')
|
2008-08-14 02:10:07 -05:00
|
|
|
False
|
|
|
|
|
2008-08-14 04:01:02 -05:00
|
|
|
With a <type 'frozenset'> argument, e.g.:
|
2008-08-14 02:10:07 -05:00
|
|
|
|
2008-08-14 04:01:02 -05:00
|
|
|
With any object that has a `__public__` attribute that is
|
2008-08-14 02:10:07 -05:00
|
|
|
<type 'frozenset'>, e.g.:
|
|
|
|
|
|
|
|
Unlike ProxyTarget.implemented_by(), this returns an abstract answer
|
|
|
|
because only the __public__ frozenset is checked... a ProxyTarget
|
|
|
|
need not itself have attributes for all names in __public__
|
|
|
|
(subclasses might provide them).
|
|
|
|
"""
|
|
|
|
assert type(cls.__public__) is frozenset
|
|
|
|
if isinstance(arg, str):
|
|
|
|
return arg in cls.__public__
|
|
|
|
if type(getattr(arg, '__public__', None)) is frozenset:
|
|
|
|
return cls.__public__.issuperset(arg.__public__)
|
|
|
|
if type(arg) is frozenset:
|
|
|
|
return cls.__public__.issuperset(arg)
|
|
|
|
raise TypeError(
|
|
|
|
"must be str, frozenset, or have frozenset '__public__' attribute"
|
|
|
|
)
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def implemented_by(cls, arg):
|
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Return True if ``arg`` implements public interface of this class.
|
|
|
|
|
|
|
|
This classmethod returns True if:
|
2008-08-14 02:10:07 -05:00
|
|
|
|
2008-10-18 02:02:31 -05:00
|
|
|
1. ``arg`` is an instance of or subclass of this class, and
|
2008-08-14 03:28:48 -05:00
|
|
|
|
2008-10-18 02:02:31 -05:00
|
|
|
2. ``arg`` (or ``arg.__class__`` if instance) has an attribute for
|
|
|
|
each name in this class's ``__public__`` frozenset.
|
2008-08-14 03:28:48 -05:00
|
|
|
|
|
|
|
Otherwise, returns False.
|
|
|
|
|
|
|
|
Unlike `Plugin.implements`, this returns a concrete answer because
|
|
|
|
the attributes of the subclass are checked.
|
|
|
|
|
|
|
|
:param arg: An instance of or subclass of this class.
|
2008-08-14 02:10:07 -05:00
|
|
|
"""
|
|
|
|
if inspect.isclass(arg):
|
|
|
|
subclass = arg
|
|
|
|
else:
|
|
|
|
subclass = arg.__class__
|
|
|
|
assert issubclass(subclass, cls), 'must be subclass of %r' % cls
|
|
|
|
for name in cls.__public__:
|
|
|
|
if not hasattr(subclass, name):
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
2008-09-21 16:50:56 -05:00
|
|
|
def finalize(self):
|
2008-08-14 02:10:07 -05:00
|
|
|
"""
|
|
|
|
"""
|
2008-09-21 16:50:56 -05:00
|
|
|
lock(self)
|
2008-08-14 02:10:07 -05:00
|
|
|
|
2008-09-21 16:30:19 -05:00
|
|
|
def set_api(self, api):
|
|
|
|
"""
|
|
|
|
Set reference to `API` instance.
|
|
|
|
"""
|
|
|
|
assert self.__api is None, 'set_api() can only be called once'
|
|
|
|
assert api is not None, 'set_api() argument cannot be None'
|
|
|
|
self.__api = api
|
2008-10-30 15:11:24 -05:00
|
|
|
if not isinstance(api, API):
|
|
|
|
return
|
|
|
|
for name in api:
|
|
|
|
assert not hasattr(self, name)
|
|
|
|
setattr(self, name, api[name])
|
|
|
|
for name in ('env', 'context', 'log'):
|
|
|
|
if hasattr(api, name):
|
|
|
|
assert not hasattr(self, name)
|
|
|
|
setattr(self, name, getattr(api, name))
|
2008-09-21 16:30:19 -05:00
|
|
|
|
2008-08-14 02:10:07 -05:00
|
|
|
def __repr__(self):
|
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Return 'module_name.class_name()' representation.
|
|
|
|
|
|
|
|
This representation could be used to instantiate this Plugin
|
|
|
|
instance given the appropriate environment.
|
2008-08-14 02:10:07 -05:00
|
|
|
"""
|
2008-09-08 16:51:05 -05:00
|
|
|
return '%s.%s()' % (
|
2008-08-14 02:10:07 -05:00
|
|
|
self.__class__.__module__,
|
|
|
|
self.__class__.__name__
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2008-08-14 22:41:17 -05:00
|
|
|
class PluginProxy(SetProxy):
|
2008-08-08 18:07:22 -05:00
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Allow access to only certain attributes on a `Plugin`.
|
2008-08-08 18:07:22 -05:00
|
|
|
|
2008-08-08 20:06:42 -05:00
|
|
|
Think of a proxy as an agreement that "I will have at most these
|
2008-08-08 18:07:22 -05:00
|
|
|
attributes". This is different from (although similar to) an interface,
|
|
|
|
which can be thought of as an agreement that "I will have at least these
|
|
|
|
attributes".
|
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
|
2008-08-08 12:11:29 -05:00
|
|
|
__slots__ = (
|
2008-08-08 16:40:03 -05:00
|
|
|
'__base',
|
|
|
|
'__target',
|
|
|
|
'__name_attr',
|
|
|
|
'__public__',
|
2008-08-09 00:19:40 -05:00
|
|
|
'name',
|
2008-08-12 21:34:36 -05:00
|
|
|
'doc',
|
2008-08-08 12:11:29 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
def __init__(self, base, target, name_attr='name'):
|
2008-08-08 18:07:22 -05:00
|
|
|
"""
|
2008-08-14 01:53:05 -05:00
|
|
|
:param base: A subclass of `Plugin`.
|
|
|
|
:param target: An instance ``base`` or a subclass of ``base``.
|
|
|
|
:param name_attr: The name of the attribute on ``target`` from which
|
|
|
|
to derive ``self.name``.
|
2008-08-08 18:07:22 -05:00
|
|
|
"""
|
2008-08-08 16:40:03 -05:00
|
|
|
if not inspect.isclass(base):
|
2008-08-14 01:53:05 -05:00
|
|
|
raise TypeError(
|
|
|
|
'`base` must be a class, got %r' % base
|
|
|
|
)
|
2008-08-08 16:40:03 -05:00
|
|
|
if not isinstance(target, base):
|
2008-08-14 01:53:05 -05:00
|
|
|
raise ValueError(
|
|
|
|
'`target` must be an instance of `base`, got %r' % target
|
|
|
|
)
|
2008-08-08 16:28:56 -05:00
|
|
|
self.__base = base
|
2008-08-08 16:40:03 -05:00
|
|
|
self.__target = target
|
|
|
|
self.__name_attr = name_attr
|
|
|
|
self.__public__ = base.__public__
|
2008-08-09 00:19:40 -05:00
|
|
|
self.name = getattr(target, name_attr)
|
2008-08-12 21:34:36 -05:00
|
|
|
self.doc = target.doc
|
|
|
|
assert type(self.__public__) is frozenset
|
2008-08-14 22:41:17 -05:00
|
|
|
super(PluginProxy, self).__init__(self.__public__)
|
2008-08-08 16:28:56 -05:00
|
|
|
|
2008-08-08 20:06:42 -05:00
|
|
|
def implements(self, arg):
|
2008-08-09 13:58:46 -05:00
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Return True if plugin being proxied implements ``arg``.
|
|
|
|
|
|
|
|
This method simply calls the corresponding `Plugin.implements`
|
|
|
|
classmethod.
|
2008-08-09 13:58:46 -05:00
|
|
|
|
2008-10-18 02:02:31 -05:00
|
|
|
Unlike `Plugin.implements`, this is not a classmethod as a
|
|
|
|
`PluginProxy` can only implement anything as an instance.
|
2008-08-09 13:58:46 -05:00
|
|
|
"""
|
2008-08-08 20:06:42 -05:00
|
|
|
return self.__base.implements(arg)
|
|
|
|
|
2008-08-08 20:46:12 -05:00
|
|
|
def __clone__(self, name_attr):
|
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Return a `PluginProxy` instance similar to this one.
|
|
|
|
|
|
|
|
The new `PluginProxy` returned will be identical to this one except
|
|
|
|
the proxy name might be derived from a different attribute on the
|
|
|
|
target `Plugin`. The same base and target will be used.
|
2008-08-08 20:46:12 -05:00
|
|
|
"""
|
|
|
|
return self.__class__(self.__base, self.__target, name_attr)
|
|
|
|
|
2008-08-08 12:11:29 -05:00
|
|
|
def __getitem__(self, key):
|
2008-08-08 18:07:22 -05:00
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Return attribute named ``key`` on target `Plugin`.
|
|
|
|
|
|
|
|
If this proxy allows access to an attribute named ``key``, that
|
|
|
|
attribute will be returned. If access is not allowed,
|
|
|
|
KeyError will be raised.
|
2008-08-08 18:07:22 -05:00
|
|
|
"""
|
2008-08-08 16:40:03 -05:00
|
|
|
if key in self.__public__:
|
|
|
|
return getattr(self.__target, key)
|
2008-09-24 21:13:16 -05:00
|
|
|
raise KeyError('no public attribute %s.%s' % (self.name, key))
|
2008-08-08 12:11:29 -05:00
|
|
|
|
|
|
|
def __getattr__(self, name):
|
2008-08-08 18:07:22 -05:00
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Return attribute named ``name`` on target `Plugin`.
|
|
|
|
|
|
|
|
If this proxy allows access to an attribute named ``name``, that
|
|
|
|
attribute will be returned. If access is not allowed,
|
|
|
|
AttributeError will be raised.
|
2008-08-08 18:07:22 -05:00
|
|
|
"""
|
2008-08-08 16:40:03 -05:00
|
|
|
if name in self.__public__:
|
|
|
|
return getattr(self.__target, name)
|
2008-09-24 21:13:16 -05:00
|
|
|
raise AttributeError('no public attribute %s.%s' % (self.name, name))
|
2008-08-08 12:11:29 -05:00
|
|
|
|
|
|
|
def __call__(self, *args, **kw):
|
2008-08-09 13:58:46 -05:00
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Call target `Plugin` and return its return value.
|
|
|
|
|
|
|
|
If `__call__` is not an attribute this proxy allows access to,
|
|
|
|
KeyError is raised.
|
2008-08-09 13:58:46 -05:00
|
|
|
"""
|
2008-08-08 16:40:03 -05:00
|
|
|
return self['__call__'](*args, **kw)
|
2008-08-08 12:11:29 -05:00
|
|
|
|
|
|
|
def __repr__(self):
|
2008-08-09 13:58:46 -05:00
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Return a Python expression that could create this instance.
|
2008-08-09 13:58:46 -05:00
|
|
|
"""
|
2008-09-08 16:37:02 -05:00
|
|
|
return '%s(%s, %r)' % (
|
2008-08-08 16:40:03 -05:00
|
|
|
self.__class__.__name__,
|
|
|
|
self.__base.__name__,
|
|
|
|
self.__target,
|
|
|
|
)
|
2008-08-06 19:14:38 -05:00
|
|
|
|
2008-08-06 15:38:07 -05:00
|
|
|
|
2008-08-14 00:46:20 -05:00
|
|
|
def check_name(name):
|
2008-08-08 12:11:29 -05:00
|
|
|
"""
|
2008-10-18 02:02:31 -05:00
|
|
|
Verify that ``name`` is suitable for a `NameSpace` member name.
|
2008-08-14 16:40:37 -05:00
|
|
|
|
|
|
|
Raises `errors.NameSpaceError` if ``name`` is not a valid Python
|
|
|
|
identifier suitable for use as the name of `NameSpace` member.
|
2008-08-14 02:43:43 -05:00
|
|
|
|
|
|
|
:param name: Identifier to test.
|
2008-08-14 00:46:20 -05:00
|
|
|
"""
|
2008-09-02 12:29:01 -05:00
|
|
|
check_type(name, str, 'name')
|
2008-08-14 00:46:20 -05:00
|
|
|
regex = r'^[a-z][_a-z0-9]*[a-z0-9]$'
|
|
|
|
if re.match(regex, name) is None:
|
|
|
|
raise errors.NameSpaceError(name, regex)
|
|
|
|
return name
|
2008-08-09 14:28:01 -05:00
|
|
|
|
2008-08-14 00:46:20 -05:00
|
|
|
|
2008-09-18 15:35:23 -05:00
|
|
|
class NameSpace(ReadOnly):
|
2008-08-14 00:46:20 -05:00
|
|
|
"""
|
|
|
|
A read-only namespace with handy container behaviours.
|
|
|
|
|
2008-08-14 02:43:43 -05:00
|
|
|
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.
|
2008-08-14 00:46:20 -05:00
|
|
|
|
|
|
|
The members can be accessed as attributes on the NameSpace instance or
|
2008-10-17 21:50:34 -05:00
|
|
|
through a dictionary interface. For example:
|
2008-08-14 00:46:20 -05:00
|
|
|
|
2008-10-17 21:50:34 -05:00
|
|
|
>>> class obj(object):
|
|
|
|
... name = 'my_obj'
|
|
|
|
...
|
|
|
|
>>> namespace = NameSpace([obj])
|
|
|
|
>>> obj is getattr(namespace, 'my_obj') # As attribute
|
2008-08-14 00:46:20 -05:00
|
|
|
True
|
2008-10-17 21:50:34 -05:00
|
|
|
>>> obj is namespace['my_obj'] # As dictionary item
|
2008-08-14 00:46:20 -05:00
|
|
|
True
|
|
|
|
|
|
|
|
Here is a more detailed example:
|
|
|
|
|
2008-10-21 09:47:08 -05:00
|
|
|
>>> class Member(object):
|
2008-08-14 00:46:20 -05:00
|
|
|
... def __init__(self, i):
|
2008-10-17 21:50:34 -05:00
|
|
|
... self.i = i
|
2008-08-14 00:46:20 -05:00
|
|
|
... self.name = 'member_%d' % i
|
2008-10-17 21:50:34 -05:00
|
|
|
... def __repr__(self):
|
2008-10-21 09:47:08 -05:00
|
|
|
... return 'Member(%d)' % self.i
|
2008-08-14 00:46:20 -05:00
|
|
|
...
|
2008-10-21 09:47:08 -05:00
|
|
|
>>> namespace = NameSpace(Member(i) for i in xrange(3))
|
2008-08-14 00:46:20 -05:00
|
|
|
>>> namespace.member_0 is namespace['member_0']
|
|
|
|
True
|
|
|
|
>>> len(namespace) # Returns the number of members in namespace
|
2008-10-17 21:50:34 -05:00
|
|
|
3
|
2008-08-14 00:46:20 -05:00
|
|
|
>>> list(namespace) # As iterable, iterates through the member names
|
2008-10-17 21:50:34 -05:00
|
|
|
['member_0', 'member_1', 'member_2']
|
2008-08-14 00:46:20 -05:00
|
|
|
>>> list(namespace()) # Calling a NameSpace iterates through the members
|
2008-10-21 09:47:08 -05:00
|
|
|
[Member(0), Member(1), Member(2)]
|
2008-10-17 21:50:34 -05:00
|
|
|
>>> 'member_1' in namespace # Does namespace contain 'member_1'?
|
2008-08-14 00:46:20 -05:00
|
|
|
True
|
2008-08-08 12:11:29 -05:00
|
|
|
"""
|
|
|
|
|
2008-09-09 18:10:49 -05:00
|
|
|
def __init__(self, members, sort=True):
|
2008-08-08 16:40:03 -05:00
|
|
|
"""
|
2008-08-14 02:43:43 -05:00
|
|
|
:param members: An iterable providing the members.
|
2008-09-09 18:10:49 -05:00
|
|
|
:param sort: Whether to sort the members by member name.
|
2008-08-08 16:40:03 -05:00
|
|
|
"""
|
2008-09-09 18:10:49 -05:00
|
|
|
self.__sort = check_type(sort, bool, 'sort')
|
|
|
|
if self.__sort:
|
2008-09-18 15:35:23 -05:00
|
|
|
self.__members = tuple(sorted(members, key=lambda m: m.name))
|
2008-09-09 18:10:49 -05:00
|
|
|
else:
|
2008-09-18 15:35:23 -05:00
|
|
|
self.__members = tuple(members)
|
|
|
|
self.__names = tuple(m.name for m in self.__members)
|
|
|
|
self.__map = dict()
|
2008-09-09 18:10:49 -05:00
|
|
|
for member in self.__members:
|
2008-08-14 00:46:20 -05:00
|
|
|
name = check_name(member.name)
|
2008-09-18 15:35:23 -05:00
|
|
|
assert name not in self.__map, 'already has key %r' % name
|
|
|
|
self.__map[name] = member
|
2008-08-14 20:46:11 -05:00
|
|
|
assert not hasattr(self, name), 'already has attribute %r' % name
|
2008-08-14 00:46:20 -05:00
|
|
|
setattr(self, name, member)
|
2008-09-18 15:35:23 -05:00
|
|
|
lock(self)
|
|
|
|
|
|
|
|
def __len__(self):
|
|
|
|
"""
|
2008-09-24 18:49:44 -05:00
|
|
|
Return the number of members.
|
2008-09-18 15:35:23 -05:00
|
|
|
"""
|
|
|
|
return len(self.__members)
|
2008-08-14 00:46:20 -05:00
|
|
|
|
2008-09-09 18:10:49 -05:00
|
|
|
def __iter__(self):
|
|
|
|
"""
|
2008-09-24 18:49:44 -05:00
|
|
|
Iterate through the member names.
|
2008-09-09 18:10:49 -05:00
|
|
|
|
2008-09-18 15:35:23 -05:00
|
|
|
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.
|
2008-09-18 16:23:05 -05:00
|
|
|
|
|
|
|
This method is like an ordered version of dict.iterkeys().
|
2008-09-09 18:10:49 -05:00
|
|
|
"""
|
|
|
|
for name in self.__names:
|
|
|
|
yield name
|
|
|
|
|
2008-09-18 15:35:23 -05:00
|
|
|
def __call__(self):
|
|
|
|
"""
|
2008-09-24 18:49:44 -05:00
|
|
|
Iterate through the members.
|
2008-09-18 15:35:23 -05:00
|
|
|
|
|
|
|
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.
|
2008-09-18 16:23:05 -05:00
|
|
|
|
|
|
|
This method is like an ordered version of dict.itervalues().
|
2008-09-18 15:35:23 -05:00
|
|
|
"""
|
|
|
|
for member in self.__members:
|
|
|
|
yield member
|
|
|
|
|
|
|
|
def __contains__(self, name):
|
|
|
|
"""
|
2008-09-24 18:49:44 -05:00
|
|
|
Return True if namespace has a member named ``name``.
|
2008-09-18 15:35:23 -05:00
|
|
|
"""
|
|
|
|
return name in self.__map
|
|
|
|
|
|
|
|
def __getitem__(self, spec):
|
|
|
|
"""
|
2008-09-24 18:49:44 -05:00
|
|
|
Return a member by name or index, or returns a slice of members.
|
2008-09-18 15:35:23 -05:00
|
|
|
|
|
|
|
: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)
|
|
|
|
)
|
|
|
|
|
2008-08-08 12:11:29 -05:00
|
|
|
def __repr__(self):
|
2008-08-14 00:46:20 -05:00
|
|
|
"""
|
2008-09-24 18:49:44 -05:00
|
|
|
Return a pseudo-valid expression that could create this instance.
|
2008-08-14 00:46:20 -05:00
|
|
|
"""
|
2008-09-09 18:10:49 -05:00
|
|
|
return '%s(<%d members>, sort=%r)' % (
|
|
|
|
self.__class__.__name__,
|
|
|
|
len(self),
|
|
|
|
self.__sort,
|
|
|
|
)
|
2008-08-06 17:59:50 -05:00
|
|
|
|
2008-09-24 18:49:44 -05:00
|
|
|
def __todict__(self):
|
|
|
|
"""
|
|
|
|
Return a copy of the private dict mapping name to member.
|
|
|
|
"""
|
|
|
|
return dict(self.__map)
|
|
|
|
|
2008-08-06 17:59:50 -05:00
|
|
|
|
2008-08-15 00:07:17 -05:00
|
|
|
class Registrar(DictProxy):
|
2008-08-14 13:50:21 -05:00
|
|
|
"""
|
|
|
|
Collects plugin classes as they are registered.
|
|
|
|
|
|
|
|
The Registrar does not instantiate plugins... it only implements the
|
|
|
|
override logic and stores the plugins in a namespace per allowed base
|
|
|
|
class.
|
|
|
|
|
|
|
|
The plugins are instantiated when `API.finalize()` is called.
|
|
|
|
"""
|
2008-08-08 12:11:29 -05:00
|
|
|
def __init__(self, *allowed):
|
2008-08-08 16:40:03 -05:00
|
|
|
"""
|
2008-08-14 04:01:02 -05:00
|
|
|
:param allowed: Base classes from which plugins accepted by this
|
2008-08-14 03:28:48 -05:00
|
|
|
Registrar must subclass.
|
2008-08-08 16:40:03 -05:00
|
|
|
"""
|
2008-08-15 00:07:17 -05:00
|
|
|
self.__allowed = dict((base, {}) for base in allowed)
|
2008-08-08 16:40:03 -05:00
|
|
|
self.__registered = set()
|
2008-08-15 00:07:17 -05:00
|
|
|
super(Registrar, self).__init__(
|
|
|
|
dict(self.__base_iter())
|
|
|
|
)
|
|
|
|
|
|
|
|
def __base_iter(self):
|
|
|
|
for (base, sub_d) in self.__allowed.iteritems():
|
|
|
|
assert inspect.isclass(base)
|
|
|
|
name = base.__name__
|
|
|
|
assert not hasattr(self, name)
|
|
|
|
setattr(self, name, MagicDict(sub_d))
|
|
|
|
yield (name, base)
|
2008-08-08 12:11:29 -05:00
|
|
|
|
2008-08-14 04:38:28 -05:00
|
|
|
def __findbases(self, klass):
|
2008-08-08 16:40:03 -05:00
|
|
|
"""
|
2008-08-14 04:38:28 -05:00
|
|
|
Iterates through allowed bases that ``klass`` is a subclass of.
|
|
|
|
|
|
|
|
Raises `errors.SubclassError` if ``klass`` is not a subclass of any
|
|
|
|
allowed base.
|
|
|
|
|
|
|
|
:param klass: The class to find bases for.
|
2008-08-08 16:40:03 -05:00
|
|
|
"""
|
2008-08-14 04:38:28 -05:00
|
|
|
assert inspect.isclass(klass)
|
2008-08-08 16:40:03 -05:00
|
|
|
found = False
|
2008-08-15 00:07:17 -05:00
|
|
|
for (base, sub_d) in self.__allowed.iteritems():
|
2008-08-14 04:38:28 -05:00
|
|
|
if issubclass(klass, base):
|
2008-08-08 16:40:03 -05:00
|
|
|
found = True
|
2008-08-15 00:07:17 -05:00
|
|
|
yield (base, sub_d)
|
2008-08-08 16:40:03 -05:00
|
|
|
if not found:
|
2008-08-15 00:07:17 -05:00
|
|
|
raise errors.SubclassError(klass, self.__allowed.keys())
|
2008-08-08 12:11:29 -05:00
|
|
|
|
2008-08-14 04:38:28 -05:00
|
|
|
def __call__(self, klass, override=False):
|
2008-08-08 16:40:03 -05:00
|
|
|
"""
|
2008-08-14 04:38:28 -05:00
|
|
|
Register the plugin ``klass``.
|
|
|
|
|
|
|
|
:param klass: A subclass of `Plugin` to attempt to register.
|
|
|
|
:param override: If true, override an already registered plugin.
|
2008-08-08 16:40:03 -05:00
|
|
|
"""
|
2008-08-14 04:38:28 -05:00
|
|
|
if not inspect.isclass(klass):
|
|
|
|
raise TypeError('plugin must be a class: %r' % klass)
|
2008-08-08 16:40:03 -05:00
|
|
|
|
|
|
|
# Raise DuplicateError if this exact class was already registered:
|
2008-08-14 04:38:28 -05:00
|
|
|
if klass in self.__registered:
|
|
|
|
raise errors.DuplicateError(klass)
|
2008-08-08 16:40:03 -05:00
|
|
|
|
|
|
|
# Find the base class or raise SubclassError:
|
2008-08-15 00:07:17 -05:00
|
|
|
for (base, sub_d) in self.__findbases(klass):
|
2008-08-08 16:40:03 -05:00
|
|
|
# Check override:
|
2008-08-14 04:38:28 -05:00
|
|
|
if klass.__name__ in sub_d:
|
2008-08-08 16:40:03 -05:00
|
|
|
if not override:
|
2008-08-15 00:07:17 -05:00
|
|
|
# Must use override=True to override:
|
2008-08-14 04:38:28 -05:00
|
|
|
raise errors.OverrideError(base, klass)
|
2008-08-08 16:40:03 -05:00
|
|
|
else:
|
|
|
|
if override:
|
2008-08-15 00:07:17 -05:00
|
|
|
# There was nothing already registered to override:
|
2008-08-14 04:38:28 -05:00
|
|
|
raise errors.MissingOverrideError(base, klass)
|
2008-08-08 16:40:03 -05:00
|
|
|
|
|
|
|
# The plugin is okay, add to sub_d:
|
2008-08-14 04:38:28 -05:00
|
|
|
sub_d[klass.__name__] = klass
|
2008-08-08 16:40:03 -05:00
|
|
|
|
|
|
|
# The plugin is okay, add to __registered:
|
2008-08-14 04:38:28 -05:00
|
|
|
self.__registered.add(klass)
|
2008-08-08 12:11:29 -05:00
|
|
|
|
2008-08-01 16:25:46 -05:00
|
|
|
|
2008-10-30 02:11:33 -05:00
|
|
|
class LazyContext(object):
|
|
|
|
"""
|
|
|
|
On-demand creation of thread-local context attributes.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, api):
|
|
|
|
self.__api = api
|
|
|
|
self.__context = threading.local()
|
|
|
|
|
|
|
|
def __getattr__(self, name):
|
|
|
|
if name not in self.__context.__dict__:
|
|
|
|
if name not in self.__api.Context:
|
|
|
|
raise AttributeError('no Context plugin for %r' % name)
|
|
|
|
value = self.__api.Context[name].get_value()
|
|
|
|
self.__context.__dict__[name] = value
|
|
|
|
return self.__context.__dict__[name]
|
|
|
|
|
2008-10-30 03:20:28 -05:00
|
|
|
def __getitem__(self, key):
|
|
|
|
return self.__getattr__(key)
|
|
|
|
|
2008-10-30 02:11:33 -05:00
|
|
|
|
|
|
|
|
2008-08-14 20:32:20 -05:00
|
|
|
class API(DictProxy):
|
2008-08-14 16:40:37 -05:00
|
|
|
"""
|
|
|
|
Dynamic API object through which `Plugin` instances are accessed.
|
|
|
|
"""
|
2008-08-12 18:40:36 -05:00
|
|
|
|
2008-10-02 13:24:05 -05:00
|
|
|
def __init__(self, *allowed):
|
2008-08-14 17:13:42 -05:00
|
|
|
self.__d = dict()
|
2008-10-27 00:28:06 -05:00
|
|
|
self.__done = set()
|
2008-08-08 16:28:56 -05:00
|
|
|
self.register = Registrar(*allowed)
|
2008-10-27 16:19:49 -05:00
|
|
|
self.env = Env()
|
2008-10-30 02:11:33 -05:00
|
|
|
self.context = LazyContext(self)
|
2008-08-14 20:32:20 -05:00
|
|
|
super(API, self).__init__(self.__d)
|
2008-08-08 12:11:29 -05:00
|
|
|
|
2008-10-27 00:28:06 -05:00
|
|
|
def __doing(self, name):
|
|
|
|
if name in self.__done:
|
|
|
|
raise StandardError(
|
|
|
|
'%s.%s() already called' % (self.__class__.__name__, name)
|
|
|
|
)
|
|
|
|
self.__done.add(name)
|
|
|
|
|
|
|
|
def __do_if_not_done(self, name):
|
|
|
|
if name not in self.__done:
|
|
|
|
getattr(self, name)()
|
|
|
|
|
|
|
|
def isdone(self, name):
|
|
|
|
return name in self.__done
|
|
|
|
|
|
|
|
def bootstrap(self, **overrides):
|
|
|
|
"""
|
2008-10-31 14:27:42 -05:00
|
|
|
Initialize environment variables and logging.
|
2008-10-27 00:28:06 -05:00
|
|
|
"""
|
|
|
|
self.__doing('bootstrap')
|
2008-10-27 16:19:49 -05:00
|
|
|
self.env._bootstrap(**overrides)
|
2008-10-31 14:27:42 -05:00
|
|
|
self.env._finalize_core(**dict(DEFAULT_CONFIG))
|
|
|
|
log = logging.getLogger('ipa')
|
|
|
|
object.__setattr__(self, 'log', log)
|
|
|
|
if self.env.debug:
|
|
|
|
log.setLevel(logging.DEBUG)
|
|
|
|
else:
|
|
|
|
log.setLevel(logging.INFO)
|
|
|
|
|
|
|
|
# Add stderr handler:
|
|
|
|
stderr = logging.StreamHandler()
|
2008-10-31 19:55:32 -05:00
|
|
|
format = self.env.log_format_stderr
|
2008-10-31 14:27:42 -05:00
|
|
|
if self.env.debug:
|
2008-10-31 19:55:32 -05:00
|
|
|
format = self.env.log_format_stderr_debug
|
|
|
|
stderr.setLevel(logging.DEBUG)
|
2008-10-31 14:27:42 -05:00
|
|
|
elif self.env.verbose:
|
2008-10-31 19:55:32 -05:00
|
|
|
stderr.setLevel(logging.INFO)
|
2008-10-31 14:27:42 -05:00
|
|
|
else:
|
2008-10-31 19:55:32 -05:00
|
|
|
stderr.setLevel(logging.WARNING)
|
2008-10-31 21:25:33 -05:00
|
|
|
stderr.setFormatter(util.LogFormatter(format))
|
2008-10-31 14:27:42 -05:00
|
|
|
log.addHandler(stderr)
|
|
|
|
|
|
|
|
# Add file handler:
|
2008-10-28 02:39:02 -05:00
|
|
|
if self.env.mode == 'unit_test':
|
2008-10-31 14:27:42 -05:00
|
|
|
return # But not if in unit-test mode
|
|
|
|
log_dir = path.dirname(self.env.log)
|
|
|
|
if not path.isdir(log_dir):
|
|
|
|
try:
|
|
|
|
os.makedirs(log_dir)
|
|
|
|
except OSError:
|
|
|
|
log.warn('Could not create log_dir %r', log_dir)
|
|
|
|
return
|
|
|
|
handler = logging.FileHandler(self.env.log)
|
2008-10-31 21:25:33 -05:00
|
|
|
handler.setFormatter(util.LogFormatter(self.env.log_format_file))
|
2008-10-31 14:27:42 -05:00
|
|
|
if self.env.debug:
|
2008-10-31 19:55:32 -05:00
|
|
|
handler.setLevel(logging.DEBUG)
|
2008-10-31 14:27:42 -05:00
|
|
|
else:
|
2008-10-31 19:55:32 -05:00
|
|
|
handler.setLevel(logging.INFO)
|
2008-10-31 14:27:42 -05:00
|
|
|
log.addHandler(handler)
|
|
|
|
|
2008-10-31 20:03:07 -05:00
|
|
|
def bootstrap_with_global_options(self, options=None, context=None):
|
2008-10-31 19:17:08 -05:00
|
|
|
if options is None:
|
|
|
|
parser = util.add_global_options()
|
|
|
|
(options, args) = parser.parse_args(
|
|
|
|
list(s.decode('utf-8') for s in sys.argv[1:])
|
|
|
|
)
|
|
|
|
overrides = {}
|
|
|
|
if options.env is not None:
|
|
|
|
assert type(options.env) is list
|
|
|
|
for item in options.env:
|
|
|
|
try:
|
|
|
|
(key, value) = item.split('=', 1)
|
|
|
|
except ValueError:
|
|
|
|
# FIXME: this should raise an IPA exception with an
|
|
|
|
# error code.
|
|
|
|
# --Jason, 2008-10-31
|
|
|
|
pass
|
|
|
|
overrides[str(key.strip())] = value.strip()
|
|
|
|
for key in ('conf', 'debug', 'verbose'):
|
|
|
|
value = getattr(options, key, None)
|
|
|
|
if value is not None:
|
|
|
|
overrides[key] = value
|
|
|
|
if context is not None:
|
|
|
|
overrides['context'] = context
|
|
|
|
self.bootstrap(**overrides)
|
2008-10-27 00:28:06 -05:00
|
|
|
|
2008-10-28 00:39:43 -05:00
|
|
|
def load_plugins(self):
|
2008-10-27 00:53:44 -05:00
|
|
|
"""
|
|
|
|
Load plugins from all standard locations.
|
|
|
|
|
|
|
|
`API.bootstrap` will automatically be called if it hasn't been
|
|
|
|
already.
|
|
|
|
"""
|
|
|
|
self.__doing('load_plugins')
|
|
|
|
self.__do_if_not_done('bootstrap')
|
2008-10-28 00:39:43 -05:00
|
|
|
if self.env.mode == 'unit_test':
|
2008-10-27 01:23:43 -05:00
|
|
|
return
|
|
|
|
util.import_plugins_subpackage('ipalib')
|
2008-10-28 00:39:43 -05:00
|
|
|
if self.env.in_server:
|
|
|
|
util.import_plugins_subpackage('ipa_server')
|
2008-10-27 00:53:44 -05:00
|
|
|
|
2008-08-12 17:52:37 -05:00
|
|
|
def finalize(self):
|
2008-08-08 16:40:03 -05:00
|
|
|
"""
|
|
|
|
Finalize the registration, instantiate the plugins.
|
2008-10-27 00:53:44 -05:00
|
|
|
|
|
|
|
`API.bootstrap` will automatically be called if it hasn't been
|
|
|
|
already.
|
2008-08-08 16:40:03 -05:00
|
|
|
"""
|
2008-10-27 00:28:06 -05:00
|
|
|
self.__doing('finalize')
|
2008-10-31 13:29:59 -05:00
|
|
|
self.__do_if_not_done('load_plugins')
|
2008-08-14 17:13:42 -05:00
|
|
|
|
2008-09-23 23:44:52 -05:00
|
|
|
class PluginInstance(object):
|
|
|
|
"""
|
|
|
|
Represents a plugin instance.
|
|
|
|
"""
|
|
|
|
|
|
|
|
i = 0
|
|
|
|
|
|
|
|
def __init__(self, klass):
|
|
|
|
self.created = self.next()
|
|
|
|
self.klass = klass
|
|
|
|
self.instance = klass()
|
|
|
|
self.bases = []
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
def next(cls):
|
|
|
|
cls.i += 1
|
|
|
|
return cls.i
|
|
|
|
|
|
|
|
class PluginInfo(ReadOnly):
|
|
|
|
def __init__(self, p):
|
|
|
|
assert isinstance(p, PluginInstance)
|
|
|
|
self.created = p.created
|
|
|
|
self.name = p.klass.__name__
|
|
|
|
self.module = str(p.klass.__module__)
|
|
|
|
self.plugin = '%s.%s' % (self.module, self.name)
|
|
|
|
self.bases = tuple(b.__name__ for b in p.bases)
|
|
|
|
lock(self)
|
|
|
|
|
|
|
|
plugins = {}
|
|
|
|
def plugin_iter(base, subclasses):
|
|
|
|
for klass in subclasses:
|
|
|
|
assert issubclass(klass, base)
|
|
|
|
if klass not in plugins:
|
|
|
|
plugins[klass] = PluginInstance(klass)
|
|
|
|
p = plugins[klass]
|
|
|
|
assert base not in p.bases
|
|
|
|
p.bases.append(base)
|
2008-09-23 19:44:41 -05:00
|
|
|
if base.__proxy__:
|
2008-09-23 23:44:52 -05:00
|
|
|
yield PluginProxy(base, p.instance)
|
2008-09-23 19:44:41 -05:00
|
|
|
else:
|
2008-09-23 23:44:52 -05:00
|
|
|
yield p.instance
|
2008-08-08 16:40:03 -05:00
|
|
|
|
2008-08-15 00:07:17 -05:00
|
|
|
for name in self.register:
|
|
|
|
base = self.register[name]
|
|
|
|
magic = getattr(self.register, name)
|
|
|
|
namespace = NameSpace(
|
|
|
|
plugin_iter(base, (magic[k] for k in magic))
|
|
|
|
)
|
2008-08-14 17:13:42 -05:00
|
|
|
assert not (
|
|
|
|
name in self.__d or hasattr(self, name)
|
|
|
|
)
|
|
|
|
self.__d[name] = namespace
|
|
|
|
object.__setattr__(self, name, namespace)
|
|
|
|
|
2008-09-23 23:44:52 -05:00
|
|
|
for p in plugins.itervalues():
|
|
|
|
p.instance.set_api(self)
|
|
|
|
assert p.instance.api is self
|
2008-09-21 16:50:56 -05:00
|
|
|
|
2008-09-23 23:44:52 -05:00
|
|
|
for p in plugins.itervalues():
|
|
|
|
p.instance.finalize()
|
2008-08-12 18:40:36 -05:00
|
|
|
object.__setattr__(self, '_API__finalized', True)
|
2008-09-23 23:44:52 -05:00
|
|
|
tuple(PluginInfo(p) for p in plugins.itervalues())
|
|
|
|
object.__setattr__(self, 'plugins',
|
|
|
|
tuple(PluginInfo(p) for p in plugins.itervalues())
|
|
|
|
)
|