06.07.2017, 13:00, "Steven D'Aprano" <st...@pearwood.info>:
> What prevents the programmer from writing this?
>
> raise NameError(nym=s, template="name '{nym}' is not defined.")
>
> Or any other keyword name for that matter. Since the exception class
> accepts arbitrary keyword arguments, we have to expect that it could be
> used with arbitrary keyword arguments.
>
> Only the exception subclass knows how many and what information it
> expects:
>
> - NameError knows that it expects a name;
> - IndexError knows that it expects an index;
> - OSError knows that it expects anything up to five arguments
>   (errno, errstr, winerr, filename1, filename2);
>
> etc. BaseException cannot be expected to enforce that. Ken's suggestion
> to put the argument handling logic in BaseException doesn't give us any
> way to guarantee that NameError.kwargs['name'] will even exist, or that
> NameError.args[0] is the name.

I am not understanding what is bad about your example. Yes, BaseException would 
allow an
arbitrary set of arguments to be passed to the exception. It also defines how 
they would be
handled by default. If people did not want that, they could subclass 
BaseException and
replace the constructor and the __str__ method. With Ken's proposal, NameError 
and
IndexError could then be replaced with:

    class NameError(Exception):
        '''Name not found.

        :param *args:
            args[0] contains the name that was not found. Values passed in 
args[1:] are ignored.
        :param **kwargs:
            kwargs['template'] is format string used to assemble an error 
message. This strings
            format() method is called with *args and **kwargs as arguments, and 
the result is
            returned by __str__().

        args and kwargs are saved as attributes, so you can access the 
exception's arguments
        through them.
        '''
        pass


    class IndexError(Exception):
        '''Sequence index out of range.

        :param *args:
            args[0] contains the value of the index.
        :param **kwargs:
            kwargs['template'] is format string used to assemble an error 
message. This strings
            format() method is called with *args and **kwargs as arguments, and 
the result is
            returned by __str__().

            args and kwargs are saved as attributes, so you can access the 
exception's arguments
            through them.
        '''
        pass

OSError could be implemented by overriding the __str__() method.

Once this is done, the new versions do everything the old versions do, but 
provide access to
the components of the error message. The are also discoverable, more 
discoverable than the
originals.

In addition, they are easily extensible. For example, if I raise the NameError 
myself, I can
provide additional useful information:

    try:
        raise NameError('welker', db='user')
    except NameError as e:
        db = e.kwargs.get('db')
        print('{}: {} not found.'.format(e.args[0], db) if db else str(e))

If, as you suggest, some one writes:

    raise NameError(nym=s, template="name '{nym}' is not defined.")

it will work as expected as long as they confined themselves to using str(e). 
It would only fail
if someone directly tried to access the argument using e.args, but that would 
be a very
unusual thing to do and the issue could be worked around with by examining args 
and kwargs.

Jeff




_______________________________________________
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to