You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add the close method for ElementTree.iterparse() object #69893
bpo-25638: Verify the etree_parse and etree_iterparse benchmarks are working appropriately
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:
assignee='https://github.com/serhiy-storchaka'closed_at=Nonecreated_at=<Date2015-11-23.13:57:25.201>labels= ['3.8', 'library', 'performance']
title='Add the close method for ElementTree.iterparse() object'updated_at=<Date2022-03-05.15:26:50.497>user='https://github.com/serhiy-storchaka'
I don't think there is a need for a close() method. Instead, the iterator should close the file first thing when it's done with it, but only if it owns it. Therefore, the fix in bpo-25688 seems correct.
Closing can also be done explicitly in a finaliser of the iterator, if implicit closing via decref is too lax.
Implicit closing an exhausted iterator helps only the iterator is iterated to the end. If the iteration has been stopped before the end, we get a leak of the file descriptor. Closing the file descriptor in the finalizer can be deferred to undefined term, especially in implementations without reference counting. Since file descriptors are limited resource, this can cause troubles in real programs.
Reasons for close() in iterparse objects are the same as for close in files and generators.
Maybe we will need to implement the full generator protocol (send() and throw()) in the iterparse objects, but currently I do not know use cases for this.
Ok, I think it's reasonable to make the resource management explicit for the specific case of letting iterparse() open the file. That suggests that there should also be context manager support, given that safe usages would often involve a try-finally.
Since it might not always be obvious for users when they need to close the iterator or not, I would also suggest to not let it raise an error on a double-close, i.e. if .close() was already called or the iterator was already exhausted (and the file closed automatically), calling .close() should just do nothing.
furkanonder commented on May 6, 2020
furkanondermannequin
Mannequin
More actions
Python 3.8.2 (default, Apr 8 2020, 14:31:25)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import xml.etree.ElementTree asET
>>> import gc
>>> ET.iterparse('/dev/null')
<xml.etree.ElementTree.iterparse.<locals>.IterParseIterator object at 0x7fb96f679d00>
>>> gc.collect()
34
The warning(main:1: ResourceWarning: unclosed file <_io.BufferedReader name='/dev/null'>) is no longer available in python3.8.2
I recently did a fully compatible implementation that solves an issue discussed here:
classiterparse:
__slots__='_source', '_opened', 'root', '_next'def__init__(self, /, source, events=None, parser=None):
# If source cannot be opened,# an error is emitted and object is moved to gc,# gc calls __del__ which calls close().# opened flag must be set to false before opening,# so close() won't emit AttributeError.self._opened=Falseifhasattr(source, 'read'):
self._source=sourceelse:
self._source=open(source, 'rb')
self._opened=Trueself.root=Noneself._next=self._iterator(XMLPullParser(events=events, _parser=parser)).__next__def__iter__(self, /):
returnselfdef_iterator(self, parser: XMLPullParser, /):
source=self._sourcetry:
data=source.read(16*1024)
whiledata:
parser.feed(data)
yieldfromparser.read_events()
data=source.read(16*1024)
root=parser._close_and_return_root()
yieldfromparser.read_events() # is it necessary?self.root=rootfinally:
self.close()
def__next__(self, /):
returnself._next()
defclose(self, /):
ifself._opened:
self._source.close()
self._opened=Falsedef__del__(self, /):
self.close()
Features:
Fully compatible with current implementation.
According to my tests, implementation above has faster creation time (no new function and class each time), but a bit slower traverse time.
Has close method that does not raise an exception on double-close as @scoder asked (file descriptors also do not raise an error in such cases).
If there is no more references to iterparse object, it is destroyed closing opened fd.
Creation time of iterparse_new: 0.08623739999984537
Creation time of iterparse: 0.10833569999977044
Traverse time of iterparse_new: 0.3242015000005267
Traverse time of iterparse: 0.3198846000004778
Recent improvements (#101438) made explicit close() not needed in CPython. But it can still be useful in alternative implementations like PyPy, and maybe in future Python versions, so it is worth to add it.
Activity
serhiy-storchaka commentedon Nov 23, 2015
If ElementTree.iterparse() is called with file names, it opens a file. When resulting iterator is not exhausted, the file lefts not closed.
Martin Panter proposed in bpo-25688 to add an explicit way to clean it up, like a generator.close() method.
Vgr255 commentedon Nov 23, 2015
I am unable to reproduce the issue on Windows 7 with 3.5.0; I have tried opening a small (non-empty) text. Here's the result:
serhiy-storchaka commentedon Nov 23, 2015
You have to enable deprecation warnings. Run the interpreter with the -Wa option.
Vgr255 commentedon Nov 23, 2015
Oh, my bad. Ignore my last message, behaviour is identical then. Thanks for clearing that up.
scoder commentedon Apr 27, 2019
I don't think there is a need for a close() method. Instead, the iterator should close the file first thing when it's done with it, but only if it owns it. Therefore, the fix in bpo-25688 seems correct.
Closing can also be done explicitly in a finaliser of the iterator, if implicit closing via decref is too lax.
serhiy-storchaka commentedon May 2, 2019
Implicit closing an exhausted iterator helps only the iterator is iterated to the end. If the iteration has been stopped before the end, we get a leak of the file descriptor. Closing the file descriptor in the finalizer can be deferred to undefined term, especially in implementations without reference counting. Since file descriptors are limited resource, this can cause troubles in real programs.
Reasons for close() in iterparse objects are the same as for close in files and generators.
Maybe we will need to implement the full generator protocol (send() and throw()) in the iterparse objects, but currently I do not know use cases for this.
scoder commentedon May 2, 2019
Ok, I think it's reasonable to make the resource management explicit for the specific case of letting iterparse() open the file. That suggests that there should also be context manager support, given that safe usages would often involve a try-finally.
Since it might not always be obvious for users when they need to close the iterator or not, I would also suggest to not let it raise an error on a double-close, i.e. if .close() was already called or the iterator was already exhausted (and the file closed automatically), calling .close() should just do nothing.
furkanonder commentedon May 6, 2020
The warning(main:1: ResourceWarning: unclosed file <_io.BufferedReader name='/dev/null'>) is no longer available in python3.8.2
Prometheus3375 commentedon Jun 30, 2022
I recently did a fully compatible implementation that solves an issue discussed here:
Features:
closemethod that does not raise an exception on double-close as @scoder asked (file descriptors also do not raise an error in such cases).iterparseobject, it is destroyed closing opened fd.Benchmarks:
I used this XML file for testing.
fixing ResourceWarning due to unclosed file when aborting iterparse i…
pythongh-69893: Add the close() method for xml.etree.ElementTree.iter…
serhiy-storchaka commentedon Jan 24, 2024
Recent improvements (#101438) made explicit close() not needed in CPython. But it can still be useful in alternative implementations like PyPy, and maybe in future Python versions, so it is worth to add it.
gh-69893: Add the close() method for xml.etree.ElementTree.iterparse(…
pythongh-69893: Add the close() method for xml.etree.ElementTree.iter…
pythongh-69893: Add the close() method for xml.etree.ElementTree.iter…