Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-18 Thread Greg Ewing

Ram Rachum wrote:
1. I'm making a program that lets people lease machines. They can issue 
a command to lease 7 machines. ... If everything goes fine, I do pop_all on
the exit stack so it doesn't get exited and the machines stay leased, 


Seems to me that would be done more easily and clearly
without involving a context manager at all:

   machines = []
   try:
  for i in range(num_machines_required):
 machines.append(lease_machine())
   except AllMachinesInUseError:
  for machine in machines:
 release_machine(machine)
  del machines[:]

A context manager is a tool to be used if it helps. If it
doesn't help, don't be afraid to not use it!

2. I have a test suite that creates a temporary folder to put files that 
are used by the test. ... But, if the test fails I want to 
move the temporary folder away into a dedicated folder


Again, don't use a context manager, just write a try-except
that does what you want.

--
Greg
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-18 Thread Ethan Furman

On 11/18/2016 12:42 PM, Ram Rachum wrote:


Sure, here are a couple of use cases:

1. I'm making a program that lets people lease machines. They can issue
 a command to lease 7 machines. When they do, my program leases them one
 by one and adds them all to an exit stack, so in case there aren't 7
 machines available, all the machines we leased get released and the
 situation is back to normal. If everything goes fine, I do pop_all on
 the exit stack so it doesn't get exited and the machines stay leased,
 then the command exits and the user gets his machines.


So you're using the contextlib.ExitStack context manager?


2. I have a test suite that creates a temporary folder to put files that
 are used by the test. The temporary folder is created by a context
 manager that deletes it at the end. But, if the test fails I want to
 move the temporary folder away into a dedicated folder for the user to
 be able to examine those files later to figure out why the test fails.
 So I want to tell the temporary folder context manager to not delete
 the folder, because it'll fail since it was moved away so it's not at
 the expected location.


My first thought is to make a custom context manager for this use-case, but if 
you really want to use a generator instead can't you just put in an existence 
check for the directory and only delete if it is still there?

--
~Ethan~
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-18 Thread Ram Rachum
Sure, here are a couple of use cases:

1. I'm making a program that lets people lease machines. They can issue a
command to lease 7 machines. When they do, my program leases them one by
one and adds them all to an exit stack, so in case there aren't 7 machines
available, all the machines we leased get released and the situation is
back to normal. If everything goes fine, I do pop_all on the exit stack so
it doesn't get exited and the machines stay leased, then the command exits
and the user gets his machines.

2. I have a test suite that creates a temporary folder to put files that
are used by the test. The temporary folder is created by a context manager
that deletes it at the end. But, if the test fails I want to move the
temporary folder away into a dedicated folder for the user to be able to
examine those files later to figure out why the test fails. So I want to
tell the temporary folder context manager to not delete the folder, because
it'll fail since it was moved away so it's not at the expected location.

(If you're replying please keep me in "to" because the Gmail filter I set
up isn't smart enough to let the messages in otherwise.)

On Nov 18, 2016 21:27, "Sven R. Kunze"  wrote:

> On 06.11.2016 09:07, Steven D'Aprano wrote:
>
>> I'm having a lot of difficulty in understanding your use-case here, and
>> so maybe I've completely misunderstood something.
>>
>
> Although, this thread is dead for a week or so, I am still curious to hear
> the real-world use-case. I am equally puzzled by the fact that somebody
> really wants to use a context manager not to work like a context manager;
> without even considering not to use context managers at all and using
> regular functions instead.
>
> Cheers,
> Sven
>
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-18 Thread Sven R. Kunze

On 06.11.2016 09:07, Steven D'Aprano wrote:
I'm having a lot of difficulty in understanding your use-case here, 
and so maybe I've completely misunderstood something. 


Although, this thread is dead for a week or so, I am still curious to 
hear the real-world use-case. I am equally puzzled by the fact that 
somebody really wants to use a context manager not to work like a 
context manager; without even considering not to use context managers at 
all and using regular functions instead.


Cheers,
Sven
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-06 Thread Ethan Furman

On 11/06/2016 08:11 PM, Nick Coghlan wrote:

On 7 November 2016 at 12:25, Ethan Furman  wrote:

On 11/06/2016 12:44 AM, Ram Rachum wrote:


I see that Python does allow you to not call `__exit__` if you don't want
  to [...]


Um, how?  I was unaware of this (mis-)feature.


It involves wrapping the context manager in another context manager
that deliberately doesn't delegate the call to __exit__ in some cases
(cf contextlib.ExitStack.pop_all()).


Ah, okay.

Perhaps a custom iterator class would suit Ram's needs better than using a 
generator shortcut.

--
~Ethan~
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-06 Thread Nick Coghlan
On 7 November 2016 at 12:25, Ethan Furman  wrote:
> On 11/06/2016 12:44 AM, Ram Rachum wrote:
>
>> I see that Python does allow you to not call `__exit__` if you don't want
>>  to [...]
>
> Um, how?  I was unaware of this (mis-)feature.

It involves wrapping the context manager in another context manager
that deliberately doesn't delegate the call to __exit__ in some cases
(cf contextlib.ExitStack.pop_all()).

By contrast, if __del__ is defined (as it is on generators), if you
don't keep the context manager itself alive, you can only prevent the
cleanup happening if you can define a subclass to use instead, and
that's not always possible (deliberately so, in the case of generator
cleanup).

So the odd part of Ram's request isn't wanting to have conditional
resource cleanup - the recipes in the contextlib docs gives some
examples of where conditional local resource management is useful and
how to achieve it using ExitStack. The odd part is wanting to make the
resource cleanup implicitly unreliable, rather than having it be
reliable by default and folks having to explicitly opt in to disabling
it, since the easiest way to obtain non-deterministic resource
management is to just avoid using the context management features in
the first place.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-06 Thread Ethan Furman

On 11/06/2016 12:44 AM, Ram Rachum wrote:


I see that Python does allow you to not call `__exit__` if you don't want
 to [...]


Um, how?  I was unaware of this (mis-)feature.

--
~Ethan~
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-06 Thread Ethan Furman

On 11/06/2016 12:18 AM, Ram Rachum wrote:


Well, you think it's weird that I want a `finally` clause to not be called
 in some circumstances.


Yes I (we) do.


 Do you think it's equally weird to want an `__exit__` method that is not
 called in some circumstances?


Yes I (we) do.

--
~Ethan~
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-06 Thread Terry Reedy

On 11/6/2016 2:18 AM, Ram Rachum wrote:


Well, you think it's weird that I want a `finally` clause to not be
called in some circumstances. Do you think it's equally weird to want an
`__exit__` method that is not called in some circumstances?


Without a deeper understanding of why you want to do so, I would.  The 
automatic exit cleanup typically the main purpose of a context manager 
and 'with' block.  If, for instance, I want a file only maybe closed, I 
would just call 'open' instead of 'with open' and then conditionally 
(with 'if' or 'try') call 'close'.


--
Terry Jan Reedy

___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-06 Thread Nick Coghlan
On 6 November 2016 at 17:44, Ram Rachum  wrote:
> I understand your point of view. I see that Python does allow you to not
> call `__exit__` if you don't want to, so I wish it'll have the same approach
> to not calling `generator.close()` if you don't want to. (This is what it's
> really about, not `finally`.)

No, as that's like asking that Python not call close() on files
automatically, or not wait for non-daemon threads to terminate when
it's shutting down.

When Python is discarding a frame that was previously suspended and
never finished normally, it throws an exception into it in order to
give it a chance to release any resources it might be holding. If you
want to deliberately make it leak resources in such cases instead of
cleaning them up, you're going to have to leak them deliberately and
explicitly, just as you would in normal synchronous code.

Regards,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-06 Thread Steven D'Aprano
On Sun, Nov 06, 2016 at 06:46:40AM +0200, Ram Rachum wrote:
> Hi everyone,
> 
> Here is a simplification of a problem that's been happening in my code:
> 
> import contextlib
> 
> @contextlib.contextmanager
> def f():
> print('1')
> try:
> yield
> finally:
> print('2')
> 
> 
> g = f()
> g.__enter__()
> 
> 
> This code prints 1 and then 2, not just 1 like you might expect.

I expect it to print 2. After all, its in a finally clause.

And why are you calling g.__enter__() directly? Calling dunder methods 
by hand is nearly always the wrong thing to do.

> This is
> because when the generator is garbage-collected, it gets `GeneratorExit`
> sent to it.

Right. That's what they're designed to do.

Later, in another thread you say:

"Well, you think it's weird that I want a `finally` clause to not be 
called in some circumstances. Do you think it's equally weird to want an
`__exit__` method that is not called in some circumstances?"

Yes to both. It is seriously weird.

You might as well be complaining that Python calls your __iter__ 
method when you call iter(my_instance). That's the whole point of 
__iter__, and the whole point of finally clauses and the __exit__ method 
is that they are unconditionally called, always, when you leave the 
with block.

But judging from your code above, it looks like you're not even using a 
with block. In that case, instead of abusing the __enter__ and __exit__ 
methods, why not just create a class with non-dunder enter() and exit() 
methods and call them by hand?

g = f()  # implementation of f is left as an exercise
g.enter()
if condition:
g.exit()


I'm having a lot of difficulty in understanding your use-case here, and 
so maybe I've completely misunderstood something.


> This has been a problem in my code since in some instances, I tell a
> context manager not to do its `__exit__` function. (I do this by using
> `ExitStack.pop_all()`. However the `__exit__` is still called here.

Have you considered something like:

def f():
print('1')
try:
yield
finally:
if f.closing:
print('2')


You can then write a decorator to set f.closing to True or False as 
needed. But again, I don't understand why you would want this feature, 
or how you are using it, so I might have this completely wrong.



-- 
Steve
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-06 Thread Ram Rachum
On Sun, Nov 6, 2016 at 9:38 AM, Nick Coghlan  wrote:

> On 6 November 2016 at 17:18, Ram Rachum  wrote:
> > On Sun, Nov 6, 2016 at 8:53 AM, Nick Coghlan  wrote:
> >> There's still something seriously odd going in relation to your
> >> overall resource management architecture if "cleanup, maybe, unless I
> >> decide to tell you not to" is a behaviour you regularly need. Cleanup
> >> functions in a garbage collected environment should be idempotent, so
> >> it doesn't matter if you redundantly call them again later.
> >
> >
> > Well, you think it's weird that I want a `finally` clause to not be
> called
> > in some circumstances. Do you think it's equally weird to want an
> `__exit__`
> > method that is not called in some circumstances?
>
> Yes, as the whole point of __exit__ is that the interpreter goes to
> great lengths to make sure it always gets called, no matter what else
> happens with the currently executing frame (whether it finishes
> normally, returns early, breaks out of a loop, continues with the next
> iteration, raises an exception, or gets suspended without ever
> resuming normal execution).
>
> If you don't want that behaviour, then __exit__ likely isn't the right
> tool (although it may provide the technical basis for a selective
> cleanup framework).
>
> Cheers,
> Nick.
>
>
I understand your point of view. I see that Python does allow you to not
call `__exit__` if you don't want to, so I wish it'll have the same
approach to not calling `generator.close()` if you don't want to. (This is
what it's really about, not `finally`.)
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-06 Thread Nick Coghlan
On 6 November 2016 at 17:18, Ram Rachum  wrote:
> On Sun, Nov 6, 2016 at 8:53 AM, Nick Coghlan  wrote:
>> There's still something seriously odd going in relation to your
>> overall resource management architecture if "cleanup, maybe, unless I
>> decide to tell you not to" is a behaviour you regularly need. Cleanup
>> functions in a garbage collected environment should be idempotent, so
>> it doesn't matter if you redundantly call them again later.
>
>
> Well, you think it's weird that I want a `finally` clause to not be called
> in some circumstances. Do you think it's equally weird to want an `__exit__`
> method that is not called in some circumstances?

Yes, as the whole point of __exit__ is that the interpreter goes to
great lengths to make sure it always gets called, no matter what else
happens with the currently executing frame (whether it finishes
normally, returns early, breaks out of a loop, continues with the next
iteration, raises an exception, or gets suspended without ever
resuming normal execution).

If you don't want that behaviour, then __exit__ likely isn't the right
tool (although it may provide the technical basis for a selective
cleanup framework).

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-06 Thread Brendan Barnwell

On 2016-11-06 00:18, Ram Rachum wrote:

Well, you think it's weird that I want a `finally` clause to not be
called in some circumstances. Do you think it's equally weird to want an
`__exit__` method that is not called in some circumstances?


	It's weird to not want the __exit__ to be called if it's defined as a 
finally block, which is what you're doing with the way you're using 
contextlib.contextmanager.  The __exit__ block there is effectively 
whatever is after the yield in the generator function you write.  If 
there's code you don't want to always be run, don't put that code inside 
a finally where the yield is in the try.


--
Brendan Barnwell
"Do not follow where the path may lead.  Go, instead, where there is no 
path, and leave a trail."

   --author unknown
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-06 Thread Ram Rachum
On Sun, Nov 6, 2016 at 8:53 AM, Nick Coghlan  wrote:

> On 6 November 2016 at 16:07, Ram Rachum  wrote:
> > Heh, I just played with this, and found a workaround. If I do something
> like
> > this after creating the generator:
> >
> > sys.g = g
> >
> > Then it wouldn't get closed when Python finishes, and the cleanup won't
> > happen, which is what I want.
>
> The interpreter goes to significant lengths to make sure that finally
> clauses get executed prior to or during interpreter shutdown, and any
> means you find by which they don't get executed is considered a bug
> (not always a fixable bug, but a bug nonetheless). If you rely on
> those bugs and limitations to get your program to perform the way you
> want it to you're going to run into problems later when upgrading to
> new CPython versions, or trying out different interpreter
> implementations.
>

I understand, and I agree with the reasoning. Still, I think I'll take my
chances.

There's still something seriously odd going in relation to your
> overall resource management architecture if "cleanup, maybe, unless I
> decide to tell you not to" is a behaviour you regularly need. Cleanup
> functions in a garbage collected environment should be idempotent, so
> it doesn't matter if you redundantly call them again later.
>

Well, you think it's weird that I want a `finally` clause to not be called
in some circumstances. Do you think it's equally weird to want an
`__exit__` method that is not called in some circumstances?


>
> However, if you *do* need that pattern regularly, then the pattern
> itself can be encapsulated in a context manager:
>
> class callback_unless_exit:
> def __init__(self, callback):
> self.callback = callback
> def __enter__(self):
> return self
> def __exit__(self, exc_type, exc_value, exc_tb):
> if issubclass(exc_type, GeneratorExit):
> return
> self.callback()
>
> and then do:
>
> with callback_unless_exit(cleanup):
> yield
>
> in the context managers where you want that behaviour.
>
>
Thanks for the workaround but I feel it's even less elegant than my
original workaround.
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-06 Thread Nick Coghlan
On 6 November 2016 at 16:07, Ram Rachum  wrote:
> Heh, I just played with this, and found a workaround. If I do something like
> this after creating the generator:
>
> sys.g = g
>
> Then it wouldn't get closed when Python finishes, and the cleanup won't
> happen, which is what I want.

The interpreter goes to significant lengths to make sure that finally
clauses get executed prior to or during interpreter shutdown, and any
means you find by which they don't get executed is considered a bug
(not always a fixable bug, but a bug nonetheless). If you rely on
those bugs and limitations to get your program to perform the way you
want it to you're going to run into problems later when upgrading to
new CPython versions, or trying out different interpreter
implementations.

There's still something seriously odd going in relation to your
overall resource management architecture if "cleanup, maybe, unless I
decide to tell you not to" is a behaviour you regularly need. Cleanup
functions in a garbage collected environment should be idempotent, so
it doesn't matter if you redundantly call them again later.

However, if you *do* need that pattern regularly, then the pattern
itself can be encapsulated in a context manager:

class callback_unless_exit:
def __init__(self, callback):
self.callback = callback
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, exc_tb):
if issubclass(exc_type, GeneratorExit):
return
self.callback()

and then do:

with callback_unless_exit(cleanup):
yield

in the context managers where you want that behaviour.

Regards,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-06 Thread Ram Rachum
Heh, I just played with this, and found a workaround. If I do something
like this after creating the generator:

sys.g = g

Then it wouldn't get closed when Python finishes, and the cleanup won't
happen, which is what I want. This is still a bit ugly but now it's
something I can do automatically for every generator-based context manager
that I create, so it's not as bad as the previous workaround in my opinion.
(I wouldn't create a new attribute for each context manager, but just have
a list with a mangled name on `sys` that'll hold all of them.)

On Sun, Nov 6, 2016 at 8:02 AM, Ram Rachum  wrote:

> Sorry, I was wrong at quoting the workaround I do, it's actually this:
>
> try:
> yield
> except GeneratorExit:
> raise
> except:
> cleanup()
> raise
> else:
> cleanup()
>
> This works, but it's ugly! And I basically need to do this to every
> generator-based context manager that I want to be able to run just the
> `__enter__` of without the `__exit__`. I wish there was a better solution
> than including this in my code.
>
> On Sun, Nov 6, 2016 at 7:51 AM, Nick Coghlan  wrote:
>
>> On 6 November 2016 at 14:46, Ram Rachum  wrote:
>> > I worked around this problem by adding `except GeneratorExit: raise` in
>> my
>> > context manager, but that's an ugly solution.
>>
>> Adding `except GeneratorExit: raise` to a try statement with a finally
>> clause won't prevent the finally clause from executing.
>>
>> Selective non-idempotent cleanup behaviour really isn't a good idea,
>> so the language is fighting you for a reason here - the meaning of a
>> "finally" clause is that the code it contains *will* get executed, and
>> you have to try incredibly hard to keep that from happening since it's
>> an implicit part of cleaning up unfinished generators, and we don't
>> let you switch the nominal class of generator objects at runtime.
>> Indeed, yield inside try/finally was prohibited for years prior to PEP
>> 342, as the runtime previously couldn't guarantee that the finally
>> clause would actually execute.
>>
>> If you *don't* want the code to execute unconditionally, then you need
>> to be more explicit about when you *do* want it to execute with some
>> combination of "except" and "else" clauses. For example:
>>
>> >>> @contextmanager
>> ... def cm():
>> ... print("enter")
>> ... try:
>> ... yield
>> ... except Exception:
>> ... print("Normal exception, not GeneratorExit,
>> KeyboardInterrupt or SystemExit")
>> ... else:
>> ... print("No exception")
>> ...
>> >>> cm().__enter__()
>> enter
>>
>> Cheers,
>> Nick.
>>
>> --
>> Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
>>
>
>
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-06 Thread Ram Rachum
Sorry, I was wrong at quoting the workaround I do, it's actually this:

try:
yield
except GeneratorExit:
raise
except:
cleanup()
raise
else:
cleanup()

This works, but it's ugly! And I basically need to do this to every
generator-based context manager that I want to be able to run just the
`__enter__` of without the `__exit__`. I wish there was a better solution
than including this in my code.

On Sun, Nov 6, 2016 at 7:51 AM, Nick Coghlan  wrote:

> On 6 November 2016 at 14:46, Ram Rachum  wrote:
> > I worked around this problem by adding `except GeneratorExit: raise` in
> my
> > context manager, but that's an ugly solution.
>
> Adding `except GeneratorExit: raise` to a try statement with a finally
> clause won't prevent the finally clause from executing.
>
> Selective non-idempotent cleanup behaviour really isn't a good idea,
> so the language is fighting you for a reason here - the meaning of a
> "finally" clause is that the code it contains *will* get executed, and
> you have to try incredibly hard to keep that from happening since it's
> an implicit part of cleaning up unfinished generators, and we don't
> let you switch the nominal class of generator objects at runtime.
> Indeed, yield inside try/finally was prohibited for years prior to PEP
> 342, as the runtime previously couldn't guarantee that the finally
> clause would actually execute.
>
> If you *don't* want the code to execute unconditionally, then you need
> to be more explicit about when you *do* want it to execute with some
> combination of "except" and "else" clauses. For example:
>
> >>> @contextmanager
> ... def cm():
> ... print("enter")
> ... try:
> ... yield
> ... except Exception:
> ... print("Normal exception, not GeneratorExit,
> KeyboardInterrupt or SystemExit")
> ... else:
> ... print("No exception")
> ...
> >>> cm().__enter__()
> enter
>
> Cheers,
> Nick.
>
> --
> Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
>
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Re: [Python-ideas] Generator-based context managers can't skip __exit__

2016-11-05 Thread Nick Coghlan
On 6 November 2016 at 14:46, Ram Rachum  wrote:
> I worked around this problem by adding `except GeneratorExit: raise` in my
> context manager, but that's an ugly solution.

Adding `except GeneratorExit: raise` to a try statement with a finally
clause won't prevent the finally clause from executing.

Selective non-idempotent cleanup behaviour really isn't a good idea,
so the language is fighting you for a reason here - the meaning of a
"finally" clause is that the code it contains *will* get executed, and
you have to try incredibly hard to keep that from happening since it's
an implicit part of cleaning up unfinished generators, and we don't
let you switch the nominal class of generator objects at runtime.
Indeed, yield inside try/finally was prohibited for years prior to PEP
342, as the runtime previously couldn't guarantee that the finally
clause would actually execute.

If you *don't* want the code to execute unconditionally, then you need
to be more explicit about when you *do* want it to execute with some
combination of "except" and "else" clauses. For example:

>>> @contextmanager
... def cm():
... print("enter")
... try:
... yield
... except Exception:
... print("Normal exception, not GeneratorExit,
KeyboardInterrupt or SystemExit")
... else:
... print("No exception")
...
>>> cm().__enter__()
enter

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/