Back to index

plone3  3.1.7
Public Member Functions | Public Attributes
kss.core.actionwrapper.kssaction Class Reference

List of all members.

Public Member Functions

def __init__
def __get__
def apply

Public Attributes

 f
 wrapper_code

Detailed Description

Descriptor to bundle kss server actions.

- render() will be called automatically if there is no
  return value

- if KSSExplicitError is raised, a normal response is returned,
  containing a single command:error KSS command.

Let's say we have a class here - that is supposed to be a kss view.

    >>> from kss.core import kssaction, KSSExplicitError, KSSView

    >>> class MyView(KSSView):
    ...     def ok(self, a, b, c=0):
    ...         return 'OK %s %s %s' % (a, b, c)
    ...     def notok(self, a, b, c=0):
    ...         pass
    ...     def error(self, a, b, c=0):
    ...         raise KSSExplicitError, 'The error'
    ...     def exception(self, a, b, c=0):
    ...         raise Exception, 'Unknown exception'
     
Now we try qualifying with kssaction. We overwrite render too, 
just to enable sensible testing of the output:

    >>> class MyView(KSSView):
    ...     def render(self):
    ...         return 'Rendered'
    ...     @kssaction
    ...     def ok(self, a, b, c=3):
    ...         return 'OK %s %s %s' % (a, b, c)
    ...     @kssaction
    ...     def notok(self, a, b, c=3):
    ...         pass
    ...     @kssaction
    ...     def error(self, a, b, c=3):
    ...         raise KSSExplicitError, 'The error'
    ...     @kssaction
    ...     def exception(self, a, b, c=3):
    ...         raise Exception, 'Unknown exception'
 
Instantiate a view.

    >>> view = MyView(None, None)

Now, of course ok renders well.

    >>> view.ok(1, b=2)
    'OK 1 2 3'

Not ok will have implicit rendering.

    >>> view.notok(1, b=2)
    'Rendered'

The third type will return an error action. But it will render
instead of an error.

    >>> view.error(1, b=2)
    'Rendered'

The fourth type will be a real error.

    >>> view.exception(1, b=2)
    Traceback (most recent call last):
    ...
    Exception: Unknown exception

Now for the sake of it, let's test the rendered kukit response.
So, we don't overwrite render like as we did in the previous
tests.

    >>> from zope.publisher.browser import TestRequest

    >>> class MyView(KSSView):
    ...     @kssaction
    ...     def error(self, a, b, c=3):
    ...         raise KSSExplicitError, 'The error'
    ...     @kssaction
    ...     def with_docstring(self, a, b, c=3):
    ...         "Docstring"
    ...         raise KSSExplicitError, 'The error'
 
    >>> request = TestRequest()
    >>> view = MyView(None, request)

Set debug-mode command rendering so we can see the results in a
more structured form.

    >>> from zope import interface as iapi
    >>> from kss.core.tests.base import IDebugRequest
    >>> iapi.directlyProvides(request, iapi.directlyProvidedBy(request) + IDebugRequest)

See the results:

    >>> view.error(1, b=2)
    [{'selectorType': None, 'params': {'message': u'The error'}, 'name': 'error', 'selector': None}]

Usage of the method wrapped in browser view
-------------------------------------------

Finally, let's check if the method appears if defined on a browser view.
Since there could be a thousand reasons why Five's magic could fail,
it's good to check this. (XXX Note that this must be adjusted to run on Zope3.)

    >>> try:
    ...     import Products.Five
    ... except ImportError:
    ...     # probably zope 3, not supported
    ...     raise 'Zope3 not supported in this test'
    ... else:
    ...     from Products.Five.zcml import load_string, load_config

    >>> import kss.core.tests
    >>> kss.core.tests.MyView = MyView

We check for two basic types of declaration. The first one declares
a view with different attributes. The second one declares a dedicated
view with the method as the view default method. This is how we use
it in several places.

    >>> load_string("""
    ...      <configure xmlns="http://namespaces.zope.org/zope"
    ...      xmlns:browser="http://namespaces.zope.org/browser"
    ...      xmlns:five="http://namespaces.zope.org/five"
    ...      xmlns:zcml="http://namespaces.zope.org/zcml"
    ...      >
    ...
    ...      <browser:page
    ...          for="*"
    ...          class="kss.core.tests.MyView"
    ...          allowed_attributes="error with_docstring"
    ...          name="my_view"
    ...          permission="zope.Public"
    ...          />
    ...
    ...      <browser:page
    ...          for="*"
    ...          class="kss.core.tests.MyView"
    ...          attribute="error"
    ...          name="my_view2"
    ...          permission="zope.Public"
    ...          />
    ...
    ...  </configure>""")

Let's check it now:

    >>> self.folder.restrictedTraverse('/@@my_view/error')
    <bound method MyView.wrapper...

It must also work as a default method of a view since that is
main usage for us:

    >>> v = self.folder.restrictedTraverse('/my_view2')
    >>> isinstance(v, MyView)
    True
    >>> hasattr(v, 'error')
    True
    >>> v(1, b=2)
    [{'selectorType': None, 'params': {'message': u'The error'}, 'name': 'error', 'selector': None}]

In addition, to be publishable, the docstring must exist. Let's
see if the wrapper actually does this. If the method had a docstring,
it will be reused, but a docstring is provided in any case.

    >>> v = self.folder.restrictedTraverse('/@@my_view')
    >>> bool(v.error.__doc__)
    True

    >>> v.with_docstring.__doc__
    'Docstring'

Definition at line 26 of file actionwrapper.py.


Constructor & Destructor Documentation

Definition at line 201 of file actionwrapper.py.

00201 
00202     def __init__(self, f):
00203         self.f = f
00204         # Now this is a solution I don't like, but we need the same
00205         # function signature, otherwise the ZPublisher won't marshall
00206         # the parameters. *arg, **kw would not suffice since no parameters
00207         # would be marshalled at all.
00208         argspec = getargspec(f)
00209         orig_args = formatargspec(*argspec)[1:-1]
00210         if argspec[3] is None:
00211             fixed_args_num = len(argspec[0])
00212         else:
00213             fixed_args_num = len(argspec[0]) - len(argspec[3])
00214         values_list = [v for v in argspec[0][:fixed_args_num]]
00215         values_list.extend(['%s=%s' % (v, v) for v in argspec[0][fixed_args_num:]])
00216         values_args = ', '.join(values_list)
00217         # provide a docstring in any case.
00218         if self.f.__doc__ is not None:
00219             docstring = repr(f.__doc__)
00220         else:
00221             docstring = '"XXX"'
00222         # orig_args: "a, b, c=2"
00223         # values_args: "a, b, c=c"
00224         code = dedent('''\n
00225                 def wrapper(%s):
00226                     %s
00227                     return descr.apply(%s)
00228                 ''' % (orig_args, docstring, values_args))
00229         self.wrapper_code = compile(code, '<wrapper>', 'exec')

Here is the caller graph for this function:


Member Function Documentation

def kss.core.actionwrapper.kssaction.__get__ (   self,
  obj,
  cls = None 
)

Definition at line 230 of file actionwrapper.py.

00230 
00231     def __get__(self, obj, cls=None):
00232         d =  {'descr': self, 'self': obj}
00233         exec(self.wrapper_code, d)
00234         wrapper = d['wrapper'].__get__(obj, cls)
00235         return wrapper

def kss.core.actionwrapper.kssaction.apply (   self,
  obj,
  arg,
  kw 
)

Definition at line 236 of file actionwrapper.py.

00236 
00237     def apply(self, obj, *arg, **kw):
00238         try:
00239             result = self.f(obj, *arg, **kw)
00240         except KSSExplicitError, exc:
00241             # Clear all the commands, and emit an error command
00242             obj._initcommands()
00243             obj.commands.addCommand('error', message=str(exc))
00244             result = None
00245         if result is None:
00246             # render not returned - so we do it.
00247             result = obj.render()
00248         return result
00249 
# backward compatibility

Member Data Documentation

Definition at line 202 of file actionwrapper.py.

Definition at line 228 of file actionwrapper.py.


The documentation for this class was generated from the following file: