summaryrefslogtreecommitdiffstats
path: root/ipalib/frontend.py
diff options
context:
space:
mode:
authorJan Cholasta <jcholast@redhat.com>2016-04-27 09:34:04 +0200
committerJan Cholasta <jcholast@redhat.com>2016-05-25 16:06:26 +0200
commit60fa6ed4442279c2c42a72ee34c51b401e6a18d0 (patch)
tree8d3306d0154d82d6ae4fdac283fb55ad376acbb6 /ipalib/frontend.py
parenta30bc8a351b983725c082e603f311e17a6d0178e (diff)
downloadfreeipa-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.py53
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.