diff --git a/Doc/library/html.parser.rst b/Doc/library/html.parser.rst index 11f851d4f6c4b74..6b0b7f7ff0e44b7 100644 --- a/Doc/library/html.parser.rst +++ b/Doc/library/html.parser.rst @@ -101,6 +101,12 @@ The output will then be: :meth:`close` is called. *data* must be :class:`str`. +.. method:: HTMLParser.flush() + + Process all data fed insofar as it consists of complete elements. Incomplete + data remains buffered. + + .. method:: HTMLParser.close() Force processing of all buffered data as if it were followed by an end-of-file diff --git a/Lib/html/parser.py b/Lib/html/parser.py index fbe0d3665e073cc..d4877247cf56b86 100644 --- a/Lib/html/parser.py +++ b/Lib/html/parser.py @@ -192,6 +192,17 @@ def feed(self, data): # Nothing was parsed; wait until the buffer doubles. self._parse_threshold = len(self.rawdata) + def flush(self): + """Process all data fed insofar as it consists of complete elements. + Incomplete data remains buffered. + """ + if self._pending: + self.rawdata += ''.join(self._pending) + self._pending.clear() + self._pending_len = 0 + self._parse_threshold = 1 + self.goahead(0) + def close(self): """Handle any buffered data.""" if self._pending: diff --git a/Lib/test/test_htmlparser.py b/Lib/test/test_htmlparser.py index 3fdaed4ff46b9d0..b4452e7ad0551b4 100644 --- a/Lib/test/test_htmlparser.py +++ b/Lib/test/test_htmlparser.py @@ -42,7 +42,9 @@ def get_events(self): else: L.append(event) prevtype = type + # Reset events and append for re-testing self.events = L + self.append = self.events.append return L # structure markup @@ -1021,6 +1023,24 @@ def test_convert_charrefs_dropped_text(self): ('endtag', 'a'), ('data', ' bar & baz')] ) + def test_flush(self): + attrs = [(f"a{i}", str(i)) for i in range(8)] + parts = ["", ""] + parser = EventCollector() + for part in parts: + parser.feed(part) + # confirm the fed data is buffered and not processed yet + self.assertEqual(parser.get_events(), []) + parser.flush() + expected = [ + ('starttag', 'div', attrs), + ('endtag', 'div'), + ] + self.assertEqual(parser.get_events(), expected) + parser.close() + # close should do nothing, event log is the same as before + self.assertEqual(parser.get_events(), expected) + @support.requires_resource('cpu') def test_eof_no_quadratic_complexity(self): # Each of these examples used to take about an hour. diff --git a/Misc/NEWS.d/next/Library/2026-09-05-15-21-00.gh-issue-157003.BOCIir.rst b/Misc/NEWS.d/next/Library/2026-09-05-15-21-00.gh-issue-157003.BOCIir.rst new file mode 100644 index 000000000000000..eb42caf55d205e8 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-05-15-21-00.gh-issue-157003.BOCIir.rst @@ -0,0 +1,3 @@ +Add ``HTMLParser.flush()`` to process all buffered data that consists of +complete elements. This method will be useful now that +``HTMLParser.feed()`` utilizes more extensive buffering.