durusmail: quixote-users: Inconsistency between _c_htmltext.c and _py_htmltext.py
Inconsistency between _c_htmltext.c and _py_htmltext.py
2004-03-08
Inconsistency between _c_htmltext.c and_py_htmltext.py
2004-03-08
Inconsistency between _c_htmltext.c and_py_htmltext.py
2004-03-08
Inconsistency between _c_htmltext.c and _py_htmltext.py
Jason Sibre
2004-03-08
I have been playing around with the form frameworks (form and form2), and I
tried to create a class that wrapped the form2.Form, and allowed
dictionary-style access to the Widget's render() method and the WidgetRow's
title attribute, when I discovered a bug in _c_htmltext.c.  Rather, after a
few hours of debugging, I'm pretty sure it's a bug.

The problem is that an instance of htmltext that came from the c version
will not accept a mapping object other than a dict, or a subclass of one,
while the python version of htmltext will allow you to provide any
'mapping-like' object.  Specifically, the python version requires that the
mapping implement items().

In a nutshell, the line that produces the error looks like this (in a
modified form2.Form object):
        return htmltext(self.body_template) % FormTemplateDict(self)

I know those names won't mean much to you folks, as they aren't part of the
standard form2, so I'll explain briefly.  body_template is a string, along
the lines of:
"""\
        
%(titleof_testInitValue)s %(testInitValue)s Named %(testName)s
%(submit)s
""" And FormTemplateDict (my fake dictionary wrapper) wraps self (the Form instance) providing __getitem__ (items(), values(), etc...) access to the widgets by via the format codes in body_template. For example, FormTemplateDict(self)['titleof_testInitValue'] would return the .title of the WidgetRow containing the widget named 'testInitValue', while FormTemplateDict(self)['testInitValue'] would return the results of calling that Widget's render() method. Ok. So, If I do: return self.body_template % FormTemplateDict(self) it works. If I do: return htmltext(self.body_template) % FormTemplateDict(self) with _c_htmltext.so missing or renamed, causing _py_htmltext.py to be used instead, it works. But if I try the desired call: return htmltext(self.body_template) % FormTemplateDict(self) while using the _c_htmltext.so file. It blows up with a KeyError. I'm not much of a C programmer, and I have NO experience with doing python extensions in C, but I spent some time trying to narrow down the problem (with the hope of providing a patch), and the best I could conclude is that the following loop _c_htmltext.c 323 while (PyDict_Next(args, &pos, &key, &value)) { 324 PyObject *wvalue = wrap_arg(value); 325 if (wvalue == NULL) { 326 Py_DECREF(wargs); 327 return NULL; 328 } 329 if (PyDict_SetItem(wargs, key, wvalue) < 0) { 330 Py_DECREF(wargs); 331 return NULL; 332 } 333 Py_DECREF(wvalue); 334 } requires a REAL dict (or subclass). It does not call "items()" as the python implementaion in _py_htmltext.py does. In fact, I provided a subclass of a dict instead, with values in it, and then put a __getattribute__ method to log all accesses, and it worked fine, but no accesses were logged at all (except for the update() in my constructor, where I initialized the object to match a dictionary passed to the constructor). My suspicion is that the PyDict_Next loop should be rewritten to use PyDict_Items, but with my current level of knowledge of writing Python extensions in C (None), anything I do to make that change would probably be *very* buggy. I wonder if a similar bug would be discovered if I was emulating a List type instead of a dictionary... In the meantime, I have two ideas for working around this, so it's not that big of a deal: - Passing in a real dictionary - htmlescaping the args inside of my fake dictionary, so that I can apply the % operator to a string instance instead of an htmltext instance But I still wanted to point this out to those who may be able to fix it with a reasonable amount of effort. If anyone wants a test case, I've attached a fairly thorough one that demonstrates behavior with a dict, a subclass of dict, and something pretending to be a dict. Everything in the script should work fine until line 125, at which point you'll get a KeyError (because the dict produced in _c_htmltext to be passed to the PyString_Format function is empty, since the PyDict_Next produced no entries.) 125: print c_htmltext % dict_emulator # <--- This will bomb out on a KeyError! I don't thinks it's relevant, but for the record, I'm using Python 2.3.3 on Linux for this. Jason Sibre
reply