On Nov 15, 2019, at 04:37, Jonathan Fine <jfine2358@gmail.com> wrote:

Serhiy suggests
https://docs.python.org/3/library/contextlib.html#contextlib.ExitStack
>>> with ExitStack() as stack:
>>>       files = [stack.enter_context(open(fname)) for fname in filenames]

A simpler alternative, for the OP's original post, could be:
>>> with ExitMap(open, ['a', 'b', 'c']) as a, b, c:
>>>     pass
where ExitMap(*args) is equivalent to helper(map(*args)). Of course, using ExitStack is the easy way to code ExitMap.

The advantage of your ExitMap over helper (besides being shorter, and conceptually simpler) is that it’s never misleading.

    with helper(open(fn) for fn in (fna, fnb, fnc)) as a, b, c:

This is fine. If open(fnb) raises, helper has already stacked up open(fna) and can exit it. But change the genexpr to a listcomp:

    with helper([open(fn) for fn in (fna, fnb, fnc)]) as a, b, c:

This looks the same, and seems fine if you don’t test error cases, but if open(fnb) fails, helper never gets called so nothing gets stacked up so open(fna) leaks.

And calling it with an iterable you create out-of-line can make it even more confusing because it’s not as clear where to look when you accidentally make the iterable not lazy. Not to mention that it could even be a properly lazy Iterator but one that needs to be fully consumed for your program logic (not an issue for a simple map over a tuple of strings), and that would be an even more subtle bug.

If you could clearly document and/or test for the requirements, maybe helper would be useful. But your ExitMap doesn’t rely on you correctly building the right iterator; it builds the Iterator itself, and doesn’t expose parts that can be misused. So it’s trivial to document, and to learn and use.

It still seems like overkill for the case of “how do I get parens somewhere around a bunch of open calls so I can write a multiline with statement”, and I’m not sure how often it would be useful in other cases. But it seems like it’s at least worth building for your personal toolbox and keeping track of how often it comes up (and how much nicer it makes things), and maybe publishing it to PyPI or submitting it to contextlib2 so more people will do the same.