[Freeipa-devel] [PATCH] 76 Refactor exc_callback invocation
Petr Viktorin
pviktori at redhat.com
Mon Apr 23 16:47:31 UTC 2012
On 04/23/2012 04:33 PM, Jan Cholasta wrote:
> Hi,
>
> this patch replaces _call_exc_callbacks with a function wrapper, which
> will automatically call exception callbacks when an exception is raised
> from the function. This removes the need to specify the function and its
> arguments twice (once in the function call itself and once in
> _call_exc_callbacks).
>
> Code like this:
>
> try:
> # original call
> ret = func(arg, kwarg=0)
> except ExecutionError, e:
> try:
> # the function and its arguments need to be specified again!
> ret = self._call_exc_callbacks(args, options, e, func, arg,
> kwarg=0)
> except ExecutionErrorSubclass, e:
> handle_error(e)
>
> becomes this:
>
> try:
> ret = self._exc_wrapper(args, options, func)(arg, kwarg=0)
> except ExecutionErrorSubclass, e:
> handle_error(e)
>
> As you can see, the resulting code is shorter and you don't have to
> remember to make changes to the arguments in two places.
>
> Honza
Please add a test, too. I've attached one you can use.
See also some style nitpicks below.
> --
> Jan Cholasta
>
> freeipa-jcholast-76-refactor-exc-callback.patch
>
>
>> From 8e070f571472ed5a27339bcc980b67ecca41b337 Mon Sep 17 00:00:00 2001
> From: Jan Cholasta<jcholast at redhat.com>
> Date: Thu, 19 Apr 2012 08:06:32 -0400
> Subject: [PATCH] Refactor exc_callback invocation.
>
> Replace _call_exc_callbacks with a function wrapper, which will automatically
> call exception callbacks when an exception is raised from the function. This
> removes the need to specify the function and its arguments twice (once in the
> function call itself and once in _call_exc_callbacks).
> ---
> ipalib/plugins/baseldap.py | 227 ++++++++++++++----------------------------
> ipalib/plugins/entitle.py | 19 ++--
> ipalib/plugins/group.py | 7 +-
> ipalib/plugins/permission.py | 27 +++---
> ipalib/plugins/pwpolicy.py | 11 +-
> 5 files changed, 109 insertions(+), 182 deletions(-)
>
> diff --git a/ipalib/plugins/baseldap.py b/ipalib/plugins/baseldap.py
> index f185977..f7a3bbc 100644
> --- a/ipalib/plugins/baseldap.py
> +++ b/ipalib/plugins/baseldap.py
> @@ -744,26 +744,24 @@ class CallbackInterface(Method):
> else:
> klass.INTERACTIVE_PROMPT_CALLBACKS.append(callback)
>
> - def _call_exc_callbacks(self, args, options, exc, call_func, *call_args, **call_kwargs):
> - rv = None
> - for i in xrange(len(getattr(self, 'EXC_CALLBACKS', []))):
> - callback = self.EXC_CALLBACKS[i]
> - try:
> - if hasattr(callback, 'im_self'):
> - rv = callback(
> - args, options, exc, call_func, *call_args, **call_kwargs
> - )
> - else:
> - rv = callback(
> - self, args, options, exc, call_func, *call_args,
> - **call_kwargs
> - )
> - except errors.ExecutionError, e:
> - if (i + 1)< len(self.EXC_CALLBACKS):
> - exc = e
> - continue
> - raise e
> - return rv
> + def _exc_wrapper(self, keys, options, call_func):
Consider adding a docstring, e.g.
"""Function wrapper that automatically calls exception callbacks"""
> + def wrapped(*call_args, **call_kwargs):
> + func = call_func
> + callbacks = list(getattr(self, 'EXC_CALLBACKS', []))
> + while True:
> + try:
You have some clever code here, rebinding `func` like you do.
It'd be nice if there was a comment warning that you're redefining a
function, in case someone who's not a Python expert looks at this.
Consider:
# `func` is either the original function, or the current error callback
> + return func(*call_args, **call_kwargs)
> + except errors.ExecutionError, e:
> + if len(callbacks) == 0:
Use just `if not callbacks`, as per PEP8.
> + raise
> + callback = callbacks.pop(0)
> + if hasattr(callback, 'im_self'):
> + def func(*args, **kwargs): #pylint: disable=E0102
> + return callback(keys, options, e, call_func, *args, **kwargs)
> + else:
> + def func(*args, **kwargs): #pylint: disable=E0102
> + return callback(self, keys, options, e, call_func, *args, **kwargs)
> + return wrapped
>
>
> class BaseLDAPCommand(CallbackInterface, Command):
[...]
> diff --git a/ipalib/plugins/entitle.py b/ipalib/plugins/entitle.py
> index 28d2c5d..6ade854 100644
> --- a/ipalib/plugins/entitle.py
> +++ b/ipalib/plugins/entitle.py
> @@ -642,12 +642,12 @@ class entitle_import(LDAPUpdate):
> If we are adding the first entry there are no updates so EmptyModlist
> will get thrown. Ignore it.
> """
> - if isinstance(exc, errors.EmptyModlist):
> - if not getattr(context, 'entitle_import', False):
> - raise exc
> - return (call_args, {})
> - else:
> - raise exc
> + if call_func.func_name == 'update_entry':
> + if isinstance(exc, errors.EmptyModlist):
> + if not getattr(context, 'entitle_import', False):
You didn't mention the additional checks for 'update_entry' in the
commit message.
By the way, the need for these checks suggests that a per-class registry
of error callbacks might not be the best design. But that's for more
long-term thinking.
> + raise exc
> + return (call_args, {})
> + raise exc
>
> def execute(self, *keys, **options):
> super(entitle_import, self).execute(*keys, **options)
[...]
--
Petr³
-------------- next part --------------
A non-text attachment was scrubbed...
Name: test_baseldap_plugin.py
Type: text/x-python
Size: 2161 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/freeipa-devel/attachments/20120423/ab941cf6/attachment.py>
More information about the Freeipa-devel
mailing list