Now that Python supports chaining multiple context managers with a single with or async with statement, I think that it would be nice if contextlib.suppress would also support both sync and async context managers.
from contextlib import suppress
import aiofiles
import json
with suppress(FileNotFoundError), open("config.json") as f:
config = json.load(f)
# do smthg that is not critical
# TypeError TypeError: 'suppress' object does not support the asynchronous context manager protocol
async with suppress(FileNotFoundError), aiofiles.open("config.json") as f:
content = await f.read()
config = json.loads(content)
# do smthg that is not critical
As we see suppress doesnât implement __aenter__ / __aexit__, so itâs not usable in async with, even though it feels it should work just fine.
Of course we can currently do:
# compliant way
with suppress(FileNotFoundError)
async with aiofiles.open("config.json") as f:
content = await f.read()
config = json.loads(content)
# do smthg that is not critical
And this is fine, but i donât find it as elegant. specially when the suppress may break a block of 2-3 async context managers that could be merged together, Anyone else sees this as a good idea?
Is not the same thing, he proposes to allow always to mix the syntax, which is confusing because because if you do that, we canât have two different behaviors for aenter and __enter__and discriminate them,
This doesnât apply to suppress though, as there is only one expected behavior both sync or async.
That option is indeed better, but is still far more disruptive than my suggestion.
I only suggest to add aenter, aexit to a class in the std library, and that suggests to change the language itself Of course if that materializes this wonât be needed, but realistically I donât think that idea it will happen anytime soon anyways⌠So why not to do this ?
Both your idea and the proposed syntax extension will happen in 3.15 at the earliest, so I donât see much of a case for adding just your smaller idea. Maybe if the PEP is rejected.
My objection is the same as to the âas_acm() helperâ rejected in PEP 806: adding __aenter__ would either break the transitive property that âasync with marks a location where it is possible to switch or cancel tasksâ, or else lock in a particular async framework as the only option.
As a Trio enthusiast and maintainer, Iâm not keen on either option! Better to make mixing sync and async context managers more ergonomic
Hi, thanks for the feedback, but maybe could you elaborate more on that please?
I donât see how will break any property that is not broken already by any class that implements both __enter__ and __aenter__, Iâm not suggestiong that __aenter__ in âsuppressâ should have any special treatment, so i would not be surprise if python switch or cancel a task there.
But yes I also think is fair to wait for PEP 806 to be accepted / rejected to implement this or not.
The trick is that the async framework can only switch or cancel a task if you await all the way back into it; unlike the operating system thread scheduler. Trioâs docs offer a nice overview.
So itâs fine to define both __enter__ and __aenter__, but in order to act as a checkpoint the latter has to e.g. await <x>.sleep(0). And how do we decide which framework x should be? For third-party libraries, anyio.sleep(0) will automatically use asyncio or trio as appropriate, but thatâs not really an option for the standard libraryâŚ
Thanks! now i understand your point way better. To be honest I would also not be bother if there is not a call to sleep at all, but i guess this is what you said that would may come unexpected behaviour (breaking the transitive property) , given that there will be at least another context manager that will implement it I donât see it as much as an issue, but In any case we can check what is the resolution of the other PEP to see if this makes senses in the first place.