Raising objects

Alex Martelli aleax at aleax.it
Thu May 1 06:49:50 EDT 2003


Steven Taschuk wrote:
   ...
> Ah yes, the type/class distinction.  I frequently forget that.

It's gonna go away.  But it will take a while.

> So what then is the distinction between new-style classes and
> types?  (Other than that types are not quite reliably subclassable
> yet, or so I hear.)

There is no way to reliably draw that distinction -- we're in
transition and in a mess:-).


>> Moreover, if what you raise is always derived by Exception, the
>> Exception base class can ensure all sort of nice error-reporting
> 
> A good point.
> 
> But usually in Python we do not care whether an object inherits
> from a specific subclass; we only care whether it has the
> attributes we need it to have.  Why should the error-reporting
> facilities of Exception be any different?

Exception handling is one of the very few places in Python that
DO care who inherits from what!  A few others are marginal and
explicit -- issubclass, isinstance, calling unbound methods --
but only in exception handling does inheritance play such a
major role.  So, I think the "usually" doesn't extent to this case.

> In other words, why should we stop a programmer who *doesn't* want
> those error-reporting facilities from raising some other object?
> Isn't trusting the programmer pythonic?

Yes, but letting the programmer make a mess in order to let
him have features he doesn't need isn't.  "23"+45 raises an
exception -- not because we don't trust the programmer, but
because the exception is more pragmatically useful here than
some other silly behavior.  Accessing a yet-unbound local
raises an exception rather than supplying some default value
such as 'None' for an entirely similar reason.

I've seen confused newbies use
    raise 'TypeError'
rather than
    raise TypeError
and lose time tracking things down because the string exception
does alas have to be accepted for backwards compatibility.  I
am NOT at all keen to extend the occasions for such confusions
and mistakes without any good countervailing practical reason.


> clause.  (My excuse for forgetting this is that I always raise
> instances.)
> 
> I'd like to get rid of this; it seems conceptually wrong to me
> that a class and its instances are interchangeable in this

Nolo contendere, but I think backwards compatibility binds
us forevermore in this respect.

> respect.  The identity test which makes it work made sense back
> when exceptions were strings (as you explained so cogently), but
> imho it's now a (minor) wart for backwards compatibility.
> 
> So, what I'd like to see eventually is this:
> 
>     Raise anything you like.
> 
>     An except clause catches an exception e if and only if it
>     names a type of which e is an instance.

I think you'll never see that -- you always raise instances, but
most others always raise classes -- look at the standard library
and you'll see just about all
    raise SomeClass, 'someargument'

If I could raise anything I like I would expect this to raise a
tuple with two items -- a class then a string:-).  Some modules
are even inconsistent in this, e.g. 

Lib/copy_reg.py:        raise ValueError, "code out of range"
Lib/copy_reg.py:        raise ValueError("key %s is already reg...

In a greenfield-designed language this might or might not
make sense (I would still prefer a specific Exception type
which must be the base of anything raised, for all of the
advantages you previously conceded, for 'except Exception'
as the best way to catch anything, etc, etc) -- but the
debate is moot. Python 3000 will introduce incompatibilities
but, I think, none as deep as this one would be.


> This seems simpler than what you would want to see eventually:
> 
>     Raise any subclass of (New)Exception, or an instance of such
>     a subclass.
> 
>     An except clause catches an exception if and only if it
>     names either a type of which e is an instance, or an object
>     which is identical to e.
> 
> (Hopefully I've correctly understood what you're saying.)  Either
> of these is, of course, simpler than the present rules.

I would explain my desiderata differently: raising any instance
of NewException raises it; raising any subclass of NewException
instantiates it (optionally with the provided argument[s]) and
raises the resulting instance.

So, the exception that is propagating is ALWAYS an instance of
NewException.  An except clause catches an exception E, if and
only if it names a type of which E is an instance.

The backwards-compatibility hacks aren't in my "desiderata" --
and it MIGHT conceivably be possible to remove string exceptions
one day and just make Exception newstyle (well, one can dream,
but I DO dream of a day where classic classes have gone!).


> The only disadvantage of my ideal scenario that I can see is that
> it introduces a new kind of "forgot the parentheses" bug;
>     raise ValueError
> would raise an exception which would go right past
>     except ValueError:
> and only be caught by the very unlikely
>     except type:
> or a bare except.

That, and other errors.


> To get from the present rules to my ideal scenario, the identity
> test has to be removed, and while we're at it, the syntax
>     raise foo, bar
> should go too.  (Again, it made sense back when exceptions were
> strings.)  And perhaps sys.exc_* would have to be revised as well.

If the BDFL ever Pronounces that "raise foo, bar" should eventually
go, then I guess it may, but I've never seen him express any dislike
of it.

> The path seems straightforward:
> 
>     Step 0.  Revise the Tutorial to advocate "raise ValueError(...)"
>     instead of "raise ValueError, ...".  Mention the latter only as
>     an old way of doing things.

But it's not -- the newest modules in the standard library use it
just as freely as the older ones.

>     Step 1.  Issue warnings when an exception is caught by the
>     identity test, and when the "raise foo, bar" syntax is used.
>     (These warnings could be phased in via __future__ if desired.)

Warnings have their own ways to enable/disable.  And yes, it WOULD
be nice to start getting optional warnings for usage tied to string
exceptions (but I doubt the 2-arguments raise will be warned about).


>     Step 2.  Remove the identity test; catch only by the isinstance
>     test.  Remove the "raise foo, bar" syntax.  Revise sys.exc_*
>     if appropriate.
> 
>     Step 3.  Make Exception a new-style class.
> 
>     Step 4.  Allow anything to be raised.
> 
> (I haven't thought about the e in
>     except ValueError, e:
> yet.)

I don't see how that could cause a problem.


> Hm... maybe I should write a PEP.

Yes, I agree you should -- nothing as major as this is gonna happen
without a lot of PEPping.


Alex





More information about the Python-list mailing list