[Python-3000] exceptions with keyword arguments

tomer filiba tomerfiliba at gmail.com
Wed May 17 19:29:01 CEST 2006


hi all

i would like to suggest changing the base-exception class, whatever
it may be (Exception/BaseException) to work with keyword arguments
instead of positional ones.

instead of

try:
    ...
except IOError, ex:
    print ex[1]
# or
except IOError, (code, text, filename):
    ...
    # which means changes to code/text/filename do not change
    # the exception object

use

try:
    raise IOError(filename = "lala", code=17, text="blah blah blah")
except IOError, ex:
    ex.code = 18
    raise

raise IndexError("invalid index", index = the_index)
raise KeyError("key not found", key = the_key)
raise AttributeError("attribute not found", name = name)

where the new exception can be something like

class Exception:
    def __init__(self, message = None, **kw):
        self._message = message
        self.__dict__.update(kw)
    def __repr__(self):
        attrs = sorted("%s = %r" % (k, v)
                       for k, v in self.__dict__.iteritems()
                       if not k.startswith("_"))
        return "<%s(%s, %s)>" % (self.__class__.__name__,
            self._message, ", ".join(attrs))

class IOError(Exception):
   pass

raise IOError(code = 17, text = "EBLAH", filename = "lalala")

the builtin errors might want to enforce an "exception signature",

class ExceptionSignature(Exception):
    attributes = []
    def __init__(self, *args, **kw):
         for name in self.attributes:
             assert name in kw, "expected an attribute named %s" % (name,)
         Exception.__init__(self, *args, **kw)

class IOError(ExceptionSignature):
    attributes = ["code", "text", "filename"]

or something like that, so the attributes of the exception are part
of its official interface.

rationale:
* today, AttributeError's are raised as
AttributeError("%s object has no attribute %s" % ...)
which means analyzing the exception requires parsing text!
 * IOError (among others), for instance, does nasty and not-so-well documented
overloading of named/positional arguments: when you pass 1-3 arguments,
they are stored in .args, but also in .errno, .strerror, and
.filename. if you pass
more than 3 arguments, the attributes are all set to None and only
.args is filled.
yuck.

you can see this for reference:
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496698

----

that said, i would also want to introduce ArgumentError. there are
many times just a ValueError isn't enough. instead, having a builtin
ArgumentError would made things more clear:

def write_to_file(the_file):
    if the_file.closed:
        raise ArgumentError("the file must be open", name = "the_file")
    the_file.write(...)

and with ArgumentError included, calling functions with invalid
signatures would also raise ArgumentError. TypeError is quite
silly in this case, as it has nothing to do with the *type* of
the function or its arguments.

>>> def f(a): pass
>>> f(1,2)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
*TypeError*: f() takes exactly 1 argument (2 given)
>>> type(f)
<type 'function'> # like any other function

TypeError is too-broadly overloaded this way.


-tomer


More information about the Python-3000 mailing list