https://github.com/python/cpython/commit/afd8113e9d7744e52ae82093628d373d6ecb9897
commit: afd8113e9d7744e52ae82093628d373d6ecb9897
branch: main
author: Neko Asakura <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-03-30T19:42:24+03:00
summary:
gh-144270: Make SubElement parent and tag positional-only (GH-144845)
The C accelerator implementations use PyArg_ParseTuple, which
inherently enforces positional-only parameters. The Python fallback
allowed these as keyword arguments, creating a behavioral mismatch.
Make the tag parameter of Element.__init__ and the parent and tag
parameters of SubElement positional-only to align with the C
accelerator.
files:
A Misc/NEWS.d/next/Library/2026-02-19-16-34-18.gh-issue-144270.wJRtSr.rst
M Doc/library/xml.etree.elementtree.rst
M Lib/test/test_xml_etree.py
M Lib/xml/etree/ElementTree.py
diff --git a/Doc/library/xml.etree.elementtree.rst
b/Doc/library/xml.etree.elementtree.rst
index 919d4c595bf793..bbb15ce5e758c6 100644
--- a/Doc/library/xml.etree.elementtree.rst
+++ b/Doc/library/xml.etree.elementtree.rst
@@ -691,7 +691,7 @@ Functions
.. versionadded:: 3.2
-.. function:: SubElement(parent, tag, attrib={}, **extra)
+.. function:: SubElement(parent, tag, /, attrib={}, **extra)
Subelement factory. This function creates an element instance, and appends
it to an existing element.
@@ -705,6 +705,9 @@ Functions
.. versionchanged:: 3.15
*attrib* can now be a :class:`frozendict`.
+ .. versionchanged:: next
+ *parent* and *tag* are now positional-only parameters.
+
.. function:: tostring(element, encoding="us-ascii", method="xml", *, \
xml_declaration=None, default_namespace=None, \
@@ -880,7 +883,7 @@ Element Objects
:noindex:
:no-index:
-.. class:: Element(tag, attrib={}, **extra)
+.. class:: Element(tag, /, attrib={}, **extra)
Element class. This class defines the Element interface, and provides a
reference implementation of this interface.
@@ -893,6 +896,9 @@ Element Objects
.. versionchanged:: 3.15
*attrib* can now be a :class:`frozendict`.
+ .. versionchanged:: next
+ *tag* is now a positional-only parameter.
+
.. attribute:: tag
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
index 5b06e422672b1d..b380d0276b0169 100644
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -381,6 +381,19 @@ def test_simpleops(self):
self.serialize_check(element,
'<tag key="value"><subtag /><subtag /></tag>')
+ def test_positional_only_parameter(self):
+ # Test Element positional-only parameters (gh-144846).
+
+ # 'tag' is positional-only
+ with self.assertRaises(TypeError):
+ ET.Element(tag='fail')
+
+ # 'tag' and 'attrib' as kwarg/attribute names
+ e = ET.Element('e', attrib={'attrib': 'foo'}, tag='bar')
+ self.assertEqual(e.tag, 'e')
+ self.assertEqual(e.get('attrib'), 'foo')
+ self.assertEqual(e.get('tag'), 'bar')
+
def test_cdata(self):
# Test CDATA handling (etc).
@@ -484,6 +497,28 @@ def test_attrib(self):
self.assertEqual(ET.tostring(elem),
b'<test a=" " b=" " c="	 "
d=" 		 " />')
+ def test_subelement_positional_only_parameter(self):
+ # Test SubElement positional-only parameters (gh-144270).
+ parent = ET.Element('parent')
+
+ # 'parent' and 'tag' are positional-only
+ with self.assertRaises(TypeError):
+ ET.SubElement(parent=parent, tag='fail')
+ with self.assertRaises(TypeError):
+ ET.SubElement(parent, tag='fail')
+
+ # 'attrib' can be passed as keyword
+ sub1 = ET.SubElement(parent, 'sub1', attrib={'key': 'value'})
+ self.assertEqual(sub1.get('key'), 'value')
+
+ # 'tag' and 'parent' as kwargs become XML attributes, not func params
+ sub2 = ET.SubElement(parent, 'sub2', attrib={'attrib': 'foo'},
+ tag='bar', parent='baz')
+ self.assertEqual(sub2.tag, 'sub2')
+ self.assertEqual(sub2.get('attrib'), 'foo')
+ self.assertEqual(sub2.get('tag'), 'bar')
+ self.assertEqual(sub2.get('parent'), 'baz')
+
def test_makeelement(self):
# Test makeelement handling.
diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py
index 57c5b64ea3ba70..85766e02b531ce 100644
--- a/Lib/xml/etree/ElementTree.py
+++ b/Lib/xml/etree/ElementTree.py
@@ -164,7 +164,7 @@ class Element:
"""
- def __init__(self, tag, attrib={}, **extra):
+ def __init__(self, tag, /, attrib={}, **extra):
if not isinstance(attrib, (dict, frozendict)):
raise TypeError("attrib must be dict or frozendict, not %s" % (
attrib.__class__.__name__,))
@@ -416,7 +416,7 @@ def itertext(self):
yield t
-def SubElement(parent, tag, attrib={}, **extra):
+def SubElement(parent, tag, /, attrib={}, **extra):
"""Subelement factory which creates an element instance, and appends it
to an existing parent.
diff --git
a/Misc/NEWS.d/next/Library/2026-02-19-16-34-18.gh-issue-144270.wJRtSr.rst
b/Misc/NEWS.d/next/Library/2026-02-19-16-34-18.gh-issue-144270.wJRtSr.rst
new file mode 100644
index 00000000000000..b8a4374bc2d3ca
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-02-19-16-34-18.gh-issue-144270.wJRtSr.rst
@@ -0,0 +1,3 @@
+Made the *tag* parameter of :class:`xml.etree.ElementTree.Element` and the
+*parent* and *tag* parameters of :func:`xml.etree.ElementTree.SubElement`
+positional-only, matching the behavior of the C accelerator.
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]