]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-139489: Add xml.is_valid_text() (GH-149412)
authorSerhiy Storchaka <storchaka@gmail.com>
Wed, 6 May 2026 14:40:10 +0000 (17:40 +0300)
committerGitHub <noreply@github.com>
Wed, 6 May 2026 14:40:10 +0000 (14:40 +0000)
Doc/library/xml.rst
Doc/whatsnew/3.15.rst
Lib/test/test_xml.py
Lib/xml/utils.py
Misc/NEWS.d/next/Library/2026-05-05-13-12-58.gh-issue-139489.a8qqIM.rst [new file with mode: 0644]

index f9ffaa9a94aacc2360b253c460a5b322904912fb..98be50e15ff463aa64712216e2565d48c160d200 100644 (file)
@@ -54,7 +54,19 @@ This module also defines utility functions.
    "!", "?", and "=" are forbidden.
    The name cannot start with a digit or a character like "-", ".", and "ยท".
 
-   ..versionadded:: next
+   .. versionadded:: next
+
+
+.. function:: is_valid_text(data)
+
+   Return ``True`` if the string is a sequence of legal XML 1.0 characters,
+   ``False`` otherwise.
+
+   Almost all characters are permitted in XML 1.0 documents, except C0 control
+   characters (excluding TAB, CR and LF), surrogate characters and special
+   Unicode characters U+FFFE and U+FFFF.
+
+   .. versionadded:: next
 
 
 .. _xml-security:
index 6007d772f8e2d7acbce8e3ca864b33d67a07a0e4..698a9f88e1ee3954d1dc07bea6342bf3d12ac39e 100644 (file)
@@ -1714,6 +1714,10 @@ xml
   whether a string can be used as an element or attribute name in XML.
   (Contributed by Serhiy Storchaka in :gh:`139489`.)
 
+* Add the :func:`xml.is_valid_text` function, which allows to check
+  whether a string can be used in the XML document.
+  (Contributed by Serhiy Storchaka in :gh:`139489`.)
+
 
 xml.parsers.expat
 -----------------
index fd3633e43982d76b137e0df6b81bca760db0a30d..3a8b92048166f256603c57e6e6e1a5ab9562d399 100644 (file)
@@ -22,6 +22,22 @@ class TestUtils(unittest.TestCase):
         for c in '<>/!?=\x00\x01\x7f\ud800\udfff\ufffe\uffff\U000F0000':
             self.assertFalse(is_valid_name('name' + c))
 
+    def test_is_valid_text(self):
+        is_valid_text = xml.is_valid_text
+        self.assertTrue(is_valid_text(''))
+        self.assertTrue(is_valid_text('!0Aa_~ \r\n\t\x85\xa0'))
+        self.assertTrue(is_valid_text('\ud7ff\ue000\ufffd\U00010000\U0010ffff'))
+        self.assertFalse(is_valid_text('\x00'))
+        self.assertFalse(is_valid_text('\x01'))
+        self.assertFalse(is_valid_text('\x1f'))
+        self.assertTrue(is_valid_text('\x7f'))
+        self.assertTrue(is_valid_text('\x80'))
+        self.assertTrue(is_valid_text('\x9f'))
+        self.assertFalse(is_valid_text('\ud800'))
+        self.assertFalse(is_valid_text('\udfff'))
+        self.assertFalse(is_valid_text('\ufffe'))
+        self.assertFalse(is_valid_text('\uffff'))
+
 
 if __name__ == '__main__':
     unittest.main()
index c9a0b260675beda607c3c3d2a4896ed6ada40113..532aa224dae6772243287784add377dd8e17c0d5 100644 (file)
@@ -23,3 +23,15 @@ def is_valid_name(name):
             '\uF900-\uFDCF\uFDF0-\uFFFD\U00010000-\U000EFFFF'
         ']*+',
         name) is not None
+
+# https://www.w3.org/TR/xml/#charsets
+_ILLEGAL_XML_CHAR = (
+    '['
+        '\x00-\x08\x0B\x0C\x0E-\x1F'    # C0 controls except TAB, CR and LF
+        '\uD800-\uDFFF'                 # the surrogate blocks
+        '\uFFFE\uFFFF'                  # special Unicode characters
+    ']')
+
+def is_valid_text(data):
+    """Test whether a string is a sequence of legal XML 1.0 characters."""
+    return _re.search(_ILLEGAL_XML_CHAR, data) is None
diff --git a/Misc/NEWS.d/next/Library/2026-05-05-13-12-58.gh-issue-139489.a8qqIM.rst b/Misc/NEWS.d/next/Library/2026-05-05-13-12-58.gh-issue-139489.a8qqIM.rst
new file mode 100644 (file)
index 0000000..c76879d
--- /dev/null
@@ -0,0 +1,2 @@
+Add the :func:`xml.is_valid_text` function, which allows to check whether
+a string can be used in the XML document.