More on contextlib - adding back a contextmanager decorator
A few things from the pre-alpha2 context management terminology review have had a chance to run around in the back of my head for a while now, and I'd like to return to a topic Paul Moore brought up during that discussion. Paul had a feeling there should be two generator decorators in contextlib - one for __context__ methods and one for standalone generator functions. However, contextfactory seemed to meet both needs, so we didn't follow the question up for alpha 2. The second link in this chain is the subsequent discussion with Guido about making the context manager and managed context protocols orthogonal. With this clearer separation of the two terms to happen in alpha 3, it becomes more reasonable to have two different generator decorators, one for defining managed contexts and one for defining context managers. The final link is a use case for such a context manager decorator, in the form of a couple of HTML tag example contexts in the contextlib documentation. (contextlib.nested is also a good use case, where caching can be used to ensure certain resources are always acquired in the same order, but the issue is easier to demonstrate using the HTML tag examples) Firstly, the class-based HTML tag context manager example: -------------------- class TagClass: def __init__(self, name): self.name = name @contextfactory def __context__(self): print "<%s>" % self.name yield self print "</%s>" % self.name
h1_cls = TagClass('h1') with h1_cls: ... print "Header A" ... <h1> Header A </h1> with h1_cls: ... print "Header B" ... <h1> Header B </h1>
Each with statement creates a new context object, so caching the tag object itself works just as you would expect. Unfortunately, the same cannot be said for the generator based version: -------------------- @contextfactory def tag_gen(name): print "<%s>" % name yield print "</%s>" % name
h1_gen = tag_gen('h1') with h1_gen: ... print "Header A" ... <h1> Header A </h1> with h1_gen: ... print "Header B" ... Traceback (most recent call last): ... RuntimeError: generator didn't yield
The managed contexts produced by the context factory aren't reusable, so caching them doesn't work properly - they need to be created afresh for each with statement. Adding another decorator to define context managers, as Paul suggested, solves this problem neatly. Here's a possible implementation: def managerfactory(gen_func): # Create a context manager factory from a generator function context_factory = contextfactory(gen_func) def wrapper(*args, **kwds): class ContextManager(object): def __context__(self): return context_factory(*args, **kwds) mgr = ContextManager() mgr.__context__() # Throwaway context to check arguments return mgr # Having @functools.decorator would eliminate the next 4 lines wrapper.__name__ = context_factory.__name__ wrapper.__module__ = context_factory.__module__ wrapper.__doc__ = context_factory.__doc__ wrapper.__dict__.update(context_factory.__dict__) return wrapper @managerfactory def tag_gen2(name): print "<%s>" % name yield print "</%s>" % name
h1_gen2 = tag_gen2('h1') with h1_gen2: ... print "Header A" ... <h1> Header A </h1> with h1_gen2: ... print "Header B" ... <h1> Header B </h1>
-- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
On 4/30/06, Nick Coghlan <ncoghlan@iinet.net.au> wrote:
A few things from the pre-alpha2 context management terminology review have had a chance to run around in the back of my head for a while now, and I'd like to return to a topic Paul Moore brought up during that discussion.
I believe the context API design has gotten totally out of hand. Regardless of the merits of the "with" approach to HTML generation (which I personally believe to be an abomination), I don't see why the standard library should support every possible use case with a custom-made decorator. Let the author of that tag library provide the decorator. I have a counter-proposal: let's drop __context__. Nearly all use cases have __context__ return self. In the remaining cases, would it really be such a big deal to let the user make an explicit call to some appropriately named method? The only example that I know of where __context__ doesn't return self is the decimal module. So the decimal users would have to type with mycontext.some_method() as ctx: # ctx is a clone of mycontext ctx.prec += 2 <BODY> The implementation of some_method() could be exactly what we currently have as the __context__ method on the decimal.Context object. Its return value is a decimal.WithStatementContext() instance, whose __enter__() method returns a clone of the original context object which is assigned to the variable in the with-statement (here 'ctx'). This even has an additional advantage -- some_method() could have keyword parameters to set the precision and various other context parameters, so we could write this: with mycontext.some_method(prec=mycontext.prec+2): <BODY> Note that we can drop the variable too now (unless we have another need to reference it). An API tweak for certain attributes that are often incremented or decremented could reduce writing: with mycontext.some_method(prec_incr=2): <BODY> -- --Guido van Rossum (home page: http://www.python.org/~guido/)
Guido van Rossum wrote:
I believe the context API design has gotten totally out of hand. I have a counter-proposal: let's drop __context__.
Heh. I was about to pull out the "if the implementation is hard to explain, it's a bad idea (and bad ideas shouldn't go into 2.X)" rule last week in response to the endless nick-phillip-paul "today I put in my brain the other way" threads... ;-) (but the design isn't really that hard to explain; it's just that ever- one seems to be missing that there are three objects involved, not two...) But if you think we can get rid of the "with statement context object" [1], I'm all for it. "explicit is better than implicit" etc. +1. </F> 1) http://docs.python.org/dev/ref/with.html
On 4/30/06, Guido van Rossum <guido@python.org> wrote:
On 4/30/06, Nick Coghlan <ncoghlan@iinet.net.au> wrote:
A few things from the pre-alpha2 context management terminology review have had a chance to run around in the back of my head for a while now, and I'd like to return to a topic Paul Moore brought up during that discussion.
I believe the context API design has gotten totally out of hand. Regardless of the merits of the "with" approach to HTML generation (which I personally believe to be an abomination), I don't see why the standard library should support every possible use case with a custom-made decorator. Let the author of that tag library provide the decorator.
I have a counter-proposal: let's drop __context__. Nearly all use cases have __context__ return self. In the remaining cases, would it really be such a big deal to let the user make an explicit call to some appropriately named method? The only example that I know of where __context__ doesn't return self is the decimal module. So the decimal users would have to type
+1. -Brett
with mycontext.some_method() as ctx: # ctx is a clone of mycontext ctx.prec += 2 <BODY>
The implementation of some_method() could be exactly what we currently have as the __context__ method on the decimal.Context object. Its return value is a decimal.WithStatementContext() instance, whose __enter__() method returns a clone of the original context object which is assigned to the variable in the with-statement (here 'ctx').
This even has an additional advantage -- some_method() could have keyword parameters to set the precision and various other context parameters, so we could write this:
with mycontext.some_method(prec=mycontext.prec+2): <BODY>
Note that we can drop the variable too now (unless we have another need to reference it). An API tweak for certain attributes that are often incremented or decremented could reduce writing:
with mycontext.some_method(prec_incr=2): <BODY>
-- --Guido van Rossum (home page: http://www.python.org/~guido/) _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/brett%40python.org
Guido van Rossum wrote:
On 4/30/06, Nick Coghlan <ncoghlan@iinet.net.au> wrote:
A few things from the pre-alpha2 context management terminology review have had a chance to run around in the back of my head for a while now, and I'd like to return to a topic Paul Moore brought up during that discussion.
I believe the context API design has gotten totally out of hand. Regardless of the merits of the "with" approach to HTML generation (which I personally believe to be an abomination),
The example is tempting because it's easy to follow. I agree actually doing it in real code would almost certainly be nuts :)
I don't see why the standard library should support every possible use case with a custom-made decorator. Let the author of that tag library provide the decorator.
The HTML tag was just an example. The underlying idea is being able to easily create a re-usable object that can be passed to multiple with statements (potentially nested within each other or within distinct threads). Without the __context__ method, the naive version of such an object looks like: class reusable(object): def __init__(self, factory): self.factory = factory factory() # Check the factory works at definition time def __enter__(self): current = self.current = factory() return current.__enter__() def __exit__(self, *exc_info): return self.current.__exit__(*exc_info) The downside of this over the __context__ method is that it is neither nesting nor thread-safe. Because the storage is on the object rather than in the execution frame, sharing such objects between threads or using one for nested with statements will break (as self.current gets overwritten).
I have a counter-proposal: let's drop __context__. Nearly all use cases have __context__ return self. In the remaining cases, would it really be such a big deal to let the user make an explicit call to some appropriately named method? The only example that I know of where __context__ doesn't return self is the decimal module.
It would also prevent threading.Condition from using its underlying lock object as the managed context. The real problem I have with removing __context__() is that it pushes the burden of handling thread-safety and nesting-safety issues onto the developers of context managers without giving them any additional tools beyond threading.locals(). This was the problem Jason brought up for decimal.Context that lead to the introduction of __context__ in the first place. Without the __context__() method, *users* of the with statement will be forced to create a new object with __enter__()/__exit__() methods every time, either by invoking a method (whose name will vary from object to object, depending on the whim of the designer) or by calling a factory function (which is likely to be created either as a zero-argument lambda returning an object with enter/exit methods, or else by using PEP 309's partial function). So if you see a with statement with a bare variable name as the context expression, it will probably be wrong, unless: a) the implementor of that type provided thread-safety and nesting-safety; or b) the object is known to be neither thread-safe nor nesting-safe The synchronisation objects in threading being examples of category a, file objects being examples of category b. In this scenario, generator contexts defined using @contextfactory should always be invoked directly in the context expression, as attempting to cache them in order to be reused won't work (you would need to put them in a zero-argument lambda and call it in the context expression, so that you get a new generator object each time). Documenting all of the thread-safety and nesting-safety issues and how to deal with them would be a serious pain. I consider it much easier to provide the __context__() method and explain how to use that as the one obvious way to deal with such problems. Then only implementors need to care about it - from a user's point of view, you just provide a context expression that resolves to a context manager, and everything works as intended, including being able to cache that expression in a local variable and use it multiple times. (That last point obviously not applying to context managers like files that leave themselves in an unusable state after __exit__, and don't restore themselves to a usable state in __enter__). Essentially, I don't think dropping __context__ would gain us anything - the complexity associated with it is real, and including that method in the API let's us deal with that complexity in one place, once and for all. Removing the method from the statement definition just pushes the documentation burden out to all of the context managers where it matters (like decimal.Context, the documentation for which would get stuck with trying to explain why you have to call a method in order to get a usable context manager). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
[I'm cutting straight to the chase here] On 4/30/06, Nick Coghlan <ncoghlan@gmail.com> wrote:
The downside of this over the __context__ method is that it is neither nesting nor thread-safe.
This argument is bogus. We currently have two types of objects involved in with-statements: those whose __context__ returns self and those whose __context__ returns some other object. In all of the latter cases, the original object doesn't have __enter__ or __exit__ methods, so using it in the "reduced-with-statement" will be an immediate run-time error. Writing a method that returns the correct type of object with the correct behavior (thread-safe, or nesting, or whatever is required by that specific object) is no harder whether the method name is __context__ or not.
The real problem I have with removing __context__() is that it pushes the burden of handling thread-safety and nesting-safety issues onto the developers of context managers without giving them any additional tools beyond threading.locals(). This was the problem Jason brought up for decimal.Context that lead to the introduction of __context__ in the first place.
Again, I don't see how writing the thread-safe version is easier when the method is called __context__.
Without the __context__() method, *users* of the with statement will be forced to create a new object with __enter__()/__exit__() methods every time,
You seem to be missing the evidence that 9 out of 10 objects currently have a __context__ that returns self. In all those cases the user of the with-statement won't have to make any changes at all compared to code that works with 2.5a2.
either by invoking a method (whose name will vary from object to object, depending on the whim of the designer) or by calling a factory function (which is likely to be created either as a zero-argument lambda returning an object with enter/exit methods, or else by using PEP 309's partial function).
Having the name being different in each situation may actually be an advantage -- it will give an additional clue as to what is happening, and it will let us design different APIs for use in a with-statement (just like dicts have iterkeys() and iteritems()).
So if you see a with statement with a bare variable name as the context expression, it will probably be wrong, unless: a) the implementor of that type provided thread-safety and nesting-safety; or b) the object is known to be neither thread-safe nor nesting-safe
The synchronisation objects in threading being examples of category a, file objects being examples of category b. In this scenario, generator contexts defined using @contextfactory should always be invoked directly in the context expression, as attempting to cache them in order to be reused won't work (you would need to put them in a zero-argument lambda and call it in the context expression, so that you get a new generator object each time).
Now we get to the crux of the matter. When I recommend that people write with foo.some_method(): <BLOCK> you are worried that if they need two separate blocks like that, they are tempted to write x = foo.some_method() with x: <BLOCK1> with x: <BLOCK2> But there's an obvious solution for that: make sure that the object returned by foo.some_method() can only be used once. The second "with x" will raise an exception explaining what went wrong. (We could even rig it so that nesting the second "with x" inside the first one will produce a different error message.) So the "with foo.some_method()" idiom is the only one that works, and that is just as thread-safe and nesting-resilient as is writing __context__() today. If you object against the extra typing, we'll first laugh at you (proposals that *only* shave a few characters of a common idiom aren't all that popular in these parts), and then suggest that you can spell foo.some_method() as foo().
Essentially, I don't think dropping __context__ would gain us anything - the complexity associated with it is real, and including that method in the API let's us deal with that complexity in one place, once and for all.
It's not real in 9 out of 10 current cases. (And the Condition variable could easily be restored to its previous case where it had its own __enter__ and __exit__ methods that called the corresponding methods of the underlying lock. The code currently in svn is merely an optimization.) -- --Guido van Rossum (home page: http://www.python.org/~guido/)
Guido van Rossum wrote:
I believe the context API design has gotten totally out of hand.
My thoughts exactly!
I have a counter-proposal: let's drop __context__... would it really be such a big deal to let the user make an explicit call to some appropriately named method?
Another possibility I thought of, for the case where an object is needed to keep track of the state of each invocation of a context, is to have the __enter__ method return the state object, which the with-statement tucks away and later passes to the __exit__ method. Also a thought on terminology. Even though it seems I may have been the person who thought it up originally, I'm not sure I like the term "manager". It seems rather wooly, and it's not clear whether a "context manager" is supposed to manage just one context or multiple contexts. I've been thinking about the terms "guarded context" and "context guard". We could say that the with-statement executes its body in a guarded context (an abstract notion, not a concrete object). To do this, it creates a context guard (a concrete object) with __enter__ and __exit__ methods that set up and tear down the guarded context. This seems clearer to me, since I can more readily visualise a "guard" object being specially commissioned to deal with one particular job (guarding a particular invocation of a context). With only one object, there wouldn't be a need for any more terms. But if another term is needed for an object with a __context__ method or equivalent, I rather liked Nick's "context specifier". -- Greg
Greg Ewing wrote:
I've been thinking about the terms "guarded context" and "context guard". We could say that the with-statement executes its body in a guarded context (an abstract notion, not a concrete object). To do this, it creates a context guard (a concrete object) with __enter__ and __exit__ methods that set up and tear down the guarded context. This seems clearer to me, since I can more readily visualise a "guard" object being specially commissioned to deal with one particular job (guarding a particular invocation of a context).
With only one object, there wouldn't be a need for any more terms.
contrast and compare: http://pyref.infogami.com/with http://pyref.infogami.com/with-alt http://pyref.infogami.com/with-guard a distinct term for "whatever the __enter__ method returns" (i.e. the thing assigned to the target list) would still be nice. </F>
Fredrik Lundh wrote:
a distinct term for "whatever the __enter__ method returns" (i.e. the thing assigned to the target list) would still be nice.
I've called that the "context entry value" in a few places (I don't think any of them were in the actual documentation though). A sample modification to the reference page: ------------------------------ Here's a more detailed description: 1. The context expression is evaluated, to obtain a context guard. 2. The guard object's __enter__ method is invoked to obtain the context entry value. 3. If a target list was included in the with statement, the context entry value is assigned to it. 4. The suite is executed. 5. The guard object's __exit__ method is invoked. If an exception caused the suite to be exited, its type, value, and traceback are passed as arguments to __exit__. Otherwise, three None arguments are supplied. ------------------------------ Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
Nick Coghlan wrote:
I've called that the "context entry value" in a few places (I don't think any of them were in the actual documentation though).
that doesn't really give me the right associations (I want something that makes it clear that this is an "emphemeral" object).
A sample modification to the reference page:
except for the exact term, that's a definite improvement. thanks! </F>
Greg Ewing wrote:
Also a thought on terminology. Even though it seems I may have been the person who thought it up originally, I'm not sure I like the term "manager". It seems rather wooly, and it's not clear whether a "context manager" is supposed to manage just one context or multiple contexts.
I think getting rid of __context__ should clear up most of this confusion (which is further evidence that Guido is making the right call). Once that change is made, the context expression in the with statement produces a context manager with __enter__ and __exit__ methods which set up and tear down a managed context for the body of the with statement. This is very similar to your later suggestion of context guard and guarded context. I believe this is actually going back to using the terminology as you originally suggested it (one concrete object, one abstract concept). We really only got into trouble once we tried to add a second kind of concrete object into the mix (objects with only a __context__ method). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
Nick Coghlan wrote:
the context expression in the with statement produces a context manager with __enter__ and __exit__ methods which set up and tear down a managed context for the body of the with statement. This is very similar to your later suggestion of context guard and guarded context.
Currently I think I still prefer the term "guard", since it does a better job of conjuring up the same sort of idea as a try-finally. There's also one other issue, what to call the decorator. I don't like @contextfactory, because it sounds like something that produces contexts, yet we have no such object. With only one object, it should probably be named after that object, i.e. @contextmanager or @contextguard. That's if we think it's really worth making it easy to abuse a generator in this way -- which I'm not convinced about. It's not as if people are going to be implementing context managers/guards every day. -- Greg
Greg Ewing wrote:
Nick Coghlan wrote:
the context expression in the with statement produces a context manager with __enter__ and __exit__ methods which set up and tear down a managed context for the body of the with statement. This is very similar to your later suggestion of context guard and guarded context.
Currently I think I still prefer the term "guard", since it does a better job of conjuring up the same sort of idea as a try-finally.
See the other message I wrote while you were writing this one for the various reasons I now agree with you :)
There's also one other issue, what to call the decorator. I don't like @contextfactory, because it sounds like something that produces contexts, yet we have no such object.
Agreed.
With only one object, it should probably be named after that object, i.e. @contextmanager or @contextguard. That's if we think it's really worth making it easy to abuse a generator in this way -- which I'm not convinced about. It's not as if people are going to be implementing context managers/guards every day.
I suggested renaming it to "guardfactory" in my other message. Keeping the term 'factory' in the name emphasises that the decorator results in a callable that returns a context guard, rather than producing a context guard directly. As for whether or not we should provide this ability, I think we definitely should. It allows try/finally boilerplate code to be replaced with a guarded context almost mechanically, whereas converting the same code to a manually written context guard could involve significant effort in changing from local variable based storage to instance attribute based storage. IOW, the feature is provided for the same reason that generator functions are provided: it is typically *much* easier to write a generator function than it is to write the same iterator manually. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
On Tue, May 02, 2006, Greg Ewing wrote:
Nick Coghlan wrote:
the context expression in the with statement produces a context manager with __enter__ and __exit__ methods which set up and tear down a managed context for the body of the with statement. This is very similar to your later suggestion of context guard and guarded context.
Currently I think I still prefer the term "guard", since it does a better job of conjuring up the same sort of idea as a try-finally.
"Guard" really doesn't work for me. It seems clear (especially in light of Fredrik's docs) that "wrapper" comes much closer to what's going on. -- Aahz (aahz@pythoncraft.com) <*> http://www.pythoncraft.com/ "Argue for your limitations, and sure enough they're yours." --Richard Bach
Nick Coghlan wrote:
Greg Ewing wrote:
Also a thought on terminology. Even though it seems I may have been the person who thought it up originally, I'm not sure I like the term "manager". It seems rather wooly, and it's not clear whether a "context manager" is supposed to manage just one context or multiple contexts.
I think getting rid of __context__ should clear up most of this confusion (which is further evidence that Guido is making the right call). Once that change is made, the context expression in the with statement produces a context manager with __enter__ and __exit__ methods which set up and tear down a managed context for the body of the with statement. This is very similar to your later suggestion of context guard and guarded context.
Thinking about it a bit further. . . 1. PEP 343, 2.5 alpha 1, 2.5 alpha 2 and the discussions here have no doubt seriously confused the meaning of the term 'context manager' for a lot of people (you can certainly put me down as one such person). Anyone not already confused is likely to *become* confused if we subtly change the meaning in alpha 3. 2. The phrase "managed context" is unfortunately close to .NET's term "managed code", and would likely lead to confusion for IronPython folks (and other programmers with .NET experience) 3. "manager" is an extremely generic term that is already used in a lot of different ways in various programming contexts Switching to Greg's suggestion of "context guard" and "guarded context" as the terms would allow us to hit the reset button and start the documentation afresh without terminology confusion resulting from the evolution of PEP 343 and its implementation and documentation. I think context guard also works better in terms of guarding entry to and exit from the guarded context, whereas I always wanted to call those operations "set up" and "tear down" for context managers. The current @contextfactory decorator could be renamed to @guardfactory to make it explicit that it results in a factory function for context guards. Cheers, Nick. P.S. I think I can hear anguished howls coming from the offices of various book publishers around the world ;) -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
On May 1, 2006, at 8:15 AM, Nick Coghlan wrote:
1. PEP 343, 2.5 alpha 1, 2.5 alpha 2 and the discussions here have no doubt seriously confused the meaning of the term 'context manager' for a lot of people (you can certainly put me down as one such person). Anyone not already confused is likely to *become* confused if we subtly change the meaning in alpha 3.
Don't forget that the majority of users will never have heard any of these discussions nor have used 2.5a1 or 2.5a2. Choose the best term for them, not for the readers of python-dev. James
On 5/1/06, James Y Knight <foom@fuhm.net> wrote:
Don't forget that the majority of users will never have heard any of these discussions nor have used 2.5a1 or 2.5a2. Choose the best term for them, not for the readers of python-dev.
I couldn't agree more! (Another thought, occasionally useful,is to consider that surely the number of Python programs yet to be written, and the number of Python programmers who yet have to learn the language, must surely exceed the current count. At least, one would hope so -- if that's not true, we might as well stop now. :-) Nick, do you have it in you to fix PEP 343? Or at least come up with a draft patch? We can take this off-linel with all the +0's and +1's coming in I'm pretty comfortable with this change now, although we should probably wait until later today to commit. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
Guido van Rossum wrote:
On 5/1/06, James Y Knight <foom@fuhm.net> wrote:
Don't forget that the majority of users will never have heard any of these discussions nor have used 2.5a1 or 2.5a2. Choose the best term for them, not for the readers of python-dev.
I couldn't agree more! (Another thought, occasionally useful,is to consider that surely the number of Python programs yet to be written, and the number of Python programmers who yet have to learn the language, must surely exceed the current count. At least, one would hope so -- if that's not true, we might as well stop now. :-)
If reason 1 had been my only reason for agreeing with Greg, I wouldn't have said anything :) The conflict with 'managed code' and thinking about the number of objects named 'manager' I've seen in different programs were enough to give me pause, though. I've got no real idea as to how we can get a better feel for which terminology would be clearer to people that haven't already been exposed to this topic for months, though :(
Nick, do you have it in you to fix PEP 343? Or at least come up with a draft patch? We can take this off-linel with all the +0's and +1's coming in I'm pretty comfortable with this change now, although we should probably wait until later today to commit.
I can handle the PEP (just getting rid of __context__ for now). I'll willingly cede the joy of actually fixing SVN to someone else, though :) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
On 5/2/06, Nick Coghlan <ncoghlan@gmail.com> wrote:
Nick, do you have it in you to fix PEP 343? Or at least come up with a draft patch? We can take this off-linel with all the +0's and +1's coming in I'm pretty comfortable with this change now, although we should probably wait until later today to commit.
I can handle the PEP (just getting rid of __context__ for now). I'll willingly cede the joy of actually fixing SVN to someone else, though :)
OK, if you fix the PEP, I'll fix the code to match; I added most of those __context__ methods so I can delete them. Unless someone else beats me to it. :-) -- --Guido van Rossum (home page: http://www.python.org/~guido/)
participants (8)
-
Aahz
-
Brett Cannon
-
Fredrik Lundh
-
Greg Ewing
-
Guido van Rossum
-
James Y Knight
-
Nick Coghlan
-
Nick Coghlan