Possible bug with Kid & Widgets

1 view
Skip to first unread message

Sean Jamieson

unread,
Jul 17, 2006, 3:12:22 PM7/17/06
to TurboGears
I'm trying to create a very simple widget to represent a menu box with
links. Theoretically this should have been fairly straight forward, but
I'm getting an error that doesn't seem to be caused by my widget or
template.
Here is the error, as generated by ipython:

/var/www/Test-Site/<console>

/usr/lib/python2.4/site-packages/TurboGears-0.9a4-py2.4.egg/turbogears/widgets/base.py
in display(self, value, convert, **template_vars)
156 template_vars["value"] =
to_unicode(self.adjust_value(value, convert))
157 self.update_data(template_vars)
--> 158 return view.transform(template_vars,
template=self.template_c)
159
160 def retrieve_javascript(self):

/usr/lib/python2.4/site-packages/TurboGears-0.9a4-py2.4.egg/turbogears/view/base.py
in transform(info, template)
134 "Create ElementTree representation of the output"
135 engine, template, enginename = _choose_engine(template)
--> 136 return engine.transform(info, template)
137
138 def loadBaseTemplates():

/usr/lib/python2.4/site-packages/TurboKid-0.9.3-py2.4.egg/turbokid/kidsupport.py
in transform(self, info, template)
168 options = self.options
169 if options.get("kid.i18n.runTemplateFilter", False):
170 t._filters+=[options.get("kid.i18n_filter")]
--> 171 return ElementStream(t.transform()).expand()
172

/usr/lib/python2.4/site-packages/kid-0.9-py2.4.egg/kid/pull.py in
expand(self)
109 last.tail = item
110 else:
--> 111 current.text = item
112 if isinstance(last, list):
113 return last[0]

AttributeError: 'list' object has no attribute 'text'


I don't know what 'current' is, or why it's a list here. This seems to
be something wrong with the internals of kid. But it certanly happens
when I do menu.render() or menu.display().

I get this same error from the running application, or from console,
console backtrace is much shorter since I call menu.display() directly,
but the last few bits are the same.

My widget is simply this:

class MenuBlock( widgets.Widget ):
params = ( 'title', 'links' )
template = '''
<menu py:strip="True" xmlns:py="http://purl.org/kid/ns#">
<div class="title">$title</div>
<div class="body" py:for="link in links">
<a href="${link[0]}">${link[1]}</a>
<span py:if="len(link) > 2">${link[2]}</span>
</div>
</menu>
'''

And it's used in the master.kid file as:
<div id="menu">
<div class="block" py:for="menu in view.menu">
${menu.display()}
</div>
</div>


Thanks in advance for any help,
Sean

Michele Cella

unread,
Jul 17, 2006, 3:40:00 PM7/17/06
to TurboGears
Hi Sean,

this is a know issue with kid:

http://www.kid-templating.org/trac/ticket/145

hopefully it will be fixed shortly.

Basically you can't use py:strip on the very first tag.

Ciao
Michele

Donald Ball

unread,
Jul 17, 2006, 4:29:19 PM7/17/06
to TurboGears
Hey guys, boy, do I feel stupid today. I'm trying to coerce turbogears/kid
into returning my pages as application/xhtml+xml so that they can be
parsed via XmlHttpRequest.responseXML and I can use that fancy
MochiKit.DOM.swapDOM function. Basically, I'm writing a little poll tool
and want users to have the option to have the form submitted via AJAX and
the poll results show up inline in place of the form.

Sadly, nothing is working, apparently because my app keeps returning the
results as text/html. I've changed the .cfg files to contain:

kid.outputformat="xhtml"

and edited master.kid and the other relevant kid files to contain the
basic preamble:

<?xml version="1.0"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<?python import sitetemplate ?>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://purl.org/kid/ns#" py:extends="sitetemplate">

<head>
<meta content="application/xhtml+xml; charset=UTF-8"
http-equiv="content-type" py:replace="''"/>

(One wonders both why the kid templates don't contain the xml header if
they use the xhtml dtd and namespace, and why the kid templates even
bother with content-type meta header elements instead of setting those
values in the actual heders... but that's neither here nor there ;))

Any suggestions or gotchas for me? Thanks muchly.

- donald

Christopher Arndt

unread,
Jul 17, 2006, 6:12:26 PM7/17/06
to turbo...@googlegroups.com
Donald Ball schrieb:

> Hey guys, boy, do I feel stupid today. I'm trying to coerce turbogears/kid
> into returning my pages as application/xhtml+xml so that they can be
> parsed via XmlHttpRequest.responseXML and I can use that fancy
> MochiKit.DOM.swapDOM function. Basically, I'm writing a little poll tool
> and want users to have the option to have the form submitted via AJAX and
> the poll results show up inline in place of the form.

Don't send the poll results back as xml, use loadJSONDoc, send your SQLObject
result set converted into JSON objects and then contruct the HTML (probably a
table) dynamically with the Mochikit DOM functions.

Use at the Mochikit AJAX table example as a guideline for the general approach.

Chris

Donald Ball

unread,
Jul 17, 2006, 6:25:49 PM7/17/06
to turbo...@googlegroups.com
On Mon, 17 Jul 2006, Christopher Arndt wrote:

>
> Donald Ball schrieb:
>> Hey guys, boy, do I feel stupid today. I'm trying to coerce turbogears/kid
>> into returning my pages as application/xhtml+xml so that they can be
>> parsed via XmlHttpRequest.responseXML and I can use that fancy
>> MochiKit.DOM.swapDOM function. Basically, I'm writing a little poll tool
>> and want users to have the option to have the form submitted via AJAX and
>> the poll results show up inline in place of the form.
>
> Don't send the poll results back as xml, use loadJSONDoc, send your SQLObject
> result set converted into JSON objects and then contruct the HTML (probably a
> table) dynamically with the Mochikit DOM functions.

That would work, but the formatting for the polls and their results need
to be easily customizable. That's far, far easier to do if I'm using kid
templates than if I'm constructing the HTML tree client-side. My page
designers can handle editing kid, asking them to edit javascript functions
isn't realistic.

(Also, stylistically, I mislike requiring a javascript interpreter to
render the page. I like my webapps to still function in the absence of
one. But that's just me being pedantic.)

fwiw, i can make this work on a per-method basis by exposing the format
and content_type attributes, but that's a bit annoying. still having
trouble getting XmlHttpRequest.responseXML to work, but the Content-type
header is coming across as text/xml now, so it's not kid/turbogears's
problem any longer.

- donald

Jorge Godoy

unread,
Jul 17, 2006, 6:44:24 PM7/17/06
to turbo...@googlegroups.com
Donald Ball <ba...@fiendz.org> writes:

> That would work, but the formatting for the polls and their results need
> to be easily customizable. That's far, far easier to do if I'm using kid
> templates than if I'm constructing the HTML tree client-side. My page
> designers can handle editing kid, asking them to edit javascript functions
> isn't realistic.
>
> (Also, stylistically, I mislike requiring a javascript interpreter to
> render the page. I like my webapps to still function in the absence of
> one. But that's just me being pedantic.)

What I do here is using Kid's "fragment" option and then using innerHTML on
the JavaScript side.

Something like:

@turbogears.expose(fragment = True, html = 'marketwatch.templates.some_example')
def fragment_returning_method(self, *args, **kwargs):
return dict(form = my_form)

And in your template:

<div py:strip="True" xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://purl.org/kid/ns#">
<form>
<span py:replace="my_form.display(value = data)" />
<input id='btn_save' onclick="save_form(this.form)" type="button" value="Save" />
</form>
<div id="next_form"></div>
</div>


My JavaScript looks like:

function save_form(form) {
// Get all data...
var d = postJSON('/my_page/save/', {...}; // Data from the form
d.addCallback(new_form, form);
}


function new_form(form, data) {
var new_form = get_html_form('/my_page/fragment_returning_method/');
new_form.addCallback(add_form);
}

function add_form(data) {
var pdata = data.responseText;
var local = $('next_form');
local.innerHTML = pdata;
}

I hope it is a good starting point. Of course, the code above can be improved
since it was just a proof of concept for an application that I planned
developing.


Be seeing you,
--
Jorge Godoy <jgo...@gmail.com>

Elvelind Grandin

unread,
Jul 18, 2006, 5:55:11 AM7/18/06
to turbo...@googlegroups.com
You might want to try to set the content-type to xhtml in the expose decorator

@expose(template="...", content_type="application/xhtml+xml")


--
cheers
elvelind grandin

Donald Ball

unread,
Jul 18, 2006, 2:17:25 PM7/18/06
to turbo...@googlegroups.com
On Mon, 17 Jul 2006, Jorge Godoy wrote:

> Donald Ball <ba...@fiendz.org> writes:
>
>> That would work, but the formatting for the polls and their results need
>> to be easily customizable. That's far, far easier to do if I'm using kid
>> templates than if I'm constructing the HTML tree client-side. My page
>> designers can handle editing kid, asking them to edit javascript functions
>> isn't realistic.
>>
>> (Also, stylistically, I mislike requiring a javascript interpreter to
>> render the page. I like my webapps to still function in the absence of
>> one. But that's just me being pedantic.)
>
> What I do here is using Kid's "fragment" option and then using innerHTML on
> the JavaScript side.
>
> Something like:
>
> @turbogears.expose(fragment = True, html = 'marketwatch.templates.some_example')
> def fragment_returning_method(self, *args, **kwargs):
> return dict(form = my_form)

that's a good solution. two questions occur. one, is there a way to choose
to fragment dynamically? tg_fragment in the returned dict has no effect.
also, is there a way to access the fragment datum from the kid template,
so that i could use py:if and py:strip to inhibit chunks of the page if
it's being rendered as a fragment?

- donald

Jonathan LaCour

unread,
Jul 18, 2006, 3:37:20 PM7/18/06
to turbo...@googlegroups.com
Donald Ball wrote:

>> What I do here is using Kid's "fragment" option and then using
>> innerHTML on the JavaScript side.
>>
>> Something like:
>>
>> @turbogears.expose(fragment = True, html =
>> 'marketwatch.templates.some_example') def
>> fragment_returning_method(self, *args, **kwargs): return
>> dict(form = my_form)
>
> that's a good solution. two questions occur. one, is there a way to
> choose to fragment dynamically? tg_fragment in the returned dict has
> no effect. also, is there a way to access the fragment datum from the
> kid template, so that i could use py:if and py:strip to inhibit chunks
> of the page if it's being rendered as a fragment?

I usually couple the innerHTML/fragment solution with widgets. Each
fragment is generated by a widget, and then in my regular templates
that are generating full pages, I use these widgets to insert the
fragments into the page itself.

You can see this approach in the Fast Track source:

http://cleverdevil.org/fasttrack

Best of luck!

--
Jonathan LaCour
http://cleverdevil.org

Sean Jamieson

unread,
Jul 19, 2006, 10:22:29 AM7/19/06
to turbo...@googlegroups.com
Thank you very much, I probably would never have found that.
Reply all
Reply to author
Forward
0 new messages