Index: Lib/xml/dom/expatbuilder.py =================================================================== --- Lib/xml/dom/expatbuilder.py (revision 54643) +++ Lib/xml/dom/expatbuilder.py (working copy) @@ -171,9 +171,23 @@ self._elem_info = self.document._elem_info self._cdata = False + def unlink(self): + """Clean up cycles.""" + if self.document: + self.document.unlink() + self.document = None + self._parser = None + self._intern_setdefault = None + + def __enter__(self): + return self + + def __exit__(self, type, value, traceback): + self.unlink() + def install(self, parser): """Install the callbacks needed to build the DOM into the parser.""" - # This creates circular references! + # This creates circular references! Call unlink() to clear them. parser.StartDoctypeDeclHandler = self.start_doctype_decl_handler parser.StartElementHandler = self.first_element_handler parser.EndElementHandler = self.end_element_handler @@ -211,6 +225,10 @@ parser.Parse("", True) except ParseEscape: pass + except: + self.document.unlink() + self.reset() + raise doc = self.document self.reset() self._parser = None @@ -224,6 +242,10 @@ self._setup_subset(string) except ParseEscape: pass + except: + self.document.unlink() + self.reset() + raise doc = self.document self.reset() self._parser = None @@ -918,14 +940,12 @@ else: builder = ExpatBuilder() - if isinstance(file, StringTypes): - fp = open(file, 'rb') - try: - result = builder.parseFile(fp) - finally: - fp.close() - else: - result = builder.parseFile(file) + with builder: + if isinstance(file, StringTypes): + with open(file, 'rb') as fp: + result = builder.parseFile(fp) + else: + result = builder.parseFile(file) return result @@ -937,8 +957,9 @@ builder = ExpatBuilderNS() else: builder = ExpatBuilder() - return builder.parseString(string) + with builder: + return builder.parseString(string) def parseFragment(file, context, namespaces=True): """Parse a fragment of a document, given the context from which it @@ -952,14 +973,12 @@ else: builder = FragmentBuilder(context) - if isinstance(file, StringTypes): - fp = open(file, 'rb') - try: - result = builder.parseFile(fp) - finally: - fp.close() - else: - result = builder.parseFile(file) + with builder: + if isinstance(file, StringTypes): + with open(file, 'rb') as fp: + result = builder.parseFile(fp) + else: + result = builder.parseFile(file) return result @@ -972,8 +991,9 @@ builder = FragmentBuilderNS(context) else: builder = FragmentBuilder(context) - return builder.parseString(string) + with builder: + return builder.parseString(string) def makeBuilder(options): """Create a builder based on an Options object."""