ToscaWidgets and CherryPy

7 views
Skip to first unread message

kong meng

unread,
Mar 18, 2008, 3:46:59 PM3/18/08
to ToscaWidgets-discuss
Is there any info/advice out there on how to adapt the code for new
middleware? I'd like to use ToscaWidgets in a CherryPy application
without any TurboGears dependancy.

percious

unread,
Mar 25, 2008, 10:53:28 AM3/25/08
to ToscaWidgets-discuss
Toscawidgets does provide wsgi middleware, so if you use the cherrypy
wsgi server it should work just fine. Take a look at middleware.py in
the source code, or even take a look at how I got it to work in Grok:
http://code.google.com/p/dbsprockets/wiki/Installation

cheers.
-chris

Paul Johnston

unread,
Mar 25, 2008, 5:48:51 PM3/25/08
to toscawidge...@googlegroups.com
Hi,

>Toscawidgets does provide wsgi middleware, so if you use the cherrypy
>wsgi server it should work just fine. Take a look at middleware.py in
>the source code, or even take a look at how I got it to work in Grok:
>http://code.google.com/p/dbsprockets/wiki/Installation
>
>

I think Alberto said this doesn't work, as the middleware is called in a
different thread to the controller, which breaks request-local storage.
This is why TW uses a CherryPy filter on TG1.

Kong - you're best bet is to set up TW roughly as for TG1, and do the
further tweaks necessary to make it work.

Paul

Rich Barton

unread,
Mar 27, 2008, 3:58:29 PM3/27/08
to toscawidge...@googlegroups.com

So I made a CherryPy filter, and got ToscaWidgets to render a simple form for me.  The question I now have is what special code is needed to get TW to handle errors appropriately and display messages in the view? Also, how do I get it to display previously entered information?

## some code snipets

class PostForm(forms.ListForm):
    class fields(WidgetsList):
        email = forms.TextField(validator=Email(not_empty=True))
        author = forms.SingleSelectField(options = ["Peter", "Lucy"])
        content = forms.TextArea()
forms.FormField.engine_name = "mako"
post_form = PostForm("post_form")

    # index.html
    # calling ${post_form(action="save")}
    def index(self, *args, **kwds):       
        tmp = lookup.get_template("index.html")
        return tmp.render(**dict(title="Welcome"))   
    index.exposed = True
       
    @validate(form=post_form, error_handler="index")
    def save(self, *args, **kwds):
        return "submitted: %s %s" % (args, kwds)
    save.exposed = True

## using this code below for the form validation, adapted code from pylonshf.py

def valid(controller, form=None, validators=None):
    errors = {}
    state = type('State', (object,), {'_':staticmethod(framework.translator)})

    cherrypy.log("validating: %s" % (cherrypy.request.params),
                  context='', severity=logging.DEBUG, traceback=False)
    if form:
        try:
            controller.form_result = form.validate(cherrypy.request.params, state=state)
        except Invalid, e:
            controller.validation_exception = e
            errors = e.error_dict

    if validators:
        if isinstance(validators, dict):
            if not hasattr(controller, 'form_result'):
                controller.form_result = {}
            for field, validator in validators.iteritems():
                try:
                    controller.form_result[field] = \
                        validator.to_python(decoded[field] or None, state)
                except Invalid, error:
                    errors[field] = error
    if errors:
        controller.errors = errors
        return False
    return True


def validate(form=None, validators=None, error_handler=None):
    def wrapper(func, self, *args, **kwargs):
        if not valid(self, form=form, validators=validators):
            if error_handler:
                raise cherrypy.InternalRedirect(error_handler)
        return func(self, *args, **kwargs)
    return decorator(wrapper)

dbrattli

unread,
Mar 29, 2008, 8:04:04 AM3/29/08
to ToscaWidgets-discuss
Hi,

Here is the code I currently use to get TW working with CP3 without
TG. This still gives you TG style form validation. Maybe this code
could be put into TW so we could improve it together?

-- Dag

from inspect import isclass
from pkg_resources import require
from pkg_resources import resource_stream, resource_listdir,
resource_filename
from decorator import decorator

import os, logging, new, threading

from paste.registry import Registry

import cherrypy

import toscawidgets
from toscawidgets import view
from toscawidgets.mods.base import HostFramework
from toscawidgets.util import RequestLocalDescriptor,
install_framework

from formencode import Invalid

install_framework()

PREFIX = '/toscawidgets'

_first_call_lock = threading.Lock()
_has_registered = False
def url_wrapper(self, url):
# On first call, register all static directories and delete the
# instance method so subsequent calls hit the original class
method with
# no thread lock overhead
_first_call_lock.acquire()
try:
global _has_registered
if not _has_registered:
# Preload tg.include_widgets so they get registered
#for widget in cherrypy.config.get("tg.include_widgets",
[]):
# widget = load_class(widget)
# if isclass(widget):
# widget = widget()

from toscawidgets.resources import registry
for webpath, dir in registry.get_prefixed():
#XXX: the commented line produces wrong paths when TG
app
# not mounted at root. See http://trac.turbogears.org/ticket/1403
#webpath = self.__class__.url(webpath)
webpath = PREFIX + webpath
#print "Registering static directory %r at %r" % (dir,
webpath)
self._register_static_directory(webpath, dir)
del self.url
_has_registered = True
finally:
_first_call_lock.release()
return self.__class__.url(url)

class CherryPyFramework(HostFramework):

@staticmethod
def url(url):
return PREFIX + url

def _register_static_directory(self, webpath, directory):
"""
Registers the given filesystem's directory to be accesible
from
the web"""
directory = os.path.abspath(directory)
self.app.merge({
webpath: {
'tools.staticdir.on' : True,
'tools.staticdir.dir' : directory
}
})

class ToscaWidgetsTool(cherrypy.Tool):
"""Sort-of-emulates TWWidgetsMiddleware + Paste's RegsitryManager.
Takes
care of preparing the hostframework for a request."""

def __init__(self, host_framework=None):
self.host_framework = host_framework

return super(ToscaWidgetsTool,
self).__init__("on_start_resource", self.start)

def start(self):
#try:
environ = cherrypy.request.wsgi_environ
#except:
# from cherrypy.lib.wsgiapp import make_environ
# environ = make_environ()
# cherrypy.request.wsgi_environ = environ

registry = environ.setdefault('paste.registry', Registry())
registry.prepare()
registry.register(toscawidgets.framework, self.host_framework)
self.host_framework.start_request(environ)

def end(self):
try:
environ = cherrypy.request.wsgi_environ
self.host_framework.end_request(environ)
finally:
registry = environ.get('paste.registry', None)
if registry:
registry.cleanup()

def _setup(self):
conf = self._merged_args()
p = conf.pop("priority", None)
if p is None:
p = getattr(self.callable, "priority", self._priority)

cherrypy.request.hooks.attach(self._point, self.callable,
priority=p, **conf)
cherrypy.request.hooks.attach('on_end_request', self.end)


def start_extension(app, stdvars):
engines = view.EngineManager()
engines.load_all(cherrypy.config, stdvars)
host_framework = CherryPyFramework(engines = engines, default_view
= 'genshi', translator=_)
host_framework.app = app

host_framework.url = new.instancemethod(url_wrapper,
host_framework, CherryPyFramework)
cherrypy.log("Loaded HostFramework", 'ToscaWidgets', logging.INFO)

cherrypy.tools.toscawidgets =
ToscaWidgetsTool(host_framework=host_framework)

def validate(form=None, validators=None, error_handler=None,
post_only=True, state_factory=None):
"""This decorator will use valid() to automatically validate
input.

If validation is successful the decorated function will be called
and the
valid result dict will be saved as ``self.form_result``.

Otherwise, the action will be re-run as if it was a GET without
setting
``form_result`` and if the form is redisplayed it will display
errors and
previous input values.

If the decorated method did not originally display the
form, then ``error_handler`` should be the name of the method (in
the same
controller) that originally displayed it.

If you'd like validate to also check GET (query) variables during
its
validation, set the ``post_only`` keyword argument to False.
"""
def wrapper(func, self, *args, **kwargs):
"""Decorator Wrapper function"""
if not valid(self, form=form, validators=validators,
post_only=post_only, state_factory=state_factory):
if error_handler:
environ = cherrypy.request.wsgi_environ
#environ['REQUEST_METHOD'] = 'GET'
#environ['pylons.routes_dict']['action'] =
error_handler
return self._dispatch_call()
return func(self, *args, **kwargs)
return decorator(wrapper)


def valid(controller, form=None, validators=None, post_only=True,
state_factory=None):
"""Validate input using a ToscaWidgetsForms form.

Given a TW form or dict of validators, valid() will attempt to
validate
the form or validator dict as long as a POST request is made. No
validation is performed on GET requests unless post_only is False.

If validation was succesfull, the valid result dict will be saved
as ``controller.form_result`` and valid() will return True.

Otherwise the invalid exception will be stored at
``controller.validation_exception`` and valid() will return False.

If you'd like validate to also check GET (query) variables during
its
validation, set the ``post_only`` keyword argument to False.
"""
request = cherrypy.request

if post_only:
params = request.params.copy()
else:
params = request.params.copy()

errors = {}

if state_factory:
state = state_factory()
else:
from toscawidgets import framework
# Pass registered translator for formencode
state = type('State', (object,),
{'_':staticmethod(framework.translator)})

if form:
try:
controller.form_result = form.validate(params,
state=state)
except Invalid, e:
#cherrypy.log("Validation failed with:\n%s" % e,
'ToscaWidgets', logging.ERROR)
controller.validation_exception = e
errors = e.error_dict

if validators:
if isinstance(validators, dict):
if not hasattr(controller, 'form_result'):
controller.form_result = {}
for field, validator in validators.iteritems():
try:
controller.form_result[field] = \
validator.to_python(decoded[field] or None,
state)
except Invalid, error:
errors[field] = error

controller.errors = errors
if errors:
return False

return True

def error_handler(error_func):
def decorate(func):
def wrapper(controller, *args, **kwargs):
# Check for errors
#print args, kwargs, controller
if hasattr(controller, 'errors') and controller.errors:
# Call error handler
params = error_func(controller, *args, **kwargs)
else:
# Call normal handler
params = func(controller, *args, **kwargs)
return params
return wrapper
return decorate

percious

unread,
Mar 31, 2008, 2:56:50 PM3/31/08
to ToscaWidgets-discuss
I will work on getting this in. Do you have any unit tests for it?

cheers.
-chris
> # not mounted at root. Seehttp://trac.turbogears.org/ticket/1403

Alberto Valverde

unread,
Apr 1, 2008, 3:19:42 AM4/1/08
to toscawidge...@googlegroups.com

You should be able to use TW's middleware + WSGIHostFramework with
CherryPy 3. Stacking it is similar to how it's done in Pylons/TG2. If
you get stuck I'll try to cook up a short recipe later...

Alberto

Rich Barton

unread,
Apr 1, 2008, 10:07:55 AM4/1/08
to toscawidge...@googlegroups.com
thanks! if you don't mind an example on how to stack it would be great.

Alberto Valverde

unread,
Apr 1, 2008, 2:01:45 PM4/1/08
to toscawidge...@googlegroups.com
Rich Barton wrote:
> thanks! if you don't mind an example on how to stack it would be great.

This should work:

cherrypy.tree.mount(controllers.Root(), config='app.cfg')

and app.cfg:

wsgi.pipeline = [('tw', toscawidgets.middleware.make_middleware)]

Alberto

Reply all
Reply to author
Forward
0 new messages