Dealing with undefined values in Kid

4 views
Skip to first unread message

Kevin Dangoor

unread,
Sep 23, 2005, 9:08:44 AM9/23/05
to turbo...@googlegroups.com
On 9/23/05, bon...@gmail.com <bon...@gmail.com> wrote:
>
> Thanks. I found a solution, vars().has_key('my_key')

Yep. This *seriously* needs a better way. A good solution is to make a
function that is always available that lets you return a default value
if the variable doesn't exist. We can use Cheetah's name, if people
like it: getVar. Choosing a different name may be better, though,
because I'm thinking the behavior can be a little nicer...

<div py:if="foobar">
raises an exception if foobar does not exist

<div py:if="getVar('foobar')">
returns None (and doesn't display the block) if foobar does not exist

<div py:if="getVar('foobar', True)">
returns True (or whatever that second parameter is) if the variable
doesn't exist.

Note that I'm proposing getVar (or some other name) that would not be
defined as a "standard variable" (the ones that are prefixed with
std.) Standard variables are discussed here:
http://www.turbogears.org/docs/gettingstarted.html#predefined
for people who haven't encountered them yet.

Kevin

bon...@gmail.com

unread,
Sep 23, 2005, 9:34:20 AM9/23/05
to TurboGears
thanks for the info.

Is stdvars and turbogears.view.variableProviders a per session/thread
one ? Or in other words, if I register a function to variableProviders,
would the function see cherrypy.session when it is called ?

My usage(as mentioned in another thread) is that I have a header
template that is being included in all templates. This template
contains some user information(typical like gmail/yahoo mail) that
depends on the current logon user.

Currently, I use a decorator before turbogears.expose so it would
"enrich" the returned dict and pass it on to turbogears.expose. But
this can be ugly when I have say 50 such functions, especially when I
have yet another check_login wrapper.

Kevin Dangoor

unread,
Sep 23, 2005, 10:29:58 AM9/23/05
to turbo...@googlegroups.com
On 9/23/05, bon...@gmail.com <bon...@gmail.com> wrote:
> Is stdvars and turbogears.view.variableProviders a per session/thread
> one ? Or in other words, if I register a function to variableProviders,
> would the function see cherrypy.session when it is called ?

stdvars is created on every request. That means that your
variableProviders are called on every request, so you have access to
all of the cherrypy request-specific stuff.

> My usage(as mentioned in another thread) is that I have a header
> template that is being included in all templates. This template
> contains some user information(typical like gmail/yahoo mail) that
> depends on the current logon user.

Sounds like a good common case.

>
> Currently, I use a decorator before turbogears.expose so it would
> "enrich" the returned dict and pass it on to turbogears.expose. But
> this can be ugly when I have say 50 such functions, especially when I
> have yet another check_login wrapper.

Authentication is a common enough need that I'd like to get the
codified into something that's easy to get working and apply as
needed. Doing it at the individual method level is dangerous...
consider:

class MyImportantData:
@turbogears.expose(html="blah")
@authenticate
def index(self):
# display data

@turbogears.expose(html="blahblah")
@authenticate
def edit(self, id):
# display edit form

@turbogears.expose()
def save(self, id, data):
# OOPS! anyone can save

There are authentication filters on the CherryPy site, and a filter
may be the right way to go here. We can definitely improve the
usability and out-of-the-box experience, though.

Kevin

--
Kevin Dangoor
Author of the Zesty News RSS newsreader

email: k...@blazingthings.com
company: http://www.BlazingThings.com
blog: http://www.BlueSkyOnMars.com

bon...@gmail.com

unread,
Sep 23, 2005, 10:45:39 AM9/23/05
to TurboGears
Yes, authentication is quite tricky.

While a filter seems to be the right way, there are cases where not all
links need authentication, say the about page, the license page, the
contact page etc. But if it is done on a per page way(what I am doing
right now), it would happen that one function miss the decorator, it
has problem you highlighted.

Beside, the problem of filter is that the look can be ugly and not in
the same scheme as the app. For example, HTTP has its own
authentication protocol but very few people use it in application
because it looks odd.

Bob Ippolito

unread,
Sep 23, 2005, 12:15:16 PM9/23/05
to turbo...@googlegroups.com, Kevin Dangoor

On Sep 23, 2005, at 9:08 AM, Kevin Dangoor wrote:

>
> On 9/23/05, bon...@gmail.com <bon...@gmail.com> wrote:
>
>>
>> Thanks. I found a solution, vars().has_key('my_key')
>>
>
> Yep. This *seriously* needs a better way. A good solution is to make a
> function that is always available that lets you return a default value
> if the variable doesn't exist. We can use Cheetah's name, if people
> like it: getVar. Choosing a different name may be better, though,
> because I'm thinking the behavior can be a little nicer...

I find it really strange that Kid doesn't have this, TAL has "exists:".

-bob

Ian Bicking

unread,
Sep 23, 2005, 12:25:11 PM9/23/05
to turbo...@googlegroups.com, Kevin Dangoor
Bob Ippolito wrote:
>>> Thanks. I found a solution, vars().has_key('my_key')
>>>
>>
>> Yep. This *seriously* needs a better way. A good solution is to make a
>> function that is always available that lets you return a default value
>> if the variable doesn't exist. We can use Cheetah's name, if people
>> like it: getVar. Choosing a different name may be better, though,
>> because I'm thinking the behavior can be a little nicer...
>
>
> I find it really strange that Kid doesn't have this, TAL has "exists:".

TAL also has |, which I use more often. "expr1 | expr2" translates to:

try:
expr1
except (NameError, AttributeError, KeyError):
expr2

But then Kid doesn't have anything like TALES (the expression syntax),
it's all pure Python.

--
Ian Bicking / ia...@colorstudy.com / http://blog.ianbicking.org

Phillip J. Eby

unread,
Sep 24, 2005, 3:11:54 PM9/24/05
to TurboGears

Kid belongs to the family of template languages that assume you have
some type of code that's passing data to the template, and that this is
where it's best handled. ZPT is in the family of template languages
that assume the template is self-contained and usable by itself. (Btw,
it's TALES, not TAL that defines "exists:".)

But beyond that, Kid is a DSL to generate Python code that produces XML
output. It might make more sense to ask why Python doesn't have Perl's
'defined()', since Kid just exposes Python here. :) In contrast, the
TALES expression language is intended to support "web-ish" things as
well as pure Python.

The tradeoff between Kid and a DSL that defines its own expression
language (like ZPT or PWT) is that with Kid you already know the
language, but the custom language is probably easier to use for
anything that the DSL was designed to work well for. So Kid is maybe
more flexible than ZPT or PWT in this area, but potentially harder to
use as a result.

The tradeoff really has to get resolved in the wrapper code. If I were
going to do a lot with Kid, I'd probably end up creating wrapper code
that managed this stuff so the template could just do 'py:if="foo is
not None"' or 'py:if="have_foo"' and not worry about variables maybe
not existing. More specifically, I'd make it so I didn't have to write
a boilerplate wrapper for each and every template. At some point I'll
probably do this for peak.web, so that I can replace PWT with Kid.

Ian Bicking

unread,
Sep 24, 2005, 4:25:17 PM9/24/05
to turbo...@googlegroups.com
Phillip J. Eby wrote:
>>>>Thanks. I found a solution, vars().has_key('my_key')
>>>
>>>Yep. This *seriously* needs a better way. A good solution is to make a
>>>function that is always available that lets you return a default value
>>>if the variable doesn't exist. We can use Cheetah's name, if people
>>>like it: getVar. Choosing a different name may be better, though,
>>>because I'm thinking the behavior can be a little nicer...
>>
>>I find it really strange that Kid doesn't have this, TAL has "exists:".
>
>
> Kid belongs to the family of template languages that assume you have
> some type of code that's passing data to the template, and that this is
> where it's best handled. ZPT is in the family of template languages
> that assume the template is self-contained and usable by itself. (Btw,
> it's TALES, not TAL that defines "exists:".)

Maybe to make it more clear, TAL is the special attributes in ZPT that
modify the DOM(-ish object), while TALES is the expression syntax inside
those attributes that gives the values to TAL. And METAL is the
preprocessor (that uses TALES) that runs before TAL. Anyway, it might
be useful to understand how a similar language factors out the parts.

> But beyond that, Kid is a DSL to generate Python code that produces XML
> output. It might make more sense to ask why Python doesn't have Perl's
> 'defined()', since Kid just exposes Python here. :) In contrast, the
> TALES expression language is intended to support "web-ish" things as
> well as pure Python.

TALES (in my mind, at least) is most meant to insulate template writers
from the Python object model, specifically the distinction between a.b,
a.b(), and a['b']. It also has somewhat different (and simpler) quoting
rules. This is a feature shared by Cheetah, Django's templates, and I
guess PWT (and maybe others I don't know about).

I'm not sure about the details, but Django I believe has Dumb Templates
(no full Python expression support), and Cheetah actually parses and
rewrites general Python expressions, and TALES requires an explicit
escape to use general Python.

TALES is one of a couple expression syntaxes allowed -- the other two I
can think of is string: (which is a fixed string with interpolation),
and python: (which is Python ;). Then there's operators that work on
any of these, specifically not: (the not you know and love), exists: (a
try:except (NameError, AttributeError, KeyError), essentially), and |
(kind of like an exists-or that I described before). Well, there might
be more to it, I'm not sure -- there's always the issue of catching
errors you didn't intend in these cases, like when a.b exists, but a.b()
raises an AttributeError (or str(a.b) raises an AttributeError).

> The tradeoff between Kid and a DSL that defines its own expression
> language (like ZPT or PWT) is that with Kid you already know the
> language, but the custom language is probably easier to use for
> anything that the DSL was designed to work well for. So Kid is maybe
> more flexible than ZPT or PWT in this area, but potentially harder to
> use as a result.

I wouldn't say more flexible, though it is simpler. Well, if you don't
have an escape to get to Real Python, that is less flexible, and IMHO it
is flawed. Enforcing "good" template design by not even transitionally
allowing fully general Python logic into the template is bad. However,
there are subtle complications to DSLs. It is unusual that I can write
a ZPT template without resorting to python:, at which point the
simplicity of TALES is rather lost, because it's no longer
comprehensive. That might not be true if TALES had some more operators,
like "and" and "or".

And while a DSL insulates the template programmer, it also creates a
divide where they speak a different language from the application
programmer. Sometimes it's worth a little complexity up front to get
everyone on the same page. And Python has the features it has for good
reasons. "or" is useful! Something simpler than Python is highly
likely to leave out useful features, and template programmers will need
those features eventually. Even if only because the application
programmer isn't available and They Need Something Done Now.

Ryan Tomayko

unread,
Sep 25, 2005, 2:56:24 AM9/25/05
to turbo...@googlegroups.com, Kevin Dangoor
Kid makes all Template instance attributes locals when evaluating
template code. This has a couple of implications here. First, you can
use hasattr or getattr for most of what you'd want from a "defined()"
method:

<div py:if="hasattr(self, 'foo')">
${foo}
</div>

Or, perhaps:

<div content="getattr(self, 'foo', 'default value')">

Second, we could add a methods to kid.Template that make the self
implicit:

class Template:
def defined(self, name):
return hasattr(self, name)

def value_of(self, name, default=None):
return getattr(self, name, default)

I come across this problem so infrequently that adding a "defined"
method seems like excess. But adding four lines of code to that
Template class isn't going to cause me to lose any sleep either.

Ryan Tomayko
rtom...@gmail.com
http://naeblis.cx/rtomayko/
http://lesscode.org/



Kevin Dangoor

unread,
Sep 26, 2005, 4:20:32 PM9/26/05
to Ryan Tomayko, turbo...@googlegroups.com
On 9/25/05, Ryan Tomayko <rtom...@gmail.com> wrote:
> Kid makes all Template instance attributes locals when evaluating
> template code. This has a couple of implications here. First, you can
> use hasattr or getattr for most of what you'd want from a "defined()"
> method:
>
> <div py:if="hasattr(self, 'foo')">
> ${foo}
> </div>
>
> Or, perhaps:
>
> <div content="getattr(self, 'foo', 'default value')">
>
> Second, we could add a methods to kid.Template that make the self
> implicit:
>
> class Template:
> def defined(self, name):
> return hasattr(self, name)
>
> def value_of(self, name, default=None):
> return getattr(self, name, default)
>
> I come across this problem so infrequently that adding a "defined"
> method seems like excess. But adding four lines of code to that
> Template class isn't going to cause me to lose any sleep either.

This was a great bit of info for how kid templates work. I hadn't
looked closely at the code, so this was helpful.

Particularly in "master" sorts of templates, I do run into the
"defined" sort of thing. Personally, I'd vote for putting in the 4
lines of code. hasattr(self, "foo") is indeed pythonic, but until now
I didn't realize that the variables were all present in self.

The docs could note that hasattr(self, "foo") works as well as defined("foo").
Reply all
Reply to author
Forward
0 new messages