Re: [Python-ideas] Generator-based context managers can't skip __exit__
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__
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__
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__
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__
On 11/06/2016 08:11 PM, Nick Coghlan wrote: On 7 November 2016 at 12:25, Ethan Furmanwrote: 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__
On 7 November 2016 at 12:25, Ethan Furmanwrote: > 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__
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__
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__
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__
On 6 November 2016 at 17:44, Ram Rachumwrote: > 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__
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__
On Sun, Nov 6, 2016 at 9:38 AM, Nick Coghlanwrote: > 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__
On 6 November 2016 at 17:18, Ram Rachumwrote: > 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__
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__
On Sun, Nov 6, 2016 at 8:53 AM, Nick Coghlanwrote: > 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__
On 6 November 2016 at 16:07, Ram Rachumwrote: > 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__
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 Rachumwrote: > 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__
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 Coghlanwrote: > 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__
On 6 November 2016 at 14:46, Ram Rachumwrote: > 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/