-
-
Notifications
You must be signed in to change notification settings - Fork 30.9k
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
Possible resource leak in glob in non-refcount implementations #88648
Comments
There is possible resource leak in glob on alternate Python implementation witch do not use reference counting to immediate releasing resources. It is in the code names = list(_iterdir(dirname, dir_fd, dironly)) where _iterdir() is a generator function which calls os.scandir() and yields entry names after some filtering. If an exception happens inside _iterdir(), the scandir iterator will be immediately closed, because of using the with statement. But an exception can happens outside of _iterdir(), in the list constructor (MemoryError). In this case the generator will be closed immediately in CPython because of reference counting (newly created generator has only one reference), but on other implementation it can be deferred on arbitrary time. And even in CPython we rely on garbage collector if there reference loops in the generator frame. This issue has very small chance to occur but still. The right way is to close the generator explicitly: it = _iterdir(dirname, dir_fd, dironly)
try:
names = list(it)
finally:
it.close() or with contextlib.closing(_iterdir(dirname, dir_fd, dironly)) as it:
names = list(it) We should analyze all other generator functions which acquire some resources and ensure that they are always properly closed. |
So this is a problem because the generator owns a resource that it will only release once its .close() method is called, right? And we have no control over when that happens -- we can't make it the responsibility of list() to close the iterator passed into it. It is indeed a painful thing. I wonder if we should put a warning somewhere in the docs and tutorials for generators? The PR looks good. |
Yes. I added you Guido to the nosy list to attract attention to a general issue. If the generator owns resources, it should be guaranteed closed. Since generator do not support the context manager protocol, we need to use closing(). Using generators is common in the code on my work, so I am going to revise all uses of resource-owning generators in my work code, in the stdlib and in common libraries like aiohttp. May be we will add a decorator for generator functions which would add support of the context manager protocol. Or make generator objects supporting the context manager protocol by default. Or make the with statement fall back to the close() method by default. Or even add special syntax shortcut for combining "with" and "for" if this idiom will be common enough. |
I think this may be worth bringing up on python-dev. The solution currently is rather verbose. I like adding the with-protocol to generators. |
Nick Coughlin explained on bpo-13814 msg151763 why he thought that making generators be context managers could/should not be done. "Generators deliberately don't support the context management protocol. This is so that they raise an explicit TypeError or AttributeError (pointing out that __exit__ is missing) if you leave out the @contextmanager decorator when you're using a generator to write an actual context manager. Generators supporting the context management protocol natively would turn that into a far more subtle (and confusing) error: your code would silently fail to invoke the generator body. Ensuring this common error remains easy to detect is far more important than making it easier to invoke close() on a generator object (particularly when contextlib.closing() already makes that very easy)." The new entry in the Design FAQ is this: "Why don’t generators support the with statement? For technical reasons, a generator used directly as a context manager would not work correctly. When, as is most common, a generator is used as an iterator run to completion, no closing is needed. When it is, wrap it as “contextlib.closing(generator)” in the ‘with’ statment." If Nick's explanation is not currently correct, the above should be removed. If it is, perhaps further doc is needed. |
That explanation is still valid, I just hadn’t thought of it that way. |
Closing as the PR was merged. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: