Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Doc/library/xml.etree.elementtree.rst
Original file line number Diff line number Diff line change
Expand Up @@ -739,9 +739,14 @@ Functions
*prefix* is a namespace prefix. *uri* is a namespace uri. Tags and
attributes in this namespace will be serialized with the given prefix, if at
all possible.
:exc:`ValueError` is raised if *prefix* is invalid or reserved
(``ns`` followed by digits is reserved for the serializer).

.. versionadded:: 3.2

.. versionchanged:: next
Invalid and reserved prefixes are now rejected.


.. function:: SubElement(parent, tag, /, attrib={}, **extra)

Expand Down
30 changes: 30 additions & 0 deletions Lib/test/test_xml_etree.py
Original file line number Diff line number Diff line change
Expand Up @@ -2848,6 +2848,36 @@ def test_bug_200709_register_namespace(self):
self.assertEqual(ET.tostring(e),
b'<dc:title xmlns:dc="http://purl.org/dc/elements/1.1/" />')

def test_register_namespace_invalid(self):
# gh-157518
xml_ns = 'http://www.w3.org/XML/1998/namespace'
xmlns_ns = 'http://www.w3.org/2000/xmlns/'
nsmap = ET.register_namespace._namespace_map
saved = dict(nsmap)
for prefix, uri in [
('ns0', 'u'), ('ns12', 'u'),
('xml', 'u'), ('foo', xml_ns),
('xmlns', 'u'), ('foo', xmlns_ns),
('a:b', 'u'), ('1', 'u'), ('a b', 'u'), ('a\xa0b', 'u'),
]:
with self.subTest(prefix=prefix, uri=uri):
with self.assertRaises(ValueError):
ET.register_namespace(prefix, uri)
# the registry is not changed
self.assertEqual(nsmap, saved)
for prefix, uri in [(1, 'u'), (b'a', 'u'), ('a', 1), ('a', None)]:
with self.subTest(prefix=prefix, uri=uri):
with self.assertRaises(TypeError):
ET.register_namespace(prefix, uri)
# the xml prefix can be registered for its namespace
ET.register_namespace('xml', xml_ns)
self.assertEqual(ET.register_namespace._namespace_map[xml_ns], 'xml')
# non-ASCII names are valid
self.addCleanup(ET.register_namespace._namespace_map.pop, 'u', None)
ET.register_namespace('\xe9', 'u')
self.assertEqual(ET.tostring(ET.Element('{u}a'), encoding='unicode'),
'<\xe9:a xmlns:\xe9="u" />')

def test_bug_200709_element_comment(self):
# Not sure if this can be fixed, really (since the serializer needs
# ET.Comment, not cET.comment).
Expand Down
27 changes: 25 additions & 2 deletions Lib/xml/etree/ElementTree.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@
import contextlib
import weakref

from .. import is_valid_name
from . import ElementPath


Expand Down Expand Up @@ -1029,6 +1030,29 @@ def _serialize_text(write, elem):
}


_XML_NAMESPACE = "http://www.w3.org/XML/1998/namespace"
_XMLNS_NAMESPACE = "http://www.w3.org/2000/xmlns/"

def _check_prefix(prefix, uri):
# Check a namespace prefix and the namespace URI bound to it
# (see Namespaces in XML 1.0, 3 and 4). The empty prefix is allowed.
if not isinstance(prefix, str) or not isinstance(uri, str):
raise TypeError("namespace prefix and URI must be strings")
if re.match(r"ns\d+$", prefix):
raise ValueError("Prefix format reserved for internal use")
if prefix == "xml":
if uri != _XML_NAMESPACE:
raise ValueError("the 'xml' prefix can only be bound to "
"the XML namespace")
elif uri == _XML_NAMESPACE:
raise ValueError("the XML namespace can only be bound to "
"the 'xml' prefix")
elif prefix == "xmlns" or uri == _XMLNS_NAMESPACE:
raise ValueError("the 'xmlns' prefix cannot be bound")
elif prefix and (not is_valid_name(prefix) or ':' in prefix):
raise ValueError("invalid namespace prefix %r" % (prefix,))


def register_namespace(prefix, uri):
"""Register a namespace prefix.

Expand All @@ -1041,8 +1065,7 @@ def register_namespace(prefix, uri):
ValueError is raised if prefix is reserved or is invalid.

"""
if re.match(r"ns\d+$", prefix):
raise ValueError("Prefix format reserved for internal use")
_check_prefix(prefix, uri)
for k, v in list(_namespace_map.items()):
if k == uri or v == prefix:
del _namespace_map[k]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
:func:`xml.etree.ElementTree.register_namespace` now raises
:exc:`ValueError` for an invalid prefix, for the reserved ``xmlns`` prefix,
and for the ``xml`` prefix or the XML namespace bound to each other's wrong
counterpart, instead of accepting them and serializing invalid XML.
Loading