New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The new context
itertool is bad
#121
Comments
See #112 and #114 for the discussions about this. I initially thought one should just write the Anyway, I'm not really sure about from more_itertools import consume, context
def iterable():
yield 1
raise RuntimeError('uh oh')
yield 2
class Manager(object):
def __enter__(self):
print('Enter')
return self
def __exit__(self, *args, **kwargs):
print('Exit')
manager = Manager()
it = iterable()
try:
consume(print(x) for m in context(manager) for x in it)
except RuntimeError:
print('Encountered exception!') The output is in fact:
|
Also CC @erikrose for comment. Should we remove this, or warn about gotchas in the docstring? |
This is a quirk of CPython, but is not promised by the Python language:
This code will not work in PyPy. If PyPy catches up to CPython down the line, all of the code that is written like this will break. (It was never supposed to work.) Another problem is that the file handle may not be closed until the program exits, so a long-running program can easily run out of file handles or some other limited resource that was supposed to be relinquished at the end of the code block. I read through the threads, but unfortunately, trying to roll context managers into iterators is problematic. You need a |
I don't have a computer handy to confirm this, but I believe contextlib.contextmanager has the same problem, putting us within the already tolerated bounds of the stdlib. Sorry for brevity. |
Now I have a computer. >>> from contextlib import contextmanager
>>> @contextmanager
... def foo():
... print "enter"
... yield
... print "exit"
...
>>> with foo():
... raise NotImplementedError
...
enter
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
NotImplementedError There's no "exit", so I think we're okay. Maybe it's a design flaw in contextlib as well, but at least we're not doing something unprecedented. I could still hear it argued that exceptions are exceptional and that we promote skipping exit even if nothing goes wrong, but it's not immediately clear. |
If you want to use
|
Most of the examples in the docs disagree? @contextmanager
def tag(name):
print("<%s>" % name)
yield
print("</%s>" % name) You are correct that PyPy does not print the |
For further context, the docs say:
The last sentence seems applicable to Erik's example with |
Your quotation from the documentation says it all. If you're writing something that should happen even when there's an exception, then you have to wrap it as I did. If it's something unimportant like printing tags, it's probably ok to leave that off since the user probably has a stack dump to worry about. Anyway, the reason I really don't like the There was a proposal actually to unify iteration and context managers. The motivation is very similar as that of this itertool, but their solution was to propose a langauge change. There's a lot interesting reading on this topic in that PEP and the discussion surrounding that PEP. |
To highlight why I think that quotation supports not keeping the
If someone does this in their context manager and then you use it with the |
I hate to zap a thing after just releasing it, but after playing around with PyPy I'm convinced that Neil is right and that there's not a good way to warn about this in the documentation. I'm inclined to remove |
I'll let Erik comment before I pull the trigger on this, but here's my plan:
All that is in this branch. |
Sounds sane to me.
|
All fixed. Thanks Neil for the explanation. |
The new
context
itertool tries to expose a context manager as an iterable. This breaks the context manager manager guarantee that__exit__
will be called. It's not enough to tell the caller that he has to iterate over the whole iterable. Even if there are nobreak
orreturn
statements in the loop, there is always the possibility of exceptions. The whole point of context managers is to guarantee that the__exit__
is always called when a block terminates. This is why context managers and iterables are orthogonal concepts; in general, one cannot be made to look like the other.Please remove
context
because it encourages people to write bad code.There is no benefit to
context
in any case. Even the motivating example in the documentation is just:which can be written just as succinctly
The text was updated successfully, but these errors were encountered: