Need help understanding decorators

7 views
Skip to first unread message

BJörn Lindqvist

unread,
Sep 5, 2006, 6:41:40 AM9/5/06
to turbo...@googlegroups.com
Hello.

I'm trying to understand how TurboGears validation of form works.
Specifically, I want to know what TurboGears does "behind the scenes."
I have followed the tutorial on this page:
http://trac.turbogears.org/turbogears/wiki/SimpleWidgetForm. The
relevant code is this:

class Root(RootController):
....
@expose(template=".templates.add")
def add(self, tg_errors=None):
if tg_errors:
flash("There was a problem with the form!")
return dict(form=comment_form)

@expose()
@validate(form=comment_form)
@error_handler(add)
def save(self, name, email, comment, notify=False):
comments.add(name, email, comment)
if notify:
flash("Comment added! You will be notified.")
else:
flash("Comment added!")
raise redirect("index")

What I'm wondering is how does the decorators validate and
error_handler really work? Validate seem to magically change the
arguments to save() to python objects instead of text strings. Then
the error_handler decorator I think makes a HTTPRedirect back to add()
if validation fails. But how does it manage to display the error
messages in the form? How does the form know that it should display
the values that you wrote there previously when you are redirected
back?

I would like to implement code similar to the example above, but
without using a form widget. And preferably without using the validate
and error_handler decorators. Maybe someone can show me an example of
that?

--
mvh Björn

Karl Guertin

unread,
Sep 5, 2006, 8:40:29 AM9/5/06
to turbo...@googlegroups.com
On 9/5/06, BJörn Lindqvist <bjo...@gmail.com> wrote:
> What I'm wondering is how does the decorators validate and
> error_handler really work?

Do not ask how sausage is made! Actually, if you _really_ want to know
how it all works, you'll have to read the source. There's more code
involved than I can reasonably cover in an email. You'll want to look
at controllers.py, errorhandling.py and widgets/forms.py.

> Validate seem to magically change the
> arguments to save() to python objects instead of text strings.

This is done by calling FormEncode behind the scenes. Formencode has
documentation but the source is pretty clear.

>Then
> the error_handler decorator I think makes a HTTPRedirect back to add()
> if validation fails.

I'm pretty sure that there isn't a HTTPRedirect involved, the add()
method is called by the error handling code and tg_errors is injected
into the parameters list.

> But how does it manage to display the error
> messages in the form?

tg_errors is a dictionary of formencode.Invalid instances. The form
widget checks this error dict while building the form and generates
the message by str()'ing the invalid instance.

> How does the form know that it should display
> the values that you wrote there previously when you are redirected
> back?

The values are still in the CherryPy environmental variables, the
widgets pull their data from there if they aren't passed in data.

> I would like to implement code similar to the example above, but
> without using a form widget. And preferably without using the validate
> and error_handler decorators. Maybe someone can show me an example of
> that?

The pieces really are designed to work together. You can use them
separately, but building this system has consumed most of the
development effort in TurboGears thus far.

I'm curious what you can't accomplish by re-templating the current system.

--
Karl

Alberto Valverde

unread,
Sep 5, 2006, 2:29:19 PM9/5/06
to turbo...@googlegroups.com

Depends on your definition of "magic" and your understanding of
decorators.... ;)
A decorator is basically a wrapper around a function [1]. What
validate more or less does when it decorates your method is something
like:

def validate(*args, **kw):
params = make_all_args_kw_args(args, kw)
try:
params = validate_params_with_your_form(params)
except ValidationFailed, exception:
# Opps, bad input. Let's call the method which shall handle errors
# (as registered by one or many "error_handler" decorators)
return dispatch_the_exception_using_error_handling
(your_controller_method, params, exception)
else:
# validation passed and params contain good python objects. Let's
put them back
# into args and kw and call the decorated method
args, kw = restore_original_args_and_kw(params)
return your_controller_method(*args, **kw)

Disclaimer: *Highly* simplified, but you can get an idea.... The
whole story lives here [2]

> Then
> the error_handler decorator I think makes a HTTPRedirect back to add()
> if validation fails. But how does it manage to display the error
> messages in the form?

Not exactly: error_handler only appends rules to the
"dispatch_the_exception_using_error_handling" generic function (which
is actually called "dispatch_error"). As you can see in the pseudo-
code, It's dispatch_error who actually calls the function you
registered using "error_handler" when validation fails.

This is highly simplified: "dispatch_error" can actually also catch
uncaught exceptions raised inside your controller method and can be
**very** flexible and **very** powerful. For example, you can define
rules to catch any kind of exception inside any controller method to
log it or give a custom error page. Or you can log specific Invalid
exceptions to see where your users most often make mistakes in your
forms. Or you can catch TurboPeakSecurity's (hey! it's been long
since I don't self-promote myself!... ;)) SecurityDenial exceptions
when some unauthorized action wants to take place [3], or... it's
really up to your imagination once you get your brain around
errorhandling and generic functions....

Honestly, I think errorhandling is one of TG's slickest feature and
the form_widgets/errorhandling combo one of TG's strongest asset
(Mark, you can quote me on this for the book. ;) )

> How does the form know that it should display
> the values that you wrote there previously when you are redirected
> back?

Again, it's not actually a HTTP redirect so you're in the same
request the input came in. This allows us to use cherrypy.request to
store all this information: The resulting validation exception is
stored in "cherrypy.request.validation_exception", the unpacked
exception is stored as a (possibly nested) dict in
"cherrypy.request.validation_errors" and the input values in
"cherrypy.request.input_values".

When the form is displayed it checks these request variables to see
if it's being redisplayed [4], if it is, it just ignores the value
your controller gave it, displays the values the user submitted and
the errors along the fields.

> I would like to implement code similar to the example above, but
> without using a form widget. And preferably without using the validate
> and error_handler decorators. Maybe someone can show me an example of
> that?

Well, good luck ;) As Karl pointed out, all this magic took quite a
lot of hard work and time to design, implement, test and debug by the
TG community to free you from writing all the boiler-plate code. If
you really want to do it without using widgets, errorhandling and
decorators there's absolutely nothing from stopping you. You could do
more or less the same stuff with something like:


def show_form(self, errors=None, input_value=None):
# build your form, populate it, etc.... here
return dict(....)

def process_form(self, **kw)
try:
kw = your_schema.validate(kw)
except Invalid, e:
errors = e.unpack_errors()
return self.show_form(errors=errors, input_value=kw)
# good python objects @ kw from now on... do something useful
# with them

However, I'm sure you'll appreciate how simpler and slicker your
controller methods will get when leaving all the boring stuff to
TG's "magic" internals when you get a hang of it (after implementing
a couple of these boring show_form/process_form pairs... ;) )

HTH,
Alberto

[1] http://en.wikipedia.org/wiki/Decorator_pattern
[2] http://trac.turbogears.org/turbogears/browser/branches/1.0/
turbogears/controllers.py#L101
[3] http://trac.toscat.net/TurboPeakSecurity/file/trunk/tpsecurity/
checkers.py (grep for "handle_unauthorized_access", sorry old Trac
version with no line-number support...)
[4] http://trac.turbogears.org/turbogears/browser/branches/1.0/
turbogears/widgets/forms.py#L194

BJörn Lindqvist

unread,
Sep 6, 2006, 5:04:34 AM9/6/06
to turbo...@googlegroups.com
On 9/5/06, Karl Guertin <gray...@gmail.com> wrote:
>
> On 9/5/06, BJörn Lindqvist <bjo...@gmail.com> wrote:
> > What I'm wondering is how does the decorators validate and
> > error_handler really work?
>
> Do not ask how sausage is made! Actually, if you _really_ want to know
> how it all works, you'll have to read the source. There's more code
> involved than I can reasonably cover in an email. You'll want to look
> at controllers.py, errorhandling.py and widgets/forms.py.

I tried that method before asking the list. :)

> >Then
> > the error_handler decorator I think makes a HTTPRedirect back to add()
> > if validation fails.
>
> I'm pretty sure that there isn't a HTTPRedirect involved, the add()
> method is called by the error handling code and tg_errors is injected
> into the parameters list.

Yes, I noticed now. It seems wrong that the URL changes to /save but
the page is still showing the /add page.

> > But how does it manage to display the error
> > messages in the form?
>
> tg_errors is a dictionary of formencode.Invalid instances. The form
> widget checks this error dict while building the form and generates
> the message by str()'ing the invalid instance.

I'm probably wrong, but I think you are wrong. If I modify the
original examples add() method to the following:

@expose(template=".templates.add")
def add(self):
return dict(form=comment_form)

I.e, I remove the tg_errors parameter. It still works so tg_errors
seem to be unused by the widgets. FormEncodes error messages must be
passed to the widget using some other mechanism.

> > How does the form know that it should display
> > the values that you wrote there previously when you are redirected
> > back?
>
> The values are still in the CherryPy environmental variables, the
> widgets pull their data from there if they aren't passed in data.

TurboGears stuffs it in request.input_values and request.validation_errors?

> > I would like to implement code similar to the example above, but
> > without using a form widget. And preferably without using the validate
> > and error_handler decorators. Maybe someone can show me an example of
> > that?
>
> The pieces really are designed to work together. You can use them
> separately, but building this system has consumed most of the
> development effort in TurboGears thus far.
>
> I'm curious what you can't accomplish by re-templating the current system.

A few reasons. I want multiple methods that share a save() method:

...
def add_stuff_one_way(self):
return dict(...)

def add_stuff_another_way(self):
return dict(...)

def save(self, ....):
save called from either add_stuff_one_way() or add_stuff_another_way()

In a setup like this, the @error_handler decorator doesn't work
because it only takes one argument.

The other reason is that I don't want to use TurboGears widgets, and
@validate and @error_handler seem to be tightly coupled to the widget
system.

--
mvh Björn

Krys

unread,
Sep 6, 2006, 7:36:50 AM9/6/06
to TurboGears
BJörn Lindqvist wrote:
>
> A few reasons. I want multiple methods that share a save() method:
>
> ...
> def add_stuff_one_way(self):
> return dict(...)
>
> def add_stuff_another_way(self):
> return dict(...)
>
> def save(self, ....):
> save called from either add_stuff_one_way() or add_stuff_another_way()
>
> In a setup like this, the @error_handler decorator doesn't work
> because it only takes one argument.
>
> The other reason is that I don't want to use TurboGears widgets, and
> @validate and @error_handler seem to be tightly coupled to the widget
> system.
>
> --
> mvh Björn

Actually, validate is tightly coupled with validators, not widgets
specifically. You can use validate without error_handler and just add
a tg_errors keyword parameter to your save method. It can then do
something approprate, like call the correct original add method (hidden
field in the form perhaps?). I have often found it useful to use
validate without error_handler for just this reason. (Though I am
probably not using error_handler to it's fullest.)

Also, validate does not need to take a form as a parameter. You can
feed it a validators keyword argument with just a list of validators.
No widgets necessary. Though you probably already knew this from
reading the source.

Or, as previously mentioned, you can do it all by hand.

I guess I'm just saying that you can use some parts and not the others
and you don't have to throw out all the validation functionality that
TG has.

Anyway, hope this helps,
Krys Wilken

Alberto Valverde

unread,
Sep 6, 2006, 7:39:38 AM9/6/06
to turbo...@googlegroups.com

On Sep 6, 2006, at 11:04 AM, BJörn Lindqvist wrote:

>
> On 9/5/06, Karl Guertin <gray...@gmail.com> wrote:
>>
>> On 9/5/06, BJörn Lindqvist <bjo...@gmail.com> wrote:
>>> What I'm wondering is how does the decorators validate and
>>> error_handler really work?
>>
>> Do not ask how sausage is made! Actually, if you _really_ want to
>> know
>> how it all works, you'll have to read the source. There's more code
>> involved than I can reasonably cover in an email. You'll want to look
>> at controllers.py, errorhandling.py and widgets/forms.py.
>
> I tried that method before asking the list. :)
>
>>> Then
>>> the error_handler decorator I think makes a HTTPRedirect back to
>>> add()
>>> if validation fails.
>>
>> I'm pretty sure that there isn't a HTTPRedirect involved, the add()
>> method is called by the error handling code and tg_errors is injected
>> into the parameters list.
>
> Yes, I noticed now. It seems wrong that the URL changes to /save but
> the page is still showing the /add page.

You could store the errors and input values in a cookie/session and
issue a HTTP Redirect if this really bothers you. However, I find it
easier to pass them around at request.X and calling the error
handling method from within the method that received input. YMMV
however.

>
>>> But how does it manage to display the error
>>> messages in the form?
>>
>> tg_errors is a dictionary of formencode.Invalid instances. The form
>> widget checks this error dict while building the form and generates
>> the message by str()'ing the invalid instance.
>
> I'm probably wrong, but I think you are wrong. If I modify the
> original examples add() method to the following:
>
> @expose(template=".templates.add")
> def add(self):
> return dict(form=comment_form)
>
> I.e, I remove the tg_errors parameter. It still works so tg_errors
> seem to be unused by the widgets. FormEncodes error messages must be
> passed to the widget using some other mechanism.

Refer to my prev. post where I tried my best to explain this as
clearly as my English allows me.

>
>>> How does the form know that it should display
>>> the values that you wrote there previously when you are redirected
>>> back?
>>
>> The values are still in the CherryPy environmental variables, the
>> widgets pull their data from there if they aren't passed in data.
>
> TurboGears stuffs it in request.input_values and
> request.validation_errors?

As I mentioned in my prev. post in this thread, yes. tg_errors ==
request.validation_errors. tg_errors can be used in the method that
is target of error_handler as a parameter to do something with them
when they're not None.

You can also omit error_handler altogether by giving a tg_errors
parameter to the method that is decorated with validate. If you do
so, this method will become it's implicit error_handler.

@validate(...)
def save(self, ..., tg_errors=None):
if tg_errors:
# Errors coerced, do something useful
else:
# No errors, work with the good, coerced values.


>
>>> I would like to implement code similar to the example above, but
>>> without using a form widget. And preferably without using the
>>> validate
>>> and error_handler decorators. Maybe someone can show me an
>>> example of
>>> that?

Refer to the my prev. post where I gave a pseudo-code example of
exactly this.

>>
>> The pieces really are designed to work together. You can use them
>> separately, but building this system has consumed most of the
>> development effort in TurboGears thus far.
>>
>> I'm curious what you can't accomplish by re-templating the current
>> system.
>
> A few reasons. I want multiple methods that share a save() method:
>
> ...
> def add_stuff_one_way(self):
> return dict(...)
>
> def add_stuff_another_way(self):
> return dict(...)
>
> def save(self, ....):
> save called from either add_stuff_one_way() or
> add_stuff_another_way()
>
> In a setup like this, the @error_handler decorator doesn't work
> because it only takes one argument.

Wrong. Did you really read the source? ;) You can accomplish exactly
what you're trying to do by specializing error_handler:

@error_handler(add_stuff_one_way, "another_way in tg_errors")
@error_handler(add_stuff_one_way, "one_way in tg_errors")
def save(self, ..):
...

Now you only need a chained_validator which adds a "one_way" or
"another_way" to the error_dict if it finds any errors in it (this is
left as an exercise to the reader). Might be hacky, but works....

> The other reason is that I don't want to use TurboGears widgets, and
> @validate and @error_handler seem to be tightly coupled to the widget
> system.

Wrong again. "validate" is tightly coupled with FormEncode *only*.
You can skip widgets altogether and use plain FE validators with it:

@validate(validator={'param1':Int, 'param2', WhatEver})
or
@validate(validator=Schema(....))

If you look at validate's source, you can even notice that form
doesn't have to be a widget at all. You can pass any object that has
a "validate" method and raises FE's Invalids on wrong input.

"error_handler" has nothing to do with widgets but with errorhandling.

The only part of this process where the widgets kick in is with the
inspection of request.input_values and request.validation_errors to
display previous values and errors. Nothing is preventing you from
rolling your own mechanism to do the same, using errorhandling and
avoiding widgets altogether.

Alberto

Alberto

unread,
Sep 6, 2006, 11:00:07 AM9/6/06
to TurboGears
Alberto Valverde wrote:
> Wrong. Did you really read the source? ;) You can accomplish exactly
> what you're trying to do by specializing error_handler:
>
> @error_handler(add_stuff_one_way, "another_way in tg_errors")
> @error_handler(add_stuff_one_way, "one_way in tg_errors")
> def save(self, ..):
> ...
>
> Now you only need a chained_validator which adds a "one_way" or
> "another_way" to the error_dict if it finds any errors in it (this is
> left as an exercise to the reader). Might be hacky, but works....

Opps, missed the straightest path to destinaton: This can be
accomplished without any custom chained validators or any dirty tricks
by inspecting "kw" for a hidden fields's value. Something like:

@error_handler(add_stuff_one_way, "kw.get('action') == 'one_way'")
@error_handler(add_stuff_another_way, "kw.get('action') ==
'another_way'")
def save(self, ..):

This will branch to "add_stuff_one_way" if the form submitted "one_way"
in the "action" hidden field, same with "add_stuff_another_way".

Did I mention errorhandling's flexibility? ;)

Alberto

Karl Guertin

unread,
Sep 6, 2006, 11:32:44 AM9/6/06
to turbo...@googlegroups.com
On 9/6/06, Alberto <alb...@toscat.net> wrote:
> Opps, missed the straightest path to destinaton: This can be
> accomplished without any custom chained validators or any dirty tricks
> by inspecting "kw" for a hidden fields's value.

I was curious why you weren't inspecting for a hidden field, but I
figured you knew something that I didn't.

BJörn Lindqvist

unread,
Sep 6, 2006, 8:06:08 PM9/6/06
to turbo...@googlegroups.com
Thanks to everyone who has explained! I'm very grateful. Unfortunately
it takes time for me to digest it all, so I only start with the
validation stuff.

On 9/6/06, Alberto Valverde <alb...@toscat.net> wrote:
> > The other reason is that I don't want to use TurboGears widgets, and
> > @validate and @error_handler seem to be tightly coupled to the widget
> > system.
>
> Wrong again. "validate" is tightly coupled with FormEncode *only*.
> You can skip widgets altogether and use plain FE validators with it:
> @validate(validator={'param1':Int, 'param2', WhatEver})
> or
> @validate(validator=Schema(....))

Ok, understood. But, IMHO, the parameter name "form" in the validate()
decorator gives the impression that there is a relationship to some
kind of "form"... I assume you meant to name the keyword parameter
"validators" instead of "validate"?

> If you look at validate's source, you can even notice that form
> doesn't have to be a widget at all. You can pass any object that has
> a "validate" method and raises FE's Invalids on wrong input.

I've tried just that. Here is controllers.py from
http://trac.turbogears.org/turbogears/wiki/SimpleWidgetForm rewritten
to work without widgets:

######################################################################
import time

from cherrypy import request

from turbogears.controllers import (RootController, expose, error_handler,
flash, redirect, validate)
import formencode
from formencode import validators

class Comments(list):
def add(self, name, email, text):
self.append((name, email, text, time.ctime()))

comments = Comments()

class CommentSchema(formencode.Schema):
name = validators.String(not_empty = True)
email = validators.Email(not_empty = True)
comment = validators.String(not_empty = True)
notify = validators.Bool()

class Root(RootController):
@expose(template=".templates.index")
def index(self):
return dict(comments=comments)

@expose(template=".templates.add")
def add(self, tg_errors=None):
if tg_errors:

# If there are errors, then the request has the attributes
# 'input_values' and 'validation_errors'
print request.input_values
print request.validation_errors


flash("There was a problem with the form!")

print tg_errors

return dict(name = "",
email = "",
comment = "",
notify = False)

@expose()
@validate(validators = CommentSchema())


@error_handler(add)
def save(self, name, email, comment, notify=False):
comments.add(name, email, comment)
if notify:
flash("Comment added! You will be notified.")
else:
flash("Comment added!")
raise redirect("index")

######################################################################

I also changed the add.kid template to match Root's add() method. It
renders the four parameters manually instead of using the widgets
display() method. When I run the code and submit the form, I always
get the error "The input field 'self' was not expected"

Also, the attributes input_values and validation_errors aren't
assigned to the request object. Shouldn't they be?

Are these problems bugs or have I fundamentally misunderstood
something?

--
mvh Björn

Karl Guertin

unread,
Sep 6, 2006, 8:34:19 PM9/6/06
to turbo...@googlegroups.com
On 9/6/06, BJörn Lindqvist <bjo...@gmail.com> wrote:
> I also changed the add.kid template to match Root's add() method. It
> renders the four parameters manually instead of using the widgets
> display() method. When I run the code and submit the form, I always
> get the error "The input field 'self' was not expected"

I'm not sure why this is occuring, it looks like the schema is somehow
getting the self parameter that's meant for save. I've never tried
this myself and can't answer this without poking around in code, so
I'll leave the real answer to someone else. ;]

> Also, the attributes input_values and validation_errors aren't
> assigned to the request object. Shouldn't they be?

It's not request, it's cherrypy.request.input_values and
cherrypy.request.validation_errors

> Are these problems bugs or have I fundamentally misunderstood
> something?

I still don't understand what is wrong enough with widgets that the
wheel needs to be reinvented. I know they have limitations but you've
never given a reason to avoid them. I keep asking this because I plan
on trying to document issues people have with the framework.

Karl Guertin

unread,
Sep 6, 2006, 8:52:17 PM9/6/06
to turbo...@googlegroups.com
On 9/6/06, BJörn Lindqvist <bjo...@gmail.com> wrote:
> I also changed the add.kid template to match Root's add() method. It
> renders the four parameters manually instead of using the widgets
> display() method. When I run the code and submit the form, I always
> get the error "The input field 'self' was not expected"

After a bit of thought, I remember the allow_extra_fields
attribute[1]. It doesn't fix the root of your problem, but it does fix
the consequences.

[1] http://formencode.org/class-formencode.schema.Schema.html#allow_extra_fields

Alberto Valverde

unread,
Sep 7, 2006, 12:51:10 AM9/7/06
to turbo...@googlegroups.com

On Sep 7, 2006, at 2:06 AM, BJörn Lindqvist wrote:

>
> Thanks to everyone who has explained! I'm very grateful. Unfortunately
> it takes time for me to digest it all, so I only start with the
> validation stuff.
>
> On 9/6/06, Alberto Valverde <alb...@toscat.net> wrote:
>>> The other reason is that I don't want to use TurboGears widgets, and
>>> @validate and @error_handler seem to be tightly coupled to the
>>> widget
>>> system.
>>
>> Wrong again. "validate" is tightly coupled with FormEncode *only*.
>> You can skip widgets altogether and use plain FE validators with it:
>> @validate(validator={'param1':Int, 'param2', WhatEver})
>> or
>> @validate(validator=Schema(....))
>
> Ok, understood. But, IMHO, the parameter name "form" in the validate()
> decorator gives the impression that there is a relationship to some
> kind of "form"... I assume you meant to name the keyword parameter
> "validators" instead of "validate"?

The keyword parameter is "validators", sorry. I was referring to the
decorator when saying "validate".

>
>> If you look at validate's source, you can even notice that form
>> doesn't have to be a widget at all. You can pass any object that has
>> a "validate" method and raises FE's Invalids on wrong input.
>
> I've tried just that. Here is controllers.py from
> http://trac.turbogears.org/turbogears/wiki/SimpleWidgetForm rewritten
> to work without widgets:

> [....]

> I also changed the add.kid template to match Root's add() method. It
> renders the four parameters manually instead of using the widgets
> display() method. When I run the code and submit the form, I always
> get the error "The input field 'self' was not expected"

It's not a bug really but a consequence of the mangling (util.to_kw
and util.from_kw) "validate" does to the parameters CP injects on
your controller method so errorhandling can work properly. There's
not much really you can do about it if using the "validate" decorator.

However, TG provides a formencode.Schema subclass at
turbogears.validators which you can use to overcome this issue.
Basically you need to set the "allow_extra_fields" and
"filter_extra_fields" attributes to a FE plain Schema so it doesn't
bark and it doesn't let that spurious variables through. This should
be enough if you don't want to subclass turbogears.validators.Schema
(it's a "widgetized" Schema ;) )

class MyBaseSchema(formencode.schema.Schema):
allow_extra_fields = True
filter_extra_fields = True

I strongly recommend filtering extra fields or else bad things could
happen... (spooky! ;) ).

Alberto

Ben Sizer

unread,
Sep 7, 2006, 5:20:24 AM9/7/06
to TurboGears
Karl Guertin wrote:
> I still don't understand what is wrong enough with widgets that the
> wheel needs to be reinvented. I know they have limitations but you've
> never given a reason to avoid them. I keep asking this because I plan
> on trying to document issues people have with the framework.

The main problem with widgets is... *drumroll* documentation.

- The first mention of widget on
http://www.turbogears.org/preview/docs/ talks about Javascript widgets,
before any entry on Widgets more generally; this is likely to make
people think widgets are just about Javascript. (This is apparently
fixed in the new docs, but then the new docs lack the following
page...)

- The following mention is 'Widgets/Forms', which is all well and good
except it doesn't even attempt to say what a widget is until paragraph
20! Even then, the description is somewhat abstract and vague. On a
page about widgets, a good description of a widget should be in
paragraph 1. Let the user know he's looking at the right page.

- The sample code isn't well explained. Why use 'TableForm'? What
alternatives are there to 'TableForm'? What alternatives are there to
TextField?

- Why is the validation section talking about invisible examples? (eg.
"It's fairly obvious that the first field (validators.NotEmpty)is
required", yet that is the only mention of validators.NotEmpty on that
page). Maybe this code is hidden away in the .tgz file, but
documentation has to stop being a collection of tutorials you download
and work through, and start being more like proper reference material.
I should be able to learn from reading, not by being led through 5 or 6
tutorials.

etc. I apologise for the uncompromising nature of my complaints but
sometimes it seems that people do not recognise what the actual problem
with the docs is. I do appreciate it's a lot easier to point out
failings than to fix them, however. I can't speak for the original
poster, but this is certainly why I am unwilling to waste my time on
widgets until there is a decent reference to work with.

--
Ben Sizer

BJörn Lindqvist

unread,
Sep 7, 2006, 7:37:08 PM9/7/06
to turbo...@googlegroups.com
On 9/7/06, Karl Guertin <gray...@gmail.com> wrote:
> > Also, the attributes input_values and validation_errors aren't
> > assigned to the request object. Shouldn't they be?
>
> It's not request, it's cherrypy.request.input_values and
> cherrypy.request.validation_errors

In the code I pasted, I imported request from cherrypy. Doing it your
way, "import cherrypy" and then referring to it like
"cherrypy.request" produces the same error:

print cherrypy.request.input_values
...
AttributeError: 'Request' object has no attribute 'input_values'

> > Are these problems bugs or have I fundamentally misunderstood
> > something?
>
> I still don't understand what is wrong enough with widgets that the
> wheel needs to be reinvented. I know they have limitations but you've
> never given a reason to avoid them. I keep asking this because I plan
> on trying to document issues people have with the framework.

You misunderstand. I'm not trying to reinvent "widgets" because I
think objects bunding html templates are pretty useless. For each
non-trivial widget, there are so many customizations you want to make
that aiming for reuse isn't worth it. Plus, widgets use Kid which I
dislike, are undocumented and have weird apis forcing you to do things
in strange ways. IMHO...

--
mvh Björn

Reply all
Reply to author
Forward
0 new messages