mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
161: Registrar now takes advantage of DictProxy; updated corresponding unit tests
This commit is contained in:
@@ -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)))
|
||||
|
||||
|
||||
|
||||
@@ -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():
|
||||
|
||||
Reference in New Issue
Block a user