summaryrefslogtreecommitdiffstats
path: root/ipalib
diff options
context:
space:
mode:
authorJason Gerard DeRose <jderose@redhat.com>2008-08-14 18:50:21 +0000
committerJason Gerard DeRose <jderose@redhat.com>2008-08-14 18:50:21 +0000
commit7c64c8b95457c3aed1a3243ef1c22c303697a057 (patch)
treeafee3a078dfe9950eccb58190b44df97156d51cd /ipalib
parent87cad5078a3c9ef7a978c85905309ee7d3ec194d (diff)
downloadfreeipa-7c64c8b95457c3aed1a3243ef1c22c303697a057.tar.gz
freeipa-7c64c8b95457c3aed1a3243ef1c22c303697a057.tar.xz
freeipa-7c64c8b95457c3aed1a3243ef1c22c303697a057.zip
161: Registrar now takes advantage of DictProxy; updated corresponding unit tests
Diffstat (limited to 'ipalib')
-rw-r--r--ipalib/plugable.py78
-rw-r--r--ipalib/tests/test_plugable.py46
2 files changed, 74 insertions, 50 deletions
diff --git a/ipalib/plugable.py b/ipalib/plugable.py
index ffef8d643..b663a7ea4 100644
--- a/ipalib/plugable.py
+++ b/ipalib/plugable.py
@@ -102,6 +102,16 @@ class ReadOnly(object):
return object.__delattr__(self, name)
+def lock(obj):
+ """
+ Convenience function to lock a `ReadOnly` instance.
+ """
+ assert isinstance(obj, ReadOnly)
+ obj.__lock__()
+ assert obj.__islocked__()
+ return obj
+
+
class Plugin(ReadOnly):
"""
Base class for all plugins.
@@ -477,8 +487,7 @@ class DictProxy(ReadOnly):
Although a DictProxy is read-only, the underlying dict can change (and is
assumed to).
- One of these is created for each allowed base class in a `Registrar`
- instance.
+ One of these is created for each allowed base in a `Registrar` instance.
"""
def __init__(self, d):
"""
@@ -486,8 +495,7 @@ class DictProxy(ReadOnly):
"""
assert type(d) is dict, '`d` must be %r, got %r' % (dict, type(d))
self.__d = d
- self.__lock__()
- assert self.__islocked__()
+ lock(self)
def __len__(self):
"""
@@ -532,19 +540,44 @@ class DictProxy(ReadOnly):
class Registrar(ReadOnly):
+ """
+ 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.
+ """
def __init__(self, *allowed):
"""
:param allowed: Base classes from which plugins accepted by this
Registrar must subclass.
"""
- self.__allowed = frozenset(allowed)
+
+ class Val(ReadOnly):
+ """
+ Internal class used so that only one mapping is needed.
+ """
+ def __init__(self, base):
+ assert inspect.isclass(base)
+ self.base = base
+ self.name = base.__name__
+ self.sub_d = dict()
+ self.dictproxy = DictProxy(self.sub_d)
+ lock(self)
+
+ self.__allowed = allowed
self.__d = {}
self.__registered = set()
for base in self.__allowed:
- assert inspect.isclass(base)
- assert base.__name__ not in self.__d
- self.__d[base.__name__] = {}
- self.__lock__()
+ val = Val(base)
+ assert not (
+ val.name in self.__d or hasattr(self, val.name)
+ )
+ self.__d[val.name] = val
+ setattr(self, val.name, val.dictproxy)
+ lock(self)
def __findbases(self, klass):
"""
@@ -580,7 +613,7 @@ class Registrar(ReadOnly):
# Find the base class or raise SubclassError:
for base in self.__findbases(klass):
- sub_d = self.__d[base.__name__]
+ sub_d = self.__d[base.__name__].sub_d
# Check override:
if klass.__name__ in sub_d:
@@ -598,26 +631,19 @@ class Registrar(ReadOnly):
# The plugin is okay, add to __registered:
self.__registered.add(klass)
- def __getitem__(self, item):
+ def __getitem__(self, key):
"""
- Returns a copy of the namespace dict of the base class named
- ``name``.
+ Returns the DictProxy for plugins subclassed from the base named ``key``.
"""
- if inspect.isclass(item):
- if item not in self.__allowed:
- raise KeyError(repr(item))
- key = item.__name__
- else:
- key = item
- return dict(self.__d[key])
+ if key not in self.__d:
+ raise KeyError('no base class named %r' % key)
+ return self.__d[key].dictproxy
- def __contains__(self, item):
+ def __contains__(self, key):
"""
- Returns True if a base class named ``name`` is in this Registrar.
+ Returns True if a base class named ``key`` is in this Registrar.
"""
- if inspect.isclass(item):
- return item in self.__allowed
- return item in self.__d
+ return key in self.__d
def __iter__(self):
"""
@@ -625,7 +651,7 @@ class Registrar(ReadOnly):
base.
"""
for base in self.__allowed:
- sub_d = self.__d[base.__name__]
+ sub_d = self.__d[base.__name__].sub_d
yield (base, tuple(sub_d[k] for k in sorted(sub_d)))
diff --git a/ipalib/tests/test_plugable.py b/ipalib/tests/test_plugable.py
index 2854ee6a6..b64cf305c 100644
--- a/ipalib/tests/test_plugable.py
+++ b/ipalib/tests/test_plugable.py
@@ -528,11 +528,10 @@ def test_Registrar():
# Test __hasitem__, __getitem__:
for base in [Base1, Base2]:
- assert base in r
assert base.__name__ in r
- assert r[base] == {}
- assert r[base.__name__] == {}
-
+ dp = r[base.__name__]
+ assert type(dp) is plugable.DictProxy
+ assert len(dp) == 0
# Check that TypeError is raised trying to register something that isn't
# a class:
@@ -544,12 +543,12 @@ def test_Registrar():
# Check that registration works
r(plugin1)
- sub_d = r['Base1']
- assert len(sub_d) == 1
- assert sub_d['plugin1'] is plugin1
- # Check that a copy is returned
- assert sub_d is not r['Base1']
- assert sub_d == r['Base1']
+ dp = r['Base1']
+ assert type(dp) is plugable.DictProxy
+ assert len(dp) == 1
+ assert r.Base1 is dp
+ assert dp['plugin1'] is plugin1
+ assert dp.plugin1 is plugin1
# Check that DuplicateError is raised trying to register exact class
# again:
@@ -566,21 +565,19 @@ def test_Registrar():
# Check that overriding works
r(plugin1, override=True)
- sub_d = r['Base1']
- assert len(sub_d) == 1
- assert sub_d['plugin1'] is plugin1
- assert sub_d['plugin1'] is not orig1
+ assert len(r.Base1) == 1
+ assert r.Base1.plugin1 is plugin1
+ assert r.Base1.plugin1 is not orig1
# Check that MissingOverrideError is raised trying to override a name
# not yet registerd:
raises(errors.MissingOverrideError, r, plugin2, override=True)
- # Check that additional plugin can be registered:
+ # Test that another plugin can be registered:
+ assert len(r.Base2) == 0
r(plugin2)
- sub_d = r['Base2']
- assert len(sub_d) == 1
- assert sub_d['plugin2'] is plugin2
-
+ assert len(r.Base2) == 1
+ assert r.Base2.plugin2 is plugin2
# Setup to test __iter__:
class plugin1a(Base1):
@@ -612,12 +609,13 @@ def test_Registrar():
# Again test __hasitem__, __getitem__:
for base in [Base1, Base2]:
- assert base in r
assert base.__name__ in r
- d = dict((p.__name__, p) for p in m[base.__name__])
- assert len(d) == 3
- assert r[base] == d
- assert r[base.__name__] == d
+ dp = r[base.__name__]
+ assert len(dp) == 3
+ for key in dp:
+ klass = dp[key]
+ assert getattr(dp, key) is klass
+ assert issubclass(klass, base)
def test_API():