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 /ipalib/frontend.py | |
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>
Diffstat (limited to 'ipalib/frontend.py')
-rw-r--r-- | ipalib/frontend.py | 53 |
1 files changed, 53 insertions, 0 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. |