mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
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>
This commit is contained in:
parent
a30bc8a351
commit
60fa6ed444
@ -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.
|
||||
|
@ -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:
|
||||
|
@ -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))
|
||||
|
Loading…
Reference in New Issue
Block a user