[Python-ideas] Wild idea: Exception.format

ilya ilya.nikokoshev at gmail.com
Sat Sep 19 09:29:30 CEST 2009


I think there's a way to define an exception subclass with message as
a one-liner::

# Simple syntax to define exception classes.
_ = BaseFormattedException
class RegisterError(_, LookupError): 'Extension "{0}" not registered.'
class ExtensionError(_, ValueError): 'Extension "{0}" returns wrong data.'

# Another way to define exception classes --- factory function.
MissingError = _.formatted(IndexError, 'No extension named "{0}"')
SomeOtherError = _.formatted(Exception)


>>> raise RegisterError('test')
Traceback (most recent call last):
  File "<pyshell#1>", line 1, in <module>
    raise RegisterError('test')
exceptions.RegisterError: Extension "test" not registered.

>>> raise MissingError('test')
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    raise MissingError('test')
exceptions.IndexError: No extension named "test"


(Note the difference between two methods above: RegisterError has its
separate __name__ while still being a subclass of IndexError, while
MissingError doesn't have its own __name__)

The trick is in defining the `BaseFormattedException` which harvests
(the first line of a) docstring to provide template (I figured out for
a small, locally-used exception class there may be no difference
between what it prints and its docstring).

The whole example is at http://web.mit.edu/~unknot/www/exceptions.py

How does that sound?

On Sat, Sep 19, 2009 at 5:43 AM, Nick Coghlan <ncoghlan at gmail.com> wrote:
> Yuvgoog Greenle wrote:
>> I fail to see why you can't have both args and kwargs...
>>
>> class BaseException:
>>     @classmethod
>>     def format(cls, fmt, *args, **kwargs):
>>         return cls(fmt.format(*args, **kwargs), *args, **kwargs)
>>
>> And concerning Gerog's actual question, I think this is a not uncommon
>> pattern, but
>>     raise Exception.format(...)
>> doesn't sound good. It's not really as readable as
>>     raise Exception(fmt.format(ext), ext)
>>
>> ilya suggested an almost ok direction but he doesn't define formats at
>> runtime. So maybe instead we can make a factory for formatted exceptions.
>>
>> def formatted_exception(exception):
>>     def format_and_return(fmt, *args, **kwargs):
>>         return exception(fmt.format(*args, **kwargs), *args, **kwargs)
>>     return format_and_return
>>
>> @formatted_exception
>> class NotRegisteredError(LookupError): pass
>>
>> then the usage becomes:
>> raise NotRegisteredError("Extension {0} not registered.", ext)
>
> I like the class decorator idea (to avoid namespace conflicts on the
> exception objects - cf. the dramas with ex.message). However, the above
> would break exception handling since NotRegisteredError would refer to
> the factory function instead of the exception that is actually thrown.
>
> Combining it with Ilya's idea (by having the class decorator return a
> new subclass rather than a factory function) gives something that should
> work in practice:
>
> def formatted_exception(exc):
>  class FormattedExc(exc):
>    def __init__(*args, **kwds):
>      self, fmt, args = args[0], args[1], args[2:]
>      super(FormattedExc, self).__init__(
>            fmt.format(*args, **kwds), *args, **kwds)
>    def __str__(self):
>      return self.args[0]
>    __name__ = exc.__name__
>    __doc__ = exc.__doc__
>    __module__ = exc.__module__
>  return FormattedExc
>
> @formatted_exception
> class ExampleError(Exception): pass
>
>>>> try:
> ...   raise ExampleError("Format: {}", "Value")
> ... except ExampleError as ex:
> ...   saved = ex
> ...
>>>> saved
> FormattedExc('Format: Value', 'Value')
>>>> print(saved)
> Format: Value
>>>> saved.args[1]
> 'Value'
>>>> saved.__name__
> 'ExampleError'
>
>
> Cheers,
> Nick.
>
> --
> Nick Coghlan   |   ncoghlan at gmail.com   |   Brisbane, Australia
> ---------------------------------------------------------------
>



More information about the Python-ideas mailing list