Getting clear error messages with a Python templating system?
Hi there, I'm developing web applications in Python, so I use a templating system to produce HTML. We're dealing with heavy traffic here, so my original choice was the fast and efficient Cheetah. The main problem is that Cheetah doesn't provide clear error messages. Even in the best cases, you only get some indication of the nature of the error ("TypeError: 'str' object is not callable") but you can't see the code where the error occurs. You can't even know the approximate region of the template code where the problem originates. So I tried another templating system, the newer and equally efficient Mako. Again, same problem. It should be noted that both Cheetah and Mako compile themselves to an intermediary Python file. This Python file is what's actually running, so that's where the error originates. This is very efficient, but also severely hinders debugging (see below for example of what a very simple error on a very short Mako template generates). So my question is: is there any way to get clear error messages with a Python templating system? I welcome both suggestion on how to get those with Cheetah (or Mako), and suggestions about alternative templating systems that provide better error messages. Thanks, Tom - # foo.mako: hello ${data} >>> print mako.template.Template(filename="foo.mako").render(data="world") hello world >>> print mako.template.Template(filename="foo.mako").render(dataerr="world") Traceback (most recent call last): File "", line 1, in File "/usr/lib/python2.5/site-packages/Mako-0.1.10-py2.5.egg/mako/template.py", line 114, in render return runtime._render(self, self.callable_, args, data) File "/usr/lib/python2.5/site-packages/Mako-0.1.10-py2.5.egg/mako/runtime.py", line 287, in _render _render_context(template, callable_, context, *args, **_kwargs_for_callable(callable_, data)) File "/usr/lib/python2.5/site-packages/Mako-0.1.10-py2.5.egg/mako/runtime.py", line 304, in _render_context _exec_template(inherit, lclcontext, args=args, kwargs=kwargs) File "/usr/lib/python2.5/site-packages/Mako-0.1.10-py2.5.egg/mako/runtime.py", line 337, in _exec_template callable_(context, *args, **kwargs) File "foo_mako", line 19, in render_body File "/usr/lib/python2.5/site-packages/Mako-0.1.10-py2.5.egg/mako/runtime.py", line 91, in __str__ raise NameError("Undefined") NameError: Undefined -- http://mail.python.org/mailman/listinfo/python-list
Re: Python Written in C?
On Mon, Jul 21, 2008 at 3:53 PM, DaveM <[EMAIL PROTECTED]> wrote: > On Mon, 21 Jul 2008 03:18:01 +0200, Michiel Overtoom <[EMAIL PROTECTED]> > wrote: > > > >Many major text/word processing programs (Emacs, vi, MS-Word) are also > >written in C. > > I thought Emacs was written in Lisp. Large parts of Emacs are indeed implemented in Emacs Lisp. There's are some core functions implemented in C. MS-Word, afaik, had very substantial parts written in Visual Basic. Tom > DaveM > -- > http://mail.python.org/mailman/listinfo/python-list > -- http://mail.python.org/mailman/listinfo/python-list
Dangerous behavior of list(generator)
In most cases, `list(generator)` works as expected. Thus, `list()` is generally equivalent to `[]`. Here's a minimal case where this equivalence breaks, causing a serious and hard-to-detect bug in a program: >>> def sit(): raise StopIteration() ... >>> [f() for f in (lambda:1, sit, lambda:2)] Traceback (most recent call last): File "", line 1, in File "", line 1, in sit StopIteration >>> list(f() for f in (lambda:1, sit, lambda:2)) [1] I was bitten hard by this inconsistency when sit() was returning the idiom `(foo for foo in bar if foo.is_baz()).next()`. The nonexistence of a foo with is_baz() True in that query raises an exception as designed, which expresses itself when I use the list comprehension version of the code above; the generator version muffles the error and silently introduces a subtle, confusing bug: `lambda:2` is never reached, and a truncated list of 1 element (instead of 3) is "successfully" generated.. Just wondered what you guys think, -- Tom -- http://mail.python.org/mailman/listinfo/python-list
Re: Dangerous behavior of list(generator)
Thanks for the comment and discussion guys. Bottom line, I'm going to have to remove this pattern from my code: foo = (foo for foo in foos if foo.bar).next() I used to have that a lot in cases where not finding at least one valid foo is an actual fatal error. But using StopIteration to signal a fatal condition becomes a bug when interacting with list() as shown in the original post. It would be nice if there was a builtin for "get the first element in a genexp, or raise an exception (which isn't StopIteration)", sort of like: from itertools import islice def first_or_raise(genexp): L = list(islice(genexp, 1)) if not L: raise RuntimeError('no elements found') return L[0] I also think Jean-Paul's had a good point about how the problems in the list/genexp interaction could be addressed. Thank you, -- Tom -- http://mail.python.org/mailman/listinfo/python-list
Re: Dangerous behavior of list(generator)
On Wed, Dec 30, 2009 at 4:01 PM, Steven D'Aprano wrote: > On Wed, 30 Dec 2009 15:18:11 -0800, Tom Machinski wrote: >> Bottom line, I'm going to have to remove this pattern from my code: >> >> foo = (foo for foo in foos if foo.bar).next() > > I don't see why. What's wrong with it? Unless you embed it in a call to > list, or similar, it will explicitly raise StopIteration as expected. Exactly; this seems innocuous, but if some caller of this code uses it in a list() constructor, a very subtle and dangerous bug is introduced - see OP. This is the entire point of this post. In a large, non-trivial application, you simply cannot afford the assumption that no caller will ever do that. Even if you have perfect memory, some of your other developers or library users may not. As for what's wrong with the "if not any" solution, Benjamin Kaplan's post hits the nail on its head. This is a bioinformatics application, so the iterable "foos" tends to be very large, so saving half the runtime makes a big difference. -- Tom -- http://mail.python.org/mailman/listinfo/python-list
Re: Dangerous behavior of list(generator)
On Thu, Dec 31, 2009 at 1:54 AM, Peter Otten <__pete...@web.de> wrote: > Somewhat related in 2.6 there's the next() built-in which accepts a default > value. You can provide a sentinel and test for that instead of using > try...except: Thanks. This can be useful in some of the simpler cases. As you surely realize, to be perfectly safe, especially when the iterable can contain any value (including your sentinel), we must use an out-of-band return value, hence an exception is the only truly safe solution. -- Tom -- http://mail.python.org/mailman/listinfo/python-list
Re: Dangerous behavior of list(generator)
On Thu, Dec 31, 2009 at 12:18 PM, Stephen Hansen wrote: > Hmm? Just use a sentinel which /can't/ exist in the list: then its truly > safe. If the list can contain all the usual sort of sentinels (False, None, > 0, -1, whatever), then just make a unique one all your own. > sentinel = object() > if next(g(), sentinel) is sentinel: > ... > Its impossible to get a false-positive then, as nothing g() can ever produce > would ever be precisely "sentinel" (which would usually for me be some > global const if I need to do such things in multiple places). > --S That's not a bad idea. Another nice feature is support for callable "default" values; it would make several new things easier, including raising an exception when you really want that (i.e. if not finding a single element is truly exceptional). -- Tom -- http://mail.python.org/mailman/listinfo/python-list