summaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--ipalib/frontend.py53
-rw-r--r--ipalib/plugins/baseldap.py61
-rw-r--r--ipatests/test_xmlrpc/test_baseldap_plugin.py7
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))