178: Registrar now subclasses from DictProxy; made Registrar.__iter__ behave same as the other container emulation in plugable.py, and made the dictorary interface return the base and the attribute interface return the MagicDict; updated API class and unit tests

This commit is contained in:
Jason Gerard DeRose 2008-08-15 05:07:17 +00:00
parent 5ed58fdb42
commit a24f2121d5
2 changed files with 47 additions and 87 deletions

View File

@ -546,7 +546,7 @@ class NameSpace(DictProxy):
return '%s(<%d members>)' % (self.__class__.__name__, len(self))
class Registrar(ReadOnly):
class Registrar(DictProxy):
"""
Collects plugin classes as they are registered.
@ -561,30 +561,19 @@ class Registrar(ReadOnly):
:param allowed: Base classes from which plugins accepted by this
Registrar must subclass.
"""
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 = MagicDict(self.sub_d)
lock(self)
self.__allowed = allowed
self.__d = {}
self.__allowed = dict((base, {}) for base in allowed)
self.__registered = set()
for base in self.__allowed:
val = Val(base)
assert not (
val.name in self.__d or hasattr(self, val.name)
super(Registrar, self).__init__(
dict(self.__base_iter())
)
self.__d[val.name] = val
setattr(self, val.name, val.dictproxy)
lock(self)
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)
def __findbases(self, klass):
"""
@ -597,12 +586,12 @@ class Registrar(ReadOnly):
"""
assert inspect.isclass(klass)
found = False
for base in self.__allowed:
for (base, sub_d) in self.__allowed.iteritems():
if issubclass(klass, base):
found = True
yield base
yield (base, sub_d)
if not found:
raise errors.SubclassError(klass, self.__allowed)
raise errors.SubclassError(klass, self.__allowed.keys())
def __call__(self, klass, override=False):
"""
@ -619,17 +608,15 @@ class Registrar(ReadOnly):
raise errors.DuplicateError(klass)
# Find the base class or raise SubclassError:
for base in self.__findbases(klass):
sub_d = self.__d[base.__name__].sub_d
for (base, sub_d) in self.__findbases(klass):
# Check override:
if klass.__name__ in sub_d:
# Must use override=True to override:
if not override:
# Must use override=True to override:
raise errors.OverrideError(base, klass)
else:
# There was nothing already registered to override:
if override:
# There was nothing already registered to override:
raise errors.MissingOverrideError(base, klass)
# The plugin is okay, add to sub_d:
@ -638,29 +625,6 @@ class Registrar(ReadOnly):
# The plugin is okay, add to __registered:
self.__registered.add(klass)
def __getitem__(self, key):
"""
Returns the MagicDict for plugins subclassed from the base named ``key``.
"""
if key not in self.__d:
raise KeyError('no base class named %r' % key)
return self.__d[key].dictproxy
def __contains__(self, key):
"""
Returns True if a base class named ``key`` is in this Registrar.
"""
return key in self.__d
def __iter__(self):
"""
Iterates through a (base, registered_plugins) tuple for each allowed
base.
"""
for base in self.__allowed:
sub_d = self.__d[base.__name__].sub_d
yield (base, tuple(sub_d[k] for k in sorted(sub_d)))
class API(DictProxy):
"""
@ -687,16 +651,19 @@ class API(DictProxy):
plugin = instances[klass]
yield PluginProxy(base, plugin)
for (base, classes) in self.register:
namespace = NameSpace(plugin_iter(base, classes))
name = base.__name__
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))
)
assert not (
name in self.__d or hasattr(self, name)
)
self.__d[name] = namespace
object.__setattr__(self, name, namespace)
for plugin in instances.values():
for plugin in instances.itervalues():
plugin.finalize(self)
lock(plugin)
assert plugin.api is self

View File

@ -643,12 +643,17 @@ def test_Registrar():
# Test creation of Registrar:
r = plugable.Registrar(Base1, Base2)
# Test __iter__:
assert list(r) == ['Base1', 'Base2']
# Test __hasitem__, __getitem__:
for base in [Base1, Base2]:
assert base.__name__ in r
dp = r[base.__name__]
assert type(dp) is plugable.MagicDict
assert len(dp) == 0
name = base.__name__
assert name in r
assert r[name] is base
magic = getattr(r, name)
assert type(magic) is plugable.MagicDict
assert len(magic) == 0
# Check that TypeError is raised trying to register something that isn't
# a class:
@ -660,12 +665,9 @@ def test_Registrar():
# Check that registration works
r(plugin1)
dp = r['Base1']
assert type(dp) is plugable.MagicDict
assert len(dp) == 1
assert r.Base1 is dp
assert dp['plugin1'] is plugin1
assert dp.plugin1 is plugin1
assert len(r.Base1) == 1
assert r.Base1['plugin1'] is plugin1
assert r.Base1.plugin1 is plugin1
# Check that DuplicateError is raised trying to register exact class
# again:
@ -696,7 +698,7 @@ def test_Registrar():
assert len(r.Base2) == 1
assert r.Base2.plugin2 is plugin2
# Setup to test __iter__:
# Setup to test more registration:
class plugin1a(Base1):
pass
r(plugin1a)
@ -713,25 +715,16 @@ def test_Registrar():
pass
r(plugin2b)
m = {
'Base1': set([plugin1, plugin1a, plugin1b]),
'Base2': set([plugin2, plugin2a, plugin2b]),
}
# Now test __iter__:
for (base, plugins) in r:
assert base in [Base1, Base2]
assert set(plugins) == m[base.__name__]
assert len(list(r)) == 2
# Again test __hasitem__, __getitem__:
for base in [Base1, Base2]:
assert base.__name__ in r
dp = r[base.__name__]
assert len(dp) == 3
for key in dp:
klass = dp[key]
assert getattr(dp, key) is klass
name = base.__name__
assert name in r
assert r[name] is base
magic = getattr(r, name)
assert len(magic) == 3
for key in magic:
klass = magic[key]
assert getattr(magic, key) is klass
assert issubclass(klass, base)