Opened 11 years ago

Last modified 11 years ago

#638 accepted enhancement

meta-decorator hooks / emacs-lisp style advice — at Version 2

Reported by: Christopher Allan Webber Owned by:
Priority: minor Milestone:
Component: programming Keywords: pluginapi
Cc: Parent Tickets:

Description (last modified by Christopher Allan Webber)

This is an incredibly evil and flexible idea that, like decorators itself, is probably elegant, hard to understand, powerful, and yes, a little bit evil.

So this is an idea inspired from emacs lisp, which allows you to pass in functions to wrap a function that already exists (this is called "advising functions"). Actually, hooks in emacs look a heck of a lot like decorators, so actually...

Say you have this function:

  @meta_decorator_hook('some_function_hook')
  def some_function(foo):
     bla bla

Now, you want to wrap this function! But how do you wrap it? It's already defined, you can't just wrap arbitrary things around it! ... or can you?

In this meta_decorator_hook, the meta_decorator_hook returns a lazy-loaded method set to actually construct a chain of wrapped methods. So if we did something like:

  pluginapi.wrap_function('some_function_hook', our_function)

.. it would return, basically a WrappedFunction() that, the first time it is executed, walks through all the decorator like methods that have been pushed onto that hook and wraps them in each other. That meta-decorated method is cached, and eventually, executed.

Sounds evil, right? :)

This could be super powerful though. You could do crazy things like double-check the results of a view before returning it, or totally override the view and decide not to call it at all for your plugin, call it both before and after, or set up special-case exception handling for hooks that are called within the function itself.

I'm sure I didn't make that sound any less evil, but I think it'd be really great.

Change History (2)

comment:1 by Christopher Allan Webber, 11 years ago

Type: defectenhancement

These are some extra notes explaining additional rationale for this that I wrote in my email about plugins to the mailing list:

How do we "halt" a function if a plugin needs to tell us to?

Exceptions probably... but how to make this clean?

Okay, let me explain a bit more. Say someone implements a special hook that sets up something special (I'm having a hard time thinking of what) but notices that something is terribly wrong. It needs to immediately halt the execution of the function. A reasonable thing to do is raise an exception. But we don't just want it to show up as a programming error and hit the logs. How to handle it? One of two ways:

We could already be expecting certain types of exceptions to possibly be raised like the following:

  try:
     pluginapi.callable_hook_runall('some_hook', arg, blarg)
  except ExpectingThisException:
     something something
     return Response('oh snap')

Okay, but what if the view designers weren't able to anticipate the kind of issue you have... how to handle this?

The answer is the meta-decorators solution above I think. In your own meta-decorator around the view that calls the hook above, you could set yourself up to catch the exception.

Tricky, but should work! :)

comment:2 by Christopher Allan Webber, 11 years ago

Description: modified (diff)
Note: See TracTickets for help on using tickets.