On Mon, 18 Nov 2019 at 22:00, Paul Moore <p.f.moore@gmail.com> wrote:
On Mon, 18 Nov 2019 at 18:34, Oscar Benjamin <oscar.j.benjamin@gmail.com> wrote:
On Mon, 18 Nov 2019 at 17:46, Paul Moore <p.f.moore@gmail.com> wrote:
I think that nested was fine but in combination with open it was prone to misuse. By the time with/contextlib etc had shipped in 2.5 it was easier to blame nested (which also had other flaws) so it took the fall for open.
I think that nested made assumptions about how context managers worked that weren't always true in practice. That's acceptable (if a little risky) as long as it's easy to know its limitations and not use it with inappropriate CMs. Unfortunately, it was extremely tempting to use it with open() which didn't satisfy the additional requirements that nested() imposed, and that made it an attractive nuisance.
It's not really fair to say that nested imposed additional requirements. It required to receive arguments and actually get called!
Treating the problem as being caused by nested() not working properly with *all* context managers was relatively easy, as nested was new. Trying to rework open to fit the expectations of nested would have been far harder (because open has been round so long that backward compatibility is a major issue), as well as not helping in the case of any *other* CMs that didn't work like nested expected them to.
The __enter__ and __exit__ methods of file objects were new in the same release as with/nested etc but I guess that it was all new at the time and people were just getting used to with so nested would have seemed like a more obscure case.
Anyway, I already said that where you choose to draw the line over what a context manager is (assuming you feel that the current definition is wrong), depends on your perspective.
It's not so much that the definition is wrong. It just isn't really defined and that makes it difficult to do anything fancy with multiple context managers. You need protocol contraints on both sides to be able to build useful utilities/patterns. The bar set for nested was that it should be able to recover from errors before it even gets called!
The definition is clearly and precisely implied by the PEP and the semantics of the with statement. A CM is anything that has __enter__ and __exit__. See https://www.python.org/dev/peps/pep-0343/#standard-terminology.
That definition is sufficient to clarify the terminology but it does nothing to define the expected behaviour of the context managers themselves - what should those methods do? 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.
Maybe it "makes it difficult to do anything fancy with multiple context managers" - I don't know about that. It feels like a fairly large generalisation to get from "nested doesn't work the way we'd like it to" to that statement.
The precise limitation on any CM utility is that it must only receive one context manager at a time in any given function or method call. If it receives two or more - whether as separate arguments or in a container - then in combination with open it will suffer the same problem as nested. Designing an API around that limitation is awkward: probably exitstack can't be improved upon in that respect. -- Oscar