diff options
author | Petr Viktorin <pviktori@redhat.com> | 2012-04-25 10:31:10 -0400 |
---|---|---|
committer | Martin Kosek <mkosek@redhat.com> | 2012-06-14 11:09:43 +0200 |
commit | 9960149e3f84564ab324bfb9db7c50063d87a7bd (patch) | |
tree | c3caa731e88c1c12479edd6a2e59a9518688626b /tests | |
parent | f52fa2a0185c8cc4e1c2cacda3eac59209e659f4 (diff) | |
download | freeipa-9960149e3f84564ab324bfb9db7c50063d87a7bd.tar.gz freeipa-9960149e3f84564ab324bfb9db7c50063d87a7bd.tar.xz freeipa-9960149e3f84564ab324bfb9db7c50063d87a7bd.zip |
Rework the CallbackInterface
Fix several problems with the callback interface:
- Automatically registered callbacks (i.e. methods named
exc_callback, pre_callback etc) were registered on every
instantiation.
Fix: Do not register callbacks in __init__; instead return the
method when asked for it.
- The calling code had to distinguish between bound methods and
plain functions by checking the 'im_self' attribute.
Fix: Always return the "default" callback as an unbound method.
Registered callbacks now always take the extra `self` argument,
whether they happen to be bound methods or not.
Calling code now always needs to pass the `self` argument.
- Did not work well with inheritance: due to the fact that Python
looks up missing attributes in superclasses, callbacks could
get attached to a superclass if it was instantiated early enough. *
Fix: Instead of attribute lookup, use a dictionary with class keys.
- The interface included the callback types, which are LDAP-specific.
Fix: Create generic register_callback and get_callback mehods,
move LDAP-specific code to BaseLDAPCommand
Update code that calls the callbacks.
Add tests.
Remove lint exceptions for CallbackInterface.
* https://fedorahosted.org/freeipa/ticket/2674
Diffstat (limited to 'tests')
-rw-r--r-- | tests/test_xmlrpc/test_baseldap_plugin.py | 95 |
1 files changed, 94 insertions, 1 deletions
diff --git a/tests/test_xmlrpc/test_baseldap_plugin.py b/tests/test_xmlrpc/test_baseldap_plugin.py index 0800a5d52..6a8501f76 100644 --- a/tests/test_xmlrpc/test_baseldap_plugin.py +++ b/tests/test_xmlrpc/test_baseldap_plugin.py @@ -24,11 +24,12 @@ Test the `ipalib.plugins.baseldap` module. from ipalib import errors from ipalib.plugins import baseldap + def test_exc_wrapper(): """Test the CallbackInterface._exc_wrapper helper method""" handled_exceptions = [] - class test_callback(baseldap.CallbackInterface): + class test_callback(baseldap.BaseLDAPCommand): """Fake IPA method""" def test_fail(self): self._exc_wrapper([], {}, self.fail)(1, 2, a=1, b=2) @@ -64,3 +65,95 @@ def test_exc_wrapper(): instance.test_fail() assert handled_exceptions == [None, errors.ExecutionError] + + +def test_callback_registration(): + class callbacktest_base(baseldap.CallbackInterface): + _callback_registry = dict(test={}) + + def test_callback(self, param): + messages.append(('Base test_callback', param)) + + def registered_callback(self, param): + messages.append(('Base registered callback', param)) + callbacktest_base.register_callback('test', registered_callback) + + class SomeClass(object): + def registered_callback(self, command, param): + messages.append(('Registered callback from another class', param)) + callbacktest_base.register_callback('test', SomeClass().registered_callback) + + class callbacktest_subclass(callbacktest_base): + pass + + def subclass_callback(self, param): + messages.append(('Subclass registered callback', param)) + callbacktest_subclass.register_callback('test', subclass_callback) + + + messages = [] + instance = callbacktest_base() + for callback in instance.get_callbacks('test'): + callback(instance, 42) + assert messages == [ + ('Base test_callback', 42), + ('Base registered callback', 42), + ('Registered callback from another class', 42)] + + messages = [] + instance = callbacktest_subclass() + for callback in instance.get_callbacks('test'): + callback(instance, 42) + assert messages == [ + ('Base test_callback', 42), + ('Subclass registered callback', 42)] + + +def test_exc_callback_registration(): + messages = [] + class callbacktest_base(baseldap.BaseLDAPCommand): + """A method superclass with an exception callback""" + def exc_callback(self, keys, options, exc, call_func, *args, **kwargs): + """Let the world know we saw the error, but don't handle it""" + messages.append('Base exc_callback') + raise exc + + def test_fail(self): + """Raise a handled exception""" + try: + self._exc_wrapper([], {}, self.fail)(1, 2, a=1, b=2) + except Exception: + pass + + def fail(self, *args, **kwargs): + """Raise an error""" + raise errors.ExecutionError('failure') + + base_instance = callbacktest_base() + + class callbacktest_subclass(callbacktest_base): + pass + + @callbacktest_subclass.register_exc_callback + def exc_callback(self, keys, options, exc, call_func, *args, **kwargs): + """Subclass's private exception callback""" + messages.append('Subclass registered callback') + raise exc + + subclass_instance = callbacktest_subclass() + + # Make sure exception in base class is only handled by the base class + base_instance.test_fail() + assert messages == ['Base exc_callback'] + + + @callbacktest_base.register_exc_callback + def exc_callback(self, keys, options, exc, call_func, *args, **kwargs): + """Callback on super class; doesn't affect the subclass""" + messages.append('Superclass registered callback') + raise exc + + # Make sure exception in subclass is only handled by both + messages = [] + subclass_instance.test_fail() + assert messages == ['Base exc_callback', 'Subclass registered callback'] |