On Tue, 19 Nov 2019 at 08:21, Paul Moore
On Mon, 18 Nov 2019 at 23:42, Oscar Benjamin
wrote: Context managers and the with statement are all about assigning responsibility so there needs to be an understanding of what has responsibility for what at any given time.
Somewhere (I can't recall where) there's an example of context managers that allow you to do something like
with html(): with body(): with div(class="main-panel"): etc...
Whether that counts as a clever usage, or an abuse, is somewhat a matter of opinion.
I think that's fine as a use of context managers. I assume that the intention is that the CM returned by html() is responsible for adding both the opening and closing html tags. It isn't clear to me from the example how errors are expected to be handled though - would you still want to add the closing tag or would you just catch the error higher up and return a completely different error document to the user.
I'm also not clear whether the rules you are proposing would allow or prohibit such a use.
I'm also not clear what nested(html(), body()) might mean. It's possible to work out the behaviours from just the definition of the mechanism. But I'm not clear how I'd interpret a rule about "what has responsibility for what" in this example.
Conceded, that's a general statement not a specific rule. But the rule you gave earlier:
If there was a requirement on context managers that __exit__ cleans up after __enter__ and any resource that needs cleaning up should only be acquired in __enter__ then there would never have been a problem with nested.
doesn't really apply here, as I don't think of the HTML example in terms of "resources".
To look at this another way, using the example of open again, what open() returns isn't an object that manages a resource - it's the resource *itself* (the open file). The fact that the resource can be self-managing at all is a result of the fact that the CM protocol and the with statement are defined purely in terms of a mechanism. Open file objects aren't closed using __exit__(), they are closed using close(). But by making __enter__() do nothing and __exit__() call close, you can make file objects work with the with statement. But you can't make a file object's __enter__ method "acquire any resource that needs cleaning up", because the __enter__ is a method on that resource in the first place.
So your proposal is really saying that self-managing resources are disallowed, and all resources need a *pair* of classes - one to represent the resource, and one to represent a "manager" for that resource. That's possible (that's what the opened() example in the with statement PEP did) but it's more restrictive than the current context manager protocol. The restrictions allow you do make more assumptions, and hence write certain functions that you otherwise couldn't, but do the benefits justify the costs?
On Tue, 19 Nov 2019 at 07:33, Greg Ewing
wrote: I think a better way to say it would be that the __enter__ method should be atomic -- it should either acquire all the resources it needs or none of them. Then it's clear that the with statement should call __exit__ if and only if __enter__ does not raise an exception.
I like that characterisation better, but it still makes an implicit assumption that all context managers own the resources they manage. Which disallows self-managing resources.
Paul