Enhanced context managers with ContextManagerExit and None
I just added a patch to the tracker: http://bugs.python.org/issue18677 Here is its' text: A proposed patch adds two features to context managers: 1)It has always irked me that it was impossible to assemble nested context managers in the python language. See issue #5251<http://bugs.python.org/issue5251>. The main problem, that exceptions in __enter__ cannot be properly handled, is fixed by introducing a new core exception, ContextManagerExit. When raised by __enter__(), the body that the context manager protects is skipped. This exception is in the spirit of other semi-internal exceptions such as GeneratorExit and StopIteration. Using this exception, contextlib.nested can properly handle the case where the body isn't run because of an internal __enter__ exception which is handled by an outer __exit__. 2) The mechanism used in implementing ContextManagerExit above is easily extended to allowing a special context manager: None. This is useful for having _optional_ context managers. E.g. code like this: with performance_timer(): do_work() def performance_timer(): if profiling: return accumulator return None None becomes the trivial context manager and its __enter__ and __exit__ calls are skipped, along with their overhead. This patch implements both features. In addition, it: 1) reintroduces contextlib.nested, which is based on nested_delayed 2) introduces contextlib.nested_delayed, which solves the other problem with previous versions of nested, that an inner context manager expression shouldn't be evaluated early. contextlib.nested evaluates callables returning context managers, rather than managers directly. 3) Allows contextlib.contextmanager decorated functions to not yield, which amounts to skipping the protected body (implicitly raising ContextManagerExit) 4) unittests for the whole thing. Cheers, Kristján
On 7 Aug, 2013, at 16:23, Kristján Valur Jónsson <kristjan@ccpgames.com> wrote:
I just added a patch to the tracker: http://bugs.python.org/issue18677
Here is its' text:
A proposed patch adds two features to context managers:
1)It has always irked me that it was impossible to assemble nested context managers in the python language. See issue #5251 . The main problem, that exceptions in __enter__ cannot be properly handled, is fixed by introducing a new core exception, ContextManagerExit. When raised by __enter__(), the body that the context manager protects is skipped. This exception is in the spirit of other semi-internal exceptions such as GeneratorExit and StopIteration. Using this exception, contextlib.nested can properly handle the case where the body isn't run because of an internal __enter__ exception which is handled by an outer __exit__.
This appears to be simular to the mechanism in PEP 377 <http://www.python.org/dev/peps/pep-0377/> which was rejected.
2) The mechanism used in implementing ContextManagerExit above is easily extended to allowing a special context manager: None. This is useful for having _optional_ context managers. E.g. code like this: with performance_timer(): do_work()
def performance_timer(): if profiling: return accumulator return None
None becomes the trivial context manager and its __enter__ and __exit__ calls are skipped, along with their overhead.
How bad is the overhead of a trivial contextmanager (that is, one with empty bodies for both __enter__ and __exit__)? Ronald
________________________________________ Frá: Ronald Oussoren [ronaldoussoren@mac.com]
This appears to be simular to the mechanism in PEP 377 http://www.python.org/dev/peps/pep-0377/ which was rejected. Indeed, it is similar, but simpler. (I was unaware of Nick's PEP). The PEP contains the fallacy that in case of __enter__ raising the SkipStatement (or in my case, ContextManagerExit) exception, that something needs to be assigned to the "as" body. The proposal I'm making is to make it possible to do in a single context manager, what it is possible to do with two context managers currently: @contextmanager errordude: 1 // 0 yield @contextmanager handler: try: yield except ZeroDivisionError: pass with handler as a, errordude as b: do_stuff(a, b)
do_stuff will be silently skipped, and b won't be assigned to. I'm proposing a mechanism where the same could be done with a single context manager: with combined as a, b: do_stuff(a, b) Currently, you have programmatic capabilities (silent skipping of the statement) with two context managers that you don't have with a single one. That's just plain odd. I don't think the original objectsions to PEP 377 are valid. My approach introduces very little added complexity. The use case is "correctly being able to combine context managers". It is a "completeness" argument too, that you can do with a single context manager what you can do with two nested ones.
2) The mechanism used in implementing ContextManagerExit above is easily extended to allowing a special context manager: None. This is useful for having _optional_ context managers. E.g. code like this: with performance_timer(): do_work()
def performance_timer(): if profiling: return accumulator return None
None becomes the trivial context manager and its __enter__ and __exit__ calls are skipped, along with their overhead.
How bad is the overhead of a trivial contextmanager (that is, one with empty bodies for both __enter__ and __exit__)? Valid question. Let's try:
#testnone.py import timeit class trivial: def __enter__(self): pass def __exit__(self, a, b, c): pass trivial = trivial() print("trivial") print(timeit.timeit("with trivial: pass", setup = "from __main__ import trivial")) print("none") print(timeit.timeit("with None: pass", setup = "from __main__ import trivial")) print("trivial + sum") print(timeit.timeit("with trivial: 1+1", setup = "from __main__ import trivial")) print("none + sum") print(timeit.timeit("with None: 1+1", setup = "from __main__ import trivial")) yields: trivial 0.786079668918435 none 0.07240212102423305 trivial + sum 0.9069962424632848 none + sum 0.11295328499933865 As you can see, the function call overhead is tremendously high. Two complete function calls with bells and whistles are omitted by using the None context manager. Ronald
On 8 Aug, 2013, at 14:14, Kristján Valur Jónsson <kristjan@ccpgames.com> wrote:
This appears to be simular to the mechanism in PEP 377 http://www.python.org/dev/peps/pep-0377/ which was rejected. Indeed, it is similar, but simpler. (I was unaware of Nick's PEP). The PEP contains the fallacy that in case of __enter__ raising the SkipStatement (or in my case, ContextManagerExit) exception, that something needs to be assigned to the "as" body. The proposal I'm making is to make it possible to do in a single context manager, what it is possible to do with two context managers currently: @contextmanager errordude: 1 // 0 yield @contextmanager handler:
________________________________________ Frá: Ronald Oussoren [ronaldoussoren@mac.com] try: yield except ZeroDivisionError: pass with handler as a, errordude as b: do_stuff(a, b)
do_stuff will be silently skipped, and b won't be assigned to. I'm proposing a mechanism where the same could be done with a single context manager: with combined as a, b: do_stuff(a, b)
Currently, you have programmatic capabilities (silent skipping of the statement) with two context managers that you don't have with a single one. That's just plain odd.
I don't think the original objectsions to PEP 377 are valid. My approach introduces very little added complexity. The use case is "correctly being able to combine context managers". It is a "completeness" argument too, that you can do with a single context manager what you can do with two nested ones.
Skipping the body makes it possible to introduce flow control in a context manager, which could make it harder to read code. That is, currently the body is either executed unless an exception is raised that raises an exception, while with your proposal the body might be skipped entirely. I haven't formed an opinion yet on whether or not that is a problem, but PEP 343 (introduction of the with statement) references <http://blogs.msdn.com/b/oldnewthing/archive/2005/01/06/347666.aspx> which claims that hiding flow control in macros can be bad. Ronald
-----Original Message-----
From: Ronald Oussoren [mailto:ronaldoussoren@mac.com]
Skipping the body makes it possible to introduce flow control in a context
manager, which could make it harder to read code. That is, currently the
body is either executed unless an exception is raised that raises an
exception, while with your proposal the body might be skipped entirely. I
haven't formed an opinion yet on whether or not that is a problem, but PEP
343 (introduction of the with statement) references
<http://blogs.msdn.com/b/oldnewthing/archive/2005/01/06/347666.aspx>
which claims that hiding flow control in macros can be bad.
Perhaps my example was not clear enough. The body will be skipped entirely if the __enter__() method raises an exception. An outer context manager can then suppress this exception. However, you cannot create a single context manager that does both. This, to me, was the most serious problem with the old nested() context manager: Nesting of two context managers could not be correctly done for arbitrary context managers I am not advocating that people do create such context manager, but pointing out that this omission means that you cannot always combine two context managers into one and preserve the semantics because it is possible to do something with two nested context managers that you cannot achieve with a single one. And this is my completeness argument. Imagine if there were code that could only be written using two nested functions, and that the nested function could not be folded into the outer one without sacrificing semantic correctness? I.e., you could do: foo = bar(spam(value)) but this: def ham(value): return bar(spam(value)) foo = ham(value) would not be correct for all possible "bar" and "spam"? As an example, here is what you can currently do in python (again, not recommending it but providing as an example) class ContextManagerExit(): pass @contextmanager def if_a(): try: yield except ContextManagerExit: pass @contextmanager def if_b(condition): if not condition: raise ContextManagerExit yield with if_a(), if_b(condition): execute_code() #this line is executed only if "condition" is True In current python, it is impossible to create a combined context manager that does this: if_c = nested(if_a(), if_b(condition)) with if_c: execute_code() #A single context manager cannot both raise an exception from __enter__() _and_ have it supressed. With my patch, "if_b()" is all that is needed, because ContextManagerExit is a special exception that is caught and suppressed by the interpreter. And so, it becomes possible to nest arbitrary context managers without sacrificing semantic correctness. Ø The overhead is fairly high, although not enough that I'd worry a lot in my code. But then again, I don't build gaming backends or cloud-scale software :-) Ø That said, using None as the "do nothing" context manager is problematic as this could hide problems in users code. I'm sure that I'm not the only person that sometimes forgets to actually return a value from a function. Using that (missing, and hence implicitly None) return value as a context manager currently causes an exception that clearly points out a bug in my code and could silently do the wrong thing with this proposal, e.g: Ø def get_lock(obj): Ø lck = LockManager.get_lock_for_object(obj) Ø # oops forgot to return lck Ø with get_lock(42): Ø ... Ø Using a different singleton instead of None would avoid this drawback. I think something like this is important for context managers that are used as "diagnostic" tools, e.g. timing related context managers that may be disabled in production without changing code. But I agree that None is problematic for the reason you demonstrate, and did consider that. I'm suggesting it here as a demonstration of the concept, and also to reduce the need for yet another built-in. Perhaps the ellipsis could be used, that's everyone's favourite singleton :) K
On 08/13/2013 04:55 AM, Kristján Valur Jónsson wrote:
But I agree that None is problematic for the reason you demonstrate, and did consider that. I’m suggesting it here as a demonstration of the concept, and also to reduce the need for yet another built-in. Perhaps the ellipsis could be used, that’s everyone’s favourite singleton :)
Heh. How about NotImplemented? -- ~Ethan~
On 13 Aug 2013 08:27, "Kristján Valur Jónsson" <kristjan@ccpgames.com> wrote:
Perhaps my example was not clear enough. The body will be skipped entirely if the __enter__() method raises an exception. An outer context manager can then suppress this exception.
However, you cannot create a single context manager that does both. This, to me, was the most serious problem with the old nested() context manager: Nesting of two context managers could not be correctly done for arbitrary context managers
nested() was deprecated and removed because it didn't handle files (or any other CM that does resource acquisition in __init__) correctly. The fact you can't factor out arbitrary context managers had nothing to do with it.
I am not advocating that people do create such context manager, but pointing out that this omission means that you cannot always combine two context managers into one and preserve the semantics because it is possible to do something with two nested context managers that you cannot achieve with a single one. And this is my completeness argument.
At the moment, a CM cannot prevent execution of the body - it must be paired with an if statement or an inner call that may raise an exception, keeping the flow control at least somewhat visible at the point of execution. The following is also an illegal context manager: @contextmanager def bad_cm(broken=False): if not broken: yield It's illegal for exactly the same reason this is illegal (this is the expanded form of your nested CM example): @contextmanager def bad_cm2(broken=False): class Skip(Exception): pass try: if broken: raise Skip yield except Skip: pass
Imagine if there were code that could only be written using two nested functions, and that the nested function could not be folded into the outer one without sacrificing semantic correctness? I.e., you could do:
foo = bar(spam(value))
but this:
def ham(value): return bar(spam(value))
foo = ham(value)
would not be correct for all possible “bar” and “spam”?
But that's not what you're asking about. You're asking for the ability to collapse two independent try statements into one. There are already things you can't factor out as functions - that's why we have generators and context managers. It's also a fact that there are things you can't factor out as single context managers. This is why we have nested context managers and also still have explicit try/except/else/finally statements.
As an example, here is what you can currently do in python (again, not recommending it but providing as an example)
class ContextManagerExit(): pass
@contextmanager def if_a(): try: yield except ContextManagerExit: pass
@contextmanager def if_b(condition): if not condition: raise ContextManagerExit yield
with if_a(), if_b(condition): execute_code() #this line is executed only if “condition” is True
Expand it out to the underlying constructs and you will see this code is outright buggy, because the exception handler is too broad: try: if not condition: raise ContextManagerExit execute_code() # ContextManagerExit will be eaten here except ContextManagerExit: pass
In current python, it is impossible to create a combined context manager that does this:
if_c = nested(if_a(), if_b(condition))
with if_c: execute_code() #A single context manager cannot both raise an exception from __enter__() _and_ have it supressed.
This is a feature, not a bug: the with statement body will *always* execute, unless __enter__ raises an exception. Don't be misled by the ability to avoid repeating the with keyword when specifying multiple context managers in the same statement: semantically, that's equivalent to multiple nested with statements, so the outer one always executes, and the inner ones can only skip the body by raising an exception from __enter__.
With my patch, “if_b()” is all that is needed, because ContextManagerExit is a special exception that is caught and suppressed by the interpreter. And so, it becomes possible to nest arbitrary context managers without sacrificing semantic correctness.
I'd be open to adding the following context manager to contextlib: @contextmanager def skip(keep=False): class Skip(Exception): pass Skip.caught = None try: yield Skip except Skip as exc: if keep: Skip.caught = exc This would allow certain currently awkward constructs to be expressed more easily without needing to drop back to a try/except block (note that 3.4 already adds contextlib.ignored to easily suppress selected exceptions in a block of code). Creating a custom exception type each time helps avoid various problems with overly broad exception handlers.
But I agree that None is problematic for the reason you demonstrate, and did consider that. I’m suggesting it here as a demonstration of the concept, and also to reduce the need for yet another built-in. Perhaps the ellipsis could be used, that’s everyone’s favourite singleton :)
An empty contextlib.ExitStack() instance is already a perfectly serviceable "do nothing" context manager, we don't need (and won't get) another one. Cheers, Nick.
Phew, it is a bit awkward to discuss this in two separate places. Those interested are invited to take a peek at the issue as well: http://bugs.python.org/issue18677 ________________________________________ Frá: Nick Coghlan [ncoghlan@gmail.com] Sent: 13. ágúst 2013 15:52 To: Kristján Valur Jónsson Cc: python-ideas@python.org; Ronald Oussoren Efni: Re: [Python-ideas] Enhanced context managers with ContextManagerExit and None
nested() was deprecated and removed because it didn't handle files (or any other CM that does resource acquisition in __init__) correctly. The fact you can't factor out arbitrary context managers had nothing to do with it. Ok, I appreciate that, although I assumed otherwise. Imho, it is not nested that is broken but "CM that do resource aquisition in __init__()". I like to think of them as "hybrid". __enter__ is for resource aquisition. The only reason to do it in __init__ is if the object is its own context manager. But that is not a very good pattern. By deprecating and removing nested(), effectively we are giving up and saying: context managers should only be instantiated with a "with" statement, inline, and should only be used with "with" statements. There is nothing magic about "nested()" causing it to be a "bug magnet". The bug magnet is that some context managers don't take well to being instantiated early. We should have fixed that, rather than effectively prohibiting any programming involving context managers.
At the moment, a CM cannot prevent execution of the body - it must be paired with an if statement or an inner call that may raise an exception, keeping the flow control at least somewhat visible at the point of execution.
so: with deal_with_error, acquire_resource as r: foo(r) has more visible flow control than: with acquire_resource_or_deal_with_error as r: foo(r) With the combined with statement, you visually have a single condition manager, if not actually.
The following is also an illegal context manager:
@contextmanager def bad_cm(broken=False): if not broken: yield Not with my patch :). In fact this now works for all cm_a and cm_b: @contextmanager def pair(cm_a, cm_b): with cm_a as a, cm_b as b: yield a, b
contextlib._GeneratorContextManager takes care of raising ContextManagerExit if nothing is yielded.
But that's not what you're asking about. You're asking for the ability to collapse two independent try statements into one. No, not any more than I'm asking to collapse two "if" statements into one. However, a CM is not a try statement. It is a first class object, just like a function is. If we can do abstract programming with functions, pass callables around, lambdas, do currying, and so on and so forth, why should we not be able to do so with context managers?
There are already things you can't factor out as functions - that's why we have generators and context managers. It's also a fact that there are things you can't factor out as single context managers. This is why we have nested context managers and also still have explicit try/except/else/finally statements.
Expand it out to the underlying constructs and you will see this code is outright buggy, because the exception handler is too broad:
try: if not condition: raise ContextManagerExit execute_code() # ContextManagerExit will be eaten here except ContextManagerExit: pass Now you are just being pedantic :) ContextManagerExit is a private exception that can only be raised by if_a, so there are no stray exceptions. Anyway, this was just to demonstrate how flow control _can_ be done with condition managers if one wanted to do so, intentionally.
In current python, it is impossible to create a combined context manager that does this:
if_c = nested(if_a(), if_b(condition))
with if_c: execute_code() #A single context manager cannot both raise an exception from __enter__() _and_ have it supressed.
This is a feature, not a bug: the with statement body will *always* execute, unless __enter__ raises an exception. Don't be misled by the ability to avoid repeating the with keyword when specifying multiple context managers in the same statement: semantically, that's equivalent to multiple nested with statements, so the outer one always executes, and the inner ones can only skip the body by raising an exception from __enter__.
I realize this, and this is the whole point of my my proposal. That __enter__ can raise an exception and have that exception silenced by the context manager machinery, not having to build that silencing around the machinery yourself. The point is: With two arbitrary context managers, cm_a and cm_b, cm_a _can_ silence the exception that was raised by cm_b's __enter__() method. This may be by design, or by accident, but it is possible. And this means that an equivalent cm_c = nested(cm_a, cm_b) is not possible. I want to be able to deal with this edge case so that can programmatically, in addition to syntactically, nest two context managers.
I'd be open to adding the following context manager to contextlib:
@contextmanager def skip(keep=False): Great, but that's not what my proposal is about. It's not about flow control, but composability.
An empty contextlib.ExitStack() instance is already a perfectly serviceable "do nothing" context manager, we don't need (and won't get) another one. Did you miss the performance argument? Of course I can write a do-nothing context manager. I'm not suggesting "None" because I'm too lasy to write my own. I'm suggesting it because context managers are very useful for other things than manageing resources, namely optional monitoring of he program:
do myfunc(): ... with app_timer: stuff() if app_is_being_monitored: app_timer = real_app_timing_contextmanager else: app_timer = None. A context manager, even a do-nothing one, is currently expensive, consisting of two dynamic function calls. Having a "special" do-nothing context manager singleton would be beneficial in such cases when performance is important. Cheers, Kristján Cheers, Nick.
On 14 August 2013 17:05, Kristján Valur Jónsson <kristjan@ccpgames.com> wrote:
Phew, it is a bit awkward to discuss this in two separate places. Those interested are invited to take a peek at the issue as well: http://bugs.python.org/issue18677
I put my real reply on the tracker issue (since that's a better historical record), but the short version is that you've persuaded *me* to go back to wanting to fix this (since your approach is significantly less horrible than what I came up with back in 2009 for PEP 377), which means Guido is the one you need to convince to let you reopen the PEP (I added him to the nosy list on the issue). If Guido still doesn't like it, then it will stay broken :( Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
07.08.13 17:23, Kristján Valur Jónsson написав(ла):
2) The mechanism used in implementing ContextManagerExit above is easily extended to allowing a special context manager: None. This is useful for having _optional_ context managers. E.g. code like this: with performance_timer(): do_work()
def performance_timer(): if profiling: return accumulator return None
None becomes the trivial context manager and its __enter__ and __exit__ calls are skipped, along with their overhead.
+1 to this idea. Of course ExitStack is powerful tool but it is too verbose for many simple cases. Consider simple example: file = open(...) if ... else None file2 = open(...) if ... else None process(file, file2, ...) if file is not None: file.close() if file2 is not None: file2.close() This code is not exception-safe. There are some ways to write it right. 1. With try/finally and a check in finally block: file = open(...) if ... else None try: file2 = open(...) if ... else None try: process(file, file2, ...) finally: if file2 is not None: file2.close() finally: if file is not None: file.close() 2. With a code duplication (note that process can be not a one-line call of a function): if ...: with open(...) as file: if ...: with open(...) as file2: process(file, file2, ...) else: process(file, None, ...) else: if ...: with open(...) as file2: process(None, file2, ...) else: process(None, None, ...) 3. With ExitStack: import contextlib with contextlib.ExitStack() as cm: if ...: file = open(...) cm.enter_context(file) else: file = None if ...: file2 = open(...) cm.enter_context(file2) else: file2 = None process(file, file2, ...) And when the with statement will support None as a "null-manager": file = open(...) if ... else None with file: file2 = open(...) if ... else None with file2: process(file, file2, ...)
On 2013-08-13, at 16:48 , Serhiy Storchaka wrote:
3. With ExitStack:
import contextlib with contextlib.ExitStack() as cm: if ...: file = open(...) cm.enter_context(file) else: file = None if ...: file2 = open(...) cm.enter_context(file2) else: file2 = None process(file, file2, …)
Please note that enter_context will return the result of object.__enter__, so: with contextlib.ExitStack() as cm: file = cm.enter_context(open(path1)) if condition1 else None file2 = cm.enter_context(open(path2)) if condition2 else None process(file, file2, ...) or if you really like conditional statements: with contextlib.ExitStack() as cm file = None file2 = None if condition1: file = cm.enter_context(open(path1)) if condition2: file2 = cm.enter_context(open(path2)) process(file, file2, ...)
13.08.13 18:04, Masklinn написав(ла):
Please note that enter_context will return the result of object.__enter__, so:
with contextlib.ExitStack() as cm: file = cm.enter_context(open(path1)) if condition1 else None file2 = cm.enter_context(open(path2)) if condition2 else None process(file, file2, ...)
Well, it works. However there are two disadvantages: 1. You need import contextlib. 2. cm.enter_context() increases a length of already long lines so you need split lines. N.B. Currently ExitStack used only in two places in stdlib (and I am the originator of both changes). In most cases it is simpler to do without ExitStack.
Furthermore: On 2013-08-13, at 16:48 , Serhiy Storchaka wrote:
And when the with statement will support None as a "null-manager":
I think that is a terrible idea, and will be a source of woe and bugs more than a solution to anything. If you want an optional context manager, why not create an OptionalContextManager(object | None) which delegates to the underlying non-None CM? That's simple, that's clear, that's explicit, and that's not a hack.
Le Tue, 13 Aug 2013 17:29:38 +0200, Masklinn <masklinn@masklinn.net> a écrit :
Furthermore:
On 2013-08-13, at 16:48 , Serhiy Storchaka wrote:
And when the with statement will support None as a "null-manager":
I think that is a terrible idea, and will be a source of woe and bugs more than a solution to anything.
I agree. I'd rather have a null_manager() in contextlib, designed expressly for this purpose. Regards Antoine.
13.08.13 18:29, Masklinn написав(ла):
If you want an optional context manager, why not create an OptionalContextManager(object | None) which delegates to the underlying non-None CM? That's simple, that's clear, that's explicit, and that's not a hack.
Because OptionalContextManager doesn't support an interface of the underlying object (e.g. doesn't have the read() method). And OptionalContextManager(None) is not None.
On 2013-08-13, at 21:19 , Serhiy Storchaka wrote:
13.08.13 18:29, Masklinn написав(ла):
If you want an optional context manager, why not create an OptionalContextManager(object | None) which delegates to the underlying non-None CM? That's simple, that's clear, that's explicit, and that's not a hack.
Because OptionalContextManager doesn't support an interface of the underlying object (e.g. doesn't have the read() method). And OptionalContextManager(None) is not None.
Irrelevant, OptionalContextManager's __enter__ returns either None or whatever the underlying contextmanager's __enter__ yields. So the true underlying object (or None) is available in the `with` body. OCM is solely there to provide a NOOP context manager for a None input.
On 8/13/2013 10:48 AM, Serhiy Storchaka wrote:
None becomes the trivial context manager and its __enter__ and __exit__ calls are skipped, along with their overhead.
And when the with statement will support None as a "null-manager":
As I understand the proposal, it is not to actually make None a context manager with __enter__ and __exit__ methods (bad), but to make it a signal to not do the normal calls. Such a use of None as a 'skip' signal is similar to its use as function argument. While such use is fine when None is a default argument (the normal case), its use in filter for a required argument is problematical (see recent thread here). I think using None as a signal object here would engender the same confusion here. "How can None be a context manager if it does not have the ... methods?" Given the alternative of an empty stack and a possible addition to contextlib, I do not think we should go for this generalization of None usage. -- Terry Jan Reedy
13.08.13 20:45, Terry Reedy написав(ла):
As I understand the proposal, it is not to actually make None a context manager with __enter__ and __exit__ methods (bad),
Why?
but to make it a signal to not do the normal calls.
I agree this is a bad idea.
13.08.13 22:23, Serhiy Storchaka написав(ла):
13.08.13 20:45, Terry Reedy написав(ла):
As I understand the proposal, it is not to actually make None a context manager with __enter__ and __exit__ methods (bad),
Why?
but to make it a signal to not do the normal calls.
I agree this is a bad idea.
Sorry, I misunderstood you. Skipping the body of the with statement is a bad idea (because it converts with into other control construction). But skipping calls of __enter__() and __exit__() methods looks just as an optimization an implementation detail (at least for None).
participants (8)
-
Antoine Pitrou -
Ethan Furman -
Kristján Valur Jónsson -
Masklinn -
Nick Coghlan -
Ronald Oussoren -
Serhiy Storchaka -
Terry Reedy