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