[Python-ideas] Re: Proposal: Allowing any variable to be used in a 'with... as...' expression

2019-06-23 Thread Steven D'Aprano
On Sat, Jun 22, 2019 at 12:14:02PM -0400, James Lu wrote:

> If a function that tends to return a context manager returns None, 
> that should not mean an error occurred.

I suppose that *technically* this is true. It might be designed to 
return a context manager or None. But:

1. Because of the way Python returns None from functions when you fall 
out the end without an explicit return, "returns None is a programming 
error (a bug)" is the safe way to bet.

2. If it is intentional, and documented, the usual practice is to 
explicitly check for the exceptional value:


mo = re.match(a, b)
if mo:
print(mo.group())


We don't give None a .group() method that returns None, so that lazy 
coders can write this and suppress the exception:


assert hasattr(None, "group")
print(re.match(a, b).group())  # doesn't raise any more, yay!


because there are too many ways that None *is* an error and we don't 
want to cover them up. Getting an exception if you have an unexpected 
None is a *good thing*.

So I don't think that with blocks should just automatically skip running 
None. For every context manager that intentionally returns None, there 
are probably a hundred that do it accidentally, where it is a bug.


I can see at least three ways to deal with the very few "context manager 
or None" cases, that require no changes to the language:

1. Have the context manager explicitly return None and require the 
caller to explicitly check for it:


x = Context(arg)
if x is not None:
with x:
...


This adds two lines and one extra level of indentation, which is not too 
bad for something self-documenting and explicit. In Python 3.8 we can 
reduce it to one line and one extra level of indentation:


if (x := Context(arg)) is not None:
with x:
...


which might be the best solution of all.

2. Have the context manager raise an exception (not an error!) and 
require the caller to explicitly catch it:


try:
with Context(arg) as x:
...
except ExceptionalCase:
pass


This adds three lines and one extra level of indentation. On the other 
hand, if your context manager can raise on actual errors as well, this 
is the most natural way to solve the problem:


try:
with Context(arg) as x:
...
except SomethingBadError as err:
handle(err)
except ExceptionalCase:
pass


3. Write the context manager to return a do-nothing mock-up instead of 
None. You might be able to use the Mock object in the standard library 
for this (I have never used Mock, so I don't know if it is suitable) but 
worst case it adds a one-off cost to the writer of the context manager, 
but it gives the caller do-nothing handling for free:


with ContextOrMock(arg) as x:
...


If x happens to be a do-nothing mock, the code in the block will do 
nothing.


> If an error or unexpected 
> condition occurred, an exception should be thrown. Errors and 
> exceptions should result in the code within the with statement not 
> executing.

Isn't that how the with statement already works?


> We could add a new constant to Python, “Dont.” It’s a global falsey 
> Singleton and a context manager that causes the code inside “with” not 
> to execute.

Don't what? I might be more sympathetic to that if the name was more 
descriptive. Say, "SkipWithBlock", and we make it an exception.

To skip the with-block, have the context manager raise SkipWithBlock 
from inside its __enter__ method.

This is less disruptive because:

- it won't hide the exception from buggy context managers that 
  accidentally return None;

- it requires an explicit raise inside the context manager;

- although it adds a new built-in, it is an exception, and 
  psychologically people tend to think of exceptions as seperate 
  from the builtins (at least *I* do, and people I've spoken to).


All this supposes that there is a moderately common need for a context 
manager to skip the with block, and that the existing solutions aren't 
sufficient.




-- 
Steven
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@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/4XSBZO5BCZRQVUKN5SYJZOL5LJGI4HWX/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Proposal: Allowing any variable to be used in a 'with... as...' expression

2019-06-23 Thread Chris Angelico
On Mon, Jun 24, 2019 at 4:03 AM James Lu  wrote:
>
> If a function that tends to return a context manager returns None, that 
> should not mean an error occurred. If an error or unexpected condition 
> occurred, an exception should be thrown. Errors and exceptions should result 
> in the code within the with statement not executing.
>
> We could add a new constant to Python, “Dont.” It’s a global falsey Singleton 
> and a context manager that causes the code inside “with” not to execute.
>

A cleaner way to handle this would be an exception. Unfortunately, the
simple trick of suppressing the exception in __exit__ doesn't work, as
__exit__ isn't called if __enter__ itself fails. Maybe you can use
ExitStack to manage this?

ChrisA
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@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/IOG5IH6QWKAEABUCPTLZHV7GDB424UM5/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-ideas] Re: Proposal: Allowing any variable to be used in a 'with... as...' expression

2019-06-23 Thread James Lu
If a function that tends to return a context manager returns None, that should 
not mean an error occurred. If an error or unexpected condition occurred, an 
exception should be thrown. Errors and exceptions should result in the code 
within the with statement not executing.

We could add a new constant to Python, “Dont.” It’s a global falsey Singleton 
and a context manager that causes the code inside “with” not to execute.

> On May 18, 2019, at 10:44 PM, Yonatan Zunger  wrote:
> 
> an
___
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@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/VJ2ZRJKPNUWQZ3UHJN26X236E5REVUXW/
Code of Conduct: http://python.org/psf/codeofconduct/