On Nov 19, 2019, at 18:53, Oscar Benjamin firstname.lastname@example.org wrote:
On Wed, 20 Nov 2019 at 02:36, Richard Damon Richard@damon-family.org wrote:
If I understand it right, an eager context manager (like open currently is) allows itself to be used somewhat optionally, and not need to be in a with statement. A lazy context manager on the other hand, seems to be assuming that it will be used in a with statement (or something similar), as it is putting off some important work until it sees it is in there.
It impetus for this seems to be to make a cleaner syntax for a with statement managing multiple resources through context managers, if we only need simple recovery (the built in provided by the context manager). It may be at the cost of making more complex (and explicit) handling of conditions for current (or possible future) 'eager' managers. For example, if open doesn't actually open the file, but only prepares to open, then the use of a file outside a with becomes more complicated if we want somewhat precise control over the file.
The idea would be (or rather would have been) that open still does the same thing it did before and returns a file object with the usual methods. However that file object would *not* be a context manager. If you wanted a context manager for the file then you would have used opened (from PEP 343) rather than open and opened would give a context manager whose __enter__ would return the same file object that open otherwise returns. So it doesn't make other uses of the file object more complicated except that you choose whether to call open or opened depending on how you want to use the file.
Would you also have added sqlite3.connected, mmap.mmapped, socket.madefile, requests.got (this name scheme doesn’t extend as well as I thought it would…), etc.?
At first it seems like a good idea, but ultimately it means you need two different functions or methods for every kind of resource.
Maybe a generic helper is a better idea. For the 80% case (where enter just has to call the function and stash and return the result, and exit just calls a close method on that result), instead of this:
with closing(urlopen(url)) as page:
… you do this:
with context(urlopen, url) as page:
… which gives you a lazy context manager that doesn’t call urlopen until enter.
And for cases where the exit isn’t as simple as calling close, but is simple enough to just be a function call, and where the enter function doesn’t take an exit kwarg:
with context(FilesCache, exit=FilesCache.shutdown) as fc:
For anything more complicated you still have to write a custom context manager, but that’s not a huge deal considering how rare it is.
Now you don’t need opened and mmapped and so on; if you are calling one of them repeatedly, just define it locally:
opened = partial(context, open)
More importantly, you don’t need files, db connections, network requests, mmaps, and other resources to be context managers, you just need them to be compatible with context (which most of them already are, and even more would be if there were a concrete advantage to it). So there’s no point in eager context managers—sure, someone could go out of their way to write one, but then you probably could get away with saying that it’s “broken”, unlike today.
This is still more verbose than current Python, and passing a function and its args doesn’t look as nice as just calling the function—but remember that you can still trivially partial any resource constructor you plan to context manage repeatedly. (And if that’s not good enough, you could elevate building and entering a context manager around a construction expression into syntactic sugar, but I doubt that would be needed.)
But unless you have a time machine back to 2007, I still don’t think this is at all feasible, because of all of the code that would have to change, including half the existing third-party libraries that provide context managers and even more applications that rely on eager context manager behavior. So we’re still not talking about an idea for Python here. The idea in my other email, providing a wrapper to turn eager context managers lazy and an ABC to distinguish them and so on, may not be nearly as clean, but it seems a lot more feasible.