Context managers in expressions
I've been thinking for a while that it would be good to be able to use context managers in an expression context, and I haven't been able to come up with any arguments against it (although it may not be to everyone's taste) and I can't see any sign that it's come up before (although the words of it don't make very specific search terms). My suggestion is that a "with" clause could be written after an expression (consistent with the conditional operator being written with "if" after the first expression). In general, this would be: a(b) with c(d) as b or it could allow multiple context managers: a(b, c) with d(e) as b, f(g) as c My original motivating example was this: if name.startswith("https://"): data = requests.get(name).json() else: with open(name) as stream: data = json.load(stream) which I'd much rather be able to write with a single expression: data = (requests.get(name).json() if name.startswith("https://") else (json.load(stream) with open(name) as stream)) It would behave just like a "with" statement, but pass a value / values back. I don't think there would be any backward-compatibility issues with this.
What should happen if the context manager attempts to suppress a raised exception? In cases where you applied the context manager to an entire line, e.g. data = fail() with contextlib.suppress(Exception) Then it would make sense to treat it like with contextlib.suppress(Exception): data = fail() Where `data` remains unassigned after the block executes assuming `fail` raises an exception. However, with the initial proposal you run into trouble when you apply this to sub-expressions that are expected to themselves have a value. For example, what should happen here? more_work(fail() with contextlib.suppress(Exception)) We have no value to pass as an argument to `more_work` so there's no way we can call it. Yet it would be odd to not call it if there's no exception being raised since it exists outside of any context manager itself.
Thanks, Mark... I had missed that aspect entirely. I think there might be a way round it... which actually was something I had been thinking of including in the same suggestion but thought was perhaps too obscure, but now I've realized it would integrate well with it. That is to have a value-returning form of try... except... But perhaps the two of them together are too much of a change. John On Mon, Oct 25, 2021 at 10:03 PM Mark Gordon <msg555@gmail.com> wrote:
What should happen if the context manager attempts to suppress a raised exception? In cases where you applied the context manager to an entire line, e.g.
data = fail() with contextlib.suppress(Exception)
Then it would make sense to treat it like
with contextlib.suppress(Exception): data = fail()
Where `data` remains unassigned after the block executes assuming `fail` raises an exception. However, with the initial proposal you run into trouble when you apply this to sub-expressions that are expected to themselves have a value. For example, what should happen here?
more_work(fail() with contextlib.suppress(Exception))
We have no value to pass as an argument to `more_work` so there's no way we can call it. Yet it would be odd to not call it if there's no exception being raised since it exists outside of any context manager itself. _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/Y7WZDD... Code of Conduct: http://python.org/psf/codeofconduct/
25.10.21 21:26, jcg.sturdy@gmail.com пише:
I've been thinking for a while that it would be good to be able to use context managers in an expression context, and I haven't been able to come up with any arguments against it (although it may not be to everyone's taste) and I can't see any sign that it's come up before (although the words of it don't make very specific search terms). My suggestion is that a "with" clause could be written after an expression (consistent with the conditional operator being written with "if" after the first expression).
In general, this would be: a(b) with c(d) as b or it could allow multiple context managers: a(b, c) with d(e) as b, f(g) as c
My original motivating example was this:
if name.startswith("https://"): data = requests.get(name).json() else: with open(name) as stream: data = json.load(stream)
which I'd much rather be able to write with a single expression:
data = (requests.get(name).json() if name.startswith("https://") else (json.load(stream) with open(name) as stream))
It would behave just like a "with" statement, but pass a value / values back.
I was going to propose this idea for discussion but did not have enough time to formulate it. It is not just a "what if we combine two random features idea", it would cover most cases of recurrent requests for adding functions which merge open() and json.load(), etc or adding new very specialized methods to the Path object. Currently you can write with open(filename, 'rb') as f: data = json.load(f) The only minor drawback of this idiom is that it is not expression (but you always can write a new function). If you could write data = json.load(f) with open(filename, 'rb') as f this counter-argument will disappear. Now, Mark Gordon asked a good question -- what to do if the context manager suppresses an exception. We do not have value and we do not have exception. The only answer I have -- raise a RuntimeError (or other specialized exception). It should be an error to use context managers which suppress exceptions in the "with" expression. Maybe there are better ideas. Before adding the "with" expressions we should consider other feature: the "with" clause in comprehensions. Currently comprehensions can contain "for" and "if" clauses. There should be at least one "for" clause and it should be the first clause. I propose to add the with clause. It will be interpreted the same way as "if" and nested "for" clauses. [expr for x in iterable if cond() with cm() as y] should be equivalent to: result = [] for x in iterable: if cond(): with cm() as y: result.append(expr) It should be discussed because there is a conflict between its syntax and the "with" expression. The "with" clause should have priority over the "with" expression (as the "if" clause has priority over the "if" expression), but it is a breaking change if the latter will be implemented first.
participants (4)
-
jcg.sturdy@gmail.com
-
John Sturdy
-
Mark Gordon
-
Serhiy Storchaka