[Python-Dev] PEP 343 rewrite complete

Guido van Rossum gvanrossum at gmail.com
Thu Jun 2 16:38:48 CEST 2005

[Arnold deVos, responding to himself]
> > This template eats eats the exception, which will cause a RuntimeError
> > in the proposed Wrapper, I think.  A raise after rollback is needed.

No, the generator returns after rolling back, which causes throw() to
raise StopIteration, which is good enough for the wrapper as written.

> Actually, the Wrapper as written in the PEP does not raise RuntimeError
> if the generator catches a block's exception.
> Shouldn't the relevant clause in the Wrapper go like this:
> try:
>     self.gen.throw(type, value, traceback)
> except type:
>     return
> except StopIteration:
>     raise RuntimeError("generator caught exception")
> else:
>     raise RuntimeError("generator didn't stop")

I considered that, but decided that it should be okay for the
generator to respond to a throw() by returning (thus replacing the
exception thrown by StopIteration) since the call to __exit__() is
contained inside a finally-clause, so that when __exit__() returns
normally, the finally-clause will re-raise the original exception

Note that there are currently no other use cases for throw() except
the with_template decorator and the close() method. Both allow the
generator to respond either by letting the exception pass through it
unchanged (after executing finally-clauses if present) or by simply
returning (which will raise StopIteration in the caller of throw()).
Erroneous behaviors are, in both cases, raising some other exception
or *yielding* another value. There may be *other* use cases for
yielding a value, for example, a future (looping) block-statement a la
PEP 343. Raising another exception is always an indication of a bug in
the generator.

> And the transaction template would go like this (re-raising the exception):
> @with_template
> def transactional(db):
>     db.begin()
>         try:
>             yield None
>         except:
>             db.rollback()
>             raise
>         else:
>             db.commit()
> At least this is what I gleaned from the earlier threads.  It means that
> the template does not appear to supress an exception that it cannot
> actually supress.

I disagree (at the -0 to -0.5 level); given that the with_template
decorator allows StopIteration, I think that "appearing to suppress an
exception it doesn't actually suppress" is a minor sin -- the key
point is that the cleanup gets executed and doesn't raise a new
exception or reaches a yield-statement.

I'll summarize this discussion in the PEP.

--Guido van Rossum (home page: http://www.python.org/~guido/)

More information about the Python-Dev mailing list