diff options
author | Jan Cholasta <jcholast@redhat.com> | 2016-04-27 09:34:04 +0200 |
---|---|---|
committer | Jan Cholasta <jcholast@redhat.com> | 2016-05-25 16:06:26 +0200 |
commit | 60fa6ed4442279c2c42a72ee34c51b401e6a18d0 (patch) | |
tree | 8d3306d0154d82d6ae4fdac283fb55ad376acbb6 | |
parent | a30bc8a351b983725c082e603f311e17a6d0178e (diff) | |
download | freeipa-60fa6ed4442279c2c42a72ee34c51b401e6a18d0.tar.gz freeipa-60fa6ed4442279c2c42a72ee34c51b401e6a18d0.tar.xz freeipa-60fa6ed4442279c2c42a72ee34c51b401e6a18d0.zip |
frontend: merge baseldap.CallbackRegistry into Command
Also make it possible for subclasses to introduce new callback types.
https://fedorahosted.org/freeipa/ticket/4739
Reviewed-By: David Kupka <dkupka@redhat.com>
-rw-r--r-- | ipalib/frontend.py | 53 | ||||
-rw-r--r-- | ipalib/plugins/baseldap.py | 61 | ||||
-rw-r--r-- | ipatests/test_xmlrpc/test_baseldap_plugin.py | 7 |
3 files changed, 63 insertions, 58 deletions
diff --git a/ipalib/frontend.py b/ipalib/frontend.py index d9bf3d6c7..23d720e72 100644 --- a/ipalib/frontend.py +++ b/ipalib/frontend.py @@ -369,6 +369,9 @@ class HasParam(Plugin): return context.current_frame +_callback_registry = {} + + class Command(HasParam): """ A public IPA atomic operation. @@ -390,6 +393,14 @@ class Command(HasParam): ['my_command'] >>> api.Command.my_command # doctest:+ELLIPSIS ipalib.frontend.my_command() + + This class's subclasses allow different types of callbacks to be added and + removed to them. + Registering a callback is done either by ``register_callback``, or by + defining a ``<type>_callback`` method. + + Subclasses should define the `callback_types` attribute as a tuple of + allowed callback types. """ finalize_early = False @@ -414,6 +425,8 @@ class Command(HasParam): msg_summary = None msg_truncated = _('Results are truncated, try a more specific search') + callback_types = () + def __call__(self, *args, **options): """ Perform validation and then execute the command. @@ -1088,6 +1101,46 @@ class Command(HasParam): return json_dict + @classmethod + def get_callbacks(cls, callback_type): + """Yield callbacks of the given type""" + # Use one shared callback registry, keyed on class, to avoid problems + # with missing attributes being looked up in superclasses + callbacks = _callback_registry.get(callback_type, {}).get(cls, [None]) + for callback in callbacks: + if callback is None: + try: + yield getattr(cls, '%s_callback' % callback_type) + except AttributeError: + pass + else: + yield callback + + @classmethod + def register_callback(cls, callback_type, callback, first=False): + """Register a callback + + :param callback_type: The callback type (e.g. 'pre', 'post') + :param callback: The callable added + :param first: If true, the new callback will be added before all + existing callbacks; otherwise it's added after them + + Note that callbacks registered this way will be attached to this class + only, not to its subclasses. + """ + assert callback_type in cls.callback_types + assert callable(callback) + _callback_registry.setdefault(callback_type, {}) + try: + callbacks = _callback_registry[callback_type][cls] + except KeyError: + callbacks = _callback_registry[callback_type][cls] = [None] + if first: + callbacks.insert(0, callback) + else: + callbacks.append(callback) + + class LocalOrRemote(Command): """ A command that is explicitly executed locally or remotely. diff --git a/ipalib/plugins/baseldap.py b/ipalib/plugins/baseldap.py index 851487206..4d4e5ac41 100644 --- a/ipalib/plugins/baseldap.py +++ b/ipalib/plugins/baseldap.py @@ -28,7 +28,7 @@ import base64 import six from ipalib import api, crud, errors -from ipalib import Method, Object, Command +from ipalib import Method, Object from ipalib import Flag, Int, Str from ipalib.cli import to_cli from ipalib import output @@ -865,59 +865,7 @@ def _check_limit_object_class(attributes, attrs, allow_only): attribute=limitattrs[0])) -class CallbackInterface(Method): - """Callback registration interface - - This class's subclasses allow different types of callbacks to be added and - removed to them. - Registering a callback is done either by ``register_callback``, or by - defining a ``<type>_callback`` method. - - Subclasses should define the `_callback_registry` attribute as a dictionary - mapping allowed callback types to (initially) empty dictionaries. - """ - - _callback_registry = dict() - - @classmethod - def get_callbacks(cls, callback_type): - """Yield callbacks of the given type""" - # Use one shared callback registry, keyed on class, to avoid problems - # with missing attributes being looked up in superclasses - callbacks = cls._callback_registry[callback_type].get(cls, [None]) - for callback in callbacks: - if callback is None: - try: - yield getattr(cls, '%s_callback' % callback_type) - except AttributeError: - pass - else: - yield callback - - @classmethod - def register_callback(cls, callback_type, callback, first=False): - """Register a callback - - :param callback_type: The callback type (e.g. 'pre', 'post') - :param callback: The callable added - :param first: If true, the new callback will be added before all - existing callbacks; otherwise it's added after them - - Note that callbacks registered this way will be attached to this class - only, not to its subclasses. - """ - assert callable(callback) - try: - callbacks = cls._callback_registry[callback_type][cls] - except KeyError: - callbacks = cls._callback_registry[callback_type][cls] = [None] - if first: - callbacks.insert(0, callback) - else: - callbacks.append(callback) - - -class BaseLDAPCommand(CallbackInterface, Command): +class BaseLDAPCommand(Method): """ Base class for Base LDAP Commands. """ @@ -940,7 +888,10 @@ last, after all sets and adds."""), exclude='webui', ) - _callback_registry = dict(pre={}, post={}, exc={}, interactive_prompt={}) + callback_types = Method.callback_types + ('pre', + 'post', + 'exc', + 'interactive_prompt') def get_summary_default(self, output): if 'value' in output: diff --git a/ipatests/test_xmlrpc/test_baseldap_plugin.py b/ipatests/test_xmlrpc/test_baseldap_plugin.py index 6764b1051..faec5e598 100644 --- a/ipatests/test_xmlrpc/test_baseldap_plugin.py +++ b/ipatests/test_xmlrpc/test_baseldap_plugin.py @@ -26,6 +26,7 @@ import ldap from ipapython.dn import DN from ipapython import ipaldap from ipalib import errors +from ipalib.frontend import Command from ipalib.plugins import baseldap from ipatests.util import assert_deepequal import pytest @@ -33,7 +34,7 @@ import pytest @pytest.mark.tier0 def test_exc_wrapper(): - """Test the CallbackInterface._exc_wrapper helper method""" + """Test the BaseLDAPCommand._exc_wrapper helper method""" handled_exceptions = [] class test_callback(baseldap.BaseLDAPCommand): @@ -77,8 +78,8 @@ def test_exc_wrapper(): @pytest.mark.tier0 def test_callback_registration(): - class callbacktest_base(baseldap.CallbackInterface): - _callback_registry = dict(test={}) + class callbacktest_base(Command): + callback_types = Command.callback_types + ('test',) def test_callback(self, param): messages.append(('Base test_callback', param)) |