From f806830d1078151e1fca6e7021258af621c1b268 Mon Sep 17 00:00:00 2001 From: Paul Tobias Date: Mon, 17 Aug 2026 14:33:09 +0700 Subject: [PATCH] gh-155944: Catch only expected errors in getDOMImplementation fallback Don't catch all exceptions from the `well_known_implementations` candidates, because only the expected `ImportError` and `AttributeError` mean the implementation is unavailable. --- Lib/test/test_xml_dom_domreg.py | 54 +++++++++++++++++++ Lib/xml/dom/domreg.py | 3 +- ...-08-17-07-30-02.gh-issue-155944.PaEX0U.rst | 1 + 3 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 Lib/test/test_xml_dom_domreg.py create mode 100644 Misc/NEWS.d/next/Library/2026-08-17-07-30-02.gh-issue-155944.PaEX0U.rst diff --git a/Lib/test/test_xml_dom_domreg.py b/Lib/test/test_xml_dom_domreg.py new file mode 100644 index 000000000000000..1bbff2b09e0ca8c --- /dev/null +++ b/Lib/test/test_xml_dom_domreg.py @@ -0,0 +1,54 @@ +import sys +import unittest +from unittest import mock + +from xml.dom import domreg + + +class BrokenModule: + """Fake DOM implementation module whose factory is genuinely buggy.""" + + @staticmethod + def getDOMImplementation(): + raise RuntimeError("bug in implementation factory") + + +class MissingFactoryModule: + """Fake DOM implementation module without a factory (AttributeError).""" + + +class GetDOMImplementationFallbackTests(unittest.TestCase): + """The nameless getDOMImplementation() call tries each well-known + implementation and skips the unavailable ones. + """ + + def _patch_implementations(self, module): + self.enterContext( + mock.patch.dict(sys.modules, {"test_fake_dom": module}) + ) + self.enterContext( + mock.patch.dict( + domreg.well_known_implementations, + {"test_fake_dom": "test_fake_dom"}, + clear=True, + ) + ) + self.enterContext(mock.patch.dict(domreg.registered, clear=True)) + + def test_fallback_skips_unavailable_implementations(self): + # A missing factory (AttributeError) means "not available": skipped, + # and with no other implementation the lookup fails cleanly. + self._patch_implementations(MissingFactoryModule) + with self.assertRaises(ImportError): + domreg.getDOMImplementation() + + def test_fallback_propagates_unexpected_errors(self): + # gh-155944: a genuine bug in a candidate's factory must propagate, + # not be silently treated as "implementation not available". + self._patch_implementations(BrokenModule) + with self.assertRaises(RuntimeError): + domreg.getDOMImplementation() + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/xml/dom/domreg.py b/Lib/xml/dom/domreg.py index 69c17eebb265daa..666963c97317b5b 100644 --- a/Lib/xml/dom/domreg.py +++ b/Lib/xml/dom/domreg.py @@ -72,7 +72,8 @@ def getDOMImplementation(name=None, features=()): for creator in well_known_implementations.keys(): try: dom = getDOMImplementation(name = creator) - except Exception: # typically ImportError, or AttributeError + except (ImportError, AttributeError): + # The implementation is not installed or lacks the factory. continue if _good_enough(dom, features): return dom diff --git a/Misc/NEWS.d/next/Library/2026-08-17-07-30-02.gh-issue-155944.PaEX0U.rst b/Misc/NEWS.d/next/Library/2026-08-17-07-30-02.gh-issue-155944.PaEX0U.rst new file mode 100644 index 000000000000000..1eca898b4c920f5 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-08-17-07-30-02.gh-issue-155944.PaEX0U.rst @@ -0,0 +1 @@ +:func:`xml.dom.getDOMImplementation` no longer masks genuine errors raised by a well-known implementation's factory during fallback discovery. Only :exc:`ImportError` and :exc:`AttributeError` are treated as "implementation not available".