Skip to content
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,9 @@ xml.parsers.expat

.. _billion laughs: https://en.wikipedia.org/wiki/Billion_laughs_attack

* Add support for namespace prefixes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be also documented in the expat docs.

Copy link
Contributor Author

@ukarroum ukarroum Nov 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you mean I should add a link to the C libexpat doc ?

EDIT: I think you probably meant to document this here: https://docs.python.org/3/library/pyexpat.html

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, to the specs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

(Contributed by Yassir Karroum in :gh:`118317`.)


zlib
----
Expand Down
34 changes: 33 additions & 1 deletion Lib/test/test_sax.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# regression test for SAX 2.0

from xml.sax import make_parser, ContentHandler, \
SAXException, SAXReaderNotAvailable, SAXParseException
SAXException, SAXReaderNotAvailable, SAXParseException, handler
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why import handler when ContentHandler is already imported? why not use the latter directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I replaced the handler.ContentHandler, with ContentHandler.
but I still need to keep the handler import for the namespace/namespace features.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about?:

Suggested change
SAXException, SAXReaderNotAvailable, SAXParseException, handler
SAXException, SAXReaderNotAvailable, SAXParseException
from xml.sax.handler import feature_namespaces, feature_namespace_prefixes

import unittest
from unittest import mock
try:
Expand Down Expand Up @@ -1307,6 +1307,38 @@ def test_expat_locator_withinfo_nonascii(self):
self.assertEqual(parser.getSystemId(), fname)
self.assertEqual(parser.getPublicId(), None)

def test_namespace_prefix(self):
parser = create_parser()
parser.setFeature(handler.feature_namespaces, 1)
parser.setFeature(handler.feature_namespace_prefixes, 1)

class Handler(handler.ContentHandler):
def startElementNS(self, name, qname, attrs):
self.qname = qname

h = Handler()

parser.setContentHandler(h)
parser.feed("<Q:E xmlns:Q='http://example.org/testuri'/>")
parser.close()
self.assertEqual(h.qname, "Q:E")

def test_default_namespace(self):
parser = create_parser()
parser.setFeature(handler.feature_namespaces, 1)

class Handler(handler.ContentHandler):
def startElementNS(self, name, qname, attrs):
self.qname = qname

h = Handler()

parser.setContentHandler(h)
parser.feed("<E xmlns='http://example.org/testuri'/>")
parser.close()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use parser.parse(..., True)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, but I didn't add the True, it seems that the parse method only accept one argument.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh. Wait, @hartwork should we do feed() + close() or parse()?

Copy link
Contributor

@hartwork hartwork Nov 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@picnixz hi!

My understanding is that:

  • create_parser is xml.sax.expatreader.create_parser and it creates an xml.sax.xmlreader.XMLReader, first sentence in the docs
  • xml.sax.xmlreader.XMLReader provides .parse that parses in one single go, one single parameter, no boolean isFinal
  • xml.sax.xmlreader.XMLReader does not provide .feed or .close because that needs IncrementalParser, not a plain XMLReader.
  • (xml.sax.expatreader.create_parser creates xml.sax.expatreader.ExpatParser that is an instance of both IncrementalParser and XMLReader for me in practice.)
  • I therefore consider parser.parse("<E xmlns='http://example.org/testuri'/>") to be better suited here because availability of .feed and .close is not guaranteed on interface level.
  • (If you want to stick to .feed here or elsewhere maybe add self.assertIsInstance(parser, IncrementalParser) prior to a call to communicate that expectation and have the test fail meaningfully for regressions in the future.)

What do you think?

self.assertEqual(h.qname, "E")

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't leave 3 blank lines, only 2 is sufficient.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed



# ===========================================================================
#
Expand Down
24 changes: 0 additions & 24 deletions Lib/test/test_xml_expatreader.py

This file was deleted.

This file was deleted.

Loading