PEP 343, second look

Ron Adam rrr at ronadam.com
Wed Jun 22 21:44:44 CEST 2005


After taking a break from following PEP 343 and it's associated PEPs, I 
wanted to look at it again because it still seemed a bit hard to get my 
mind around.

     http://www.python.org/peps/pep-0343.html


> A new statement is proposed with the syntax:
> 
>         with EXPR as VAR:
>             BLOCK
> 
>     Here, 'with' and 'as' are new keywords; EXPR is an arbitrary
>     expression (but not an expression-list) and VAR is a single
>     assignment target.

How is EXPR arbitrary?  Doesn't it need to be or return an object that 
the 'with' statement can use?  (a "with" object with an __enter__ and 
__exit__ method?)

And if so, what is the minimum 'with' class needed to create such an 
object?  It would need an __exit__ method, because there would be no 
point if it didn't have one.  Does it absolutely need an __enter__ 
method?  Are there any uses that might not require an __enter__?

Presuming __enter__ is always needed, would this be a minimum class that 
returns a 'with' object?

    class With(object):
        def __enter__(self):
            pass
        def __exit__(self):
            pass


A 'with-generator' object would need __enter__ and __exit__ methods, and 
a try-yield-finally generator. Is there a name for a 'with-generator' 
object?  (a witherator ?)

It's possible that a function may be used that returns an 
'with-generator' object and would be used in the same way that range() 
is used in 'for' statements.

     func with_gen(func, *args, **args):
         wg = WithGen()
         wg.gen = func(*arg, **args)
         return wg


Which would use a generic with-generator base class.

     class WithGen(object):
        def gen(self):         # Should this raise an error
            try:               # if it does't get over ridden?
                yield None
            finally:
                pass
        def __enter__(self):
            try:
                return self.gen.next()
            except StopIteration:
                raise RuntimeError("generator didn't yield")
        def __exit__(self, type, value, traceback):
            if type is None:
                try:
                    self.gen.next()
                except StopIteration:
                    print "file closed by with statement"
                    return
                else:
                    raise RuntimeError("generator didn't stop")
            else:
                try:
                    self.gen.throw(type, value, traceback)
                except (type, StopIteration):
                    return
                else:
                    raise RuntimeError("generator caught exception")


And used like this:

     def opening(filename, mode):
         f = open(filename, mode)
         try:
            yield f
         finally:
            f.close()

     with with_gen(opening, "testfile", "w") as f:
         f.write("test file")


This seems (to me) to be an easier to understand alternative to the 
decorator version.  The class could also be used as a base class for 
constructing other 'with' class's as well as the with_template decorator.

Will this work or am I missing something?  Any suggestions for a 
different (better) name for the with_gen function?

Regards,
Ron




More information about the Python-list mailing list