Skip to content

Commit f63dec9

Browse files
[3.12] gh-101438: Avoid reference cycle in ElementTree.iterparse. (GH-114269) (GH-114499)
The iterator returned by ElementTree.iterparse() may hold on to a file descriptor. The reference cycle prevented prompt clean-up of the file descriptor if the returned iterator was not exhausted. (cherry picked from commit ce01ab5) Co-authored-by: Sam Gross <[email protected]>
1 parent a73a6a3 commit f63dec9

File tree

2 files changed

+21
-10
lines changed

2 files changed

+21
-10
lines changed

Lib/xml/etree/ElementTree.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
import collections
100100
import collections.abc
101101
import contextlib
102+
import weakref
102103

103104
from . import ElementPath
104105

@@ -1223,13 +1224,14 @@ def iterparse(source, events=None, parser=None):
12231224
# parser argument of iterparse is removed, this can be killed.
12241225
pullparser = XMLPullParser(events=events, _parser=parser)
12251226

1226-
def iterator(source):
1227+
if not hasattr(source, "read"):
1228+
source = open(source, "rb")
1229+
close_source = True
1230+
else:
12271231
close_source = False
1232+
1233+
def iterator(source):
12281234
try:
1229-
if not hasattr(source, "read"):
1230-
source = open(source, "rb")
1231-
close_source = True
1232-
yield None
12331235
while True:
12341236
yield from pullparser.read_events()
12351237
# load event buffer
@@ -1239,18 +1241,23 @@ def iterator(source):
12391241
pullparser.feed(data)
12401242
root = pullparser._close_and_return_root()
12411243
yield from pullparser.read_events()
1242-
it.root = root
1244+
it = wr()
1245+
if it is not None:
1246+
it.root = root
12431247
finally:
12441248
if close_source:
12451249
source.close()
12461250

12471251
class IterParseIterator(collections.abc.Iterator):
12481252
__next__ = iterator(source).__next__
1249-
it = IterParseIterator()
1250-
it.root = None
1251-
del iterator, IterParseIterator
12521253

1253-
next(it)
1254+
def __del__(self):
1255+
if close_source:
1256+
source.close()
1257+
1258+
it = IterParseIterator()
1259+
wr = weakref.ref(it)
1260+
del IterParseIterator
12541261
return it
12551262

12561263

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Avoid reference cycle in ElementTree.iterparse. The iterator returned by
2+
``ElementTree.iterparse`` may hold on to a file descriptor. The reference
3+
cycle prevented prompt clean-up of the file descriptor if the returned
4+
iterator was not exhausted.

0 commit comments

Comments
 (0)