Wednesday, April 16, 2008

KeyError breaks the unwritten rules

I ran across an interesting little "feature" in Python today; it cropped up as a bug in an old version of DocumentTemplate that I am dependent upon. It turns out that, unlike other Exceptions in Python, strings within KeyErrors don't round-trip. In other words,

"hello" != str(KeyError("hello"))

This is certainly unintuitive; I would have made the same mistake as the Zope guys. In fact, I have tried it for other Exception classes and most do round-trip. To quoteth the Zen:

Special cases aren't special enough to break the rules.

What is so special about KeyError that it gets to break the rules?

Here is a little test I whipped up to explore this:

import unittest

class TestExceptionStringRoundTrip(unittest.TestCase):
def assertStrCast(self, ExceptionClass):
s = "hello"
self.assertEqual(s, str(ExceptionClass(s)))

for builtin in dir(__builtins__):
if builtin.endswith('Error') or builtin.endswith('Exception'):
def _test(self, ExceptionClass=builtin):
self.assertStrCast(getattr(__builtins__, ExceptionClass))
setattr(TestExceptionStringRoundTrip, "test%s" % builtin,

if __name__ == "__main__":

There is only one failure: KeyError. There are three errors from UnicodeDecodeError, UnicodeEncodeError and UnicodeTranslateError because they don't accept just a string as an argument (they probably should!)

Anyway, a fun little aside.

Amaury Forgeot d'Arc has beaten me to the punch with a patch to fix this. I was kind of hoping I would get a crack at this, but this guy is just a coding machine :). Looks like he has a lot more experience with Python's source, so, I trust his patch is probably a little nicer than mine would have been. Here's the bug report for those who are interested (the patch is linked from the report).

No comments: