mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Update xml and test_xml_etree
This commit is contained in:
1 parent
628c4c0edf
commit
2eec919416
13 files changed
+358
-168
No files matched your search
Vendored
+77
-25
@@ -10,7 +10,6 @@ import functools
|
||||
import html
|
||||
import io
|
||||
import itertools
|
||||
import locale
|
||||
import operator
|
||||
import os
|
||||
import pickle
|
||||
@@ -130,6 +129,9 @@ def checkwarnings(*filters, quiet=False):
|
||||
return newtest
|
||||
return decorator
|
||||
|
||||
def convlinesep(data):
|
||||
return data.replace(b'\n', os.linesep.encode())
|
||||
|
||||
|
||||
class ModuleTest(unittest.TestCase):
|
||||
def test_sanity(self):
|
||||
@@ -1023,17 +1025,15 @@ class ElementTreeTest(unittest.TestCase):
|
||||
@unittest.expectedFailure
|
||||
def test_tostring_xml_declaration_unicode_encoding(self):
|
||||
elem = ET.XML('<body><tag/></body>')
|
||||
preferredencoding = locale.getpreferredencoding()
|
||||
self.assertEqual(
|
||||
f"<?xml version='1.0' encoding='{preferredencoding}'?>\n<body><tag /></body>",
|
||||
ET.tostring(elem, encoding='unicode', xml_declaration=True)
|
||||
ET.tostring(elem, encoding='unicode', xml_declaration=True),
|
||||
"<?xml version='1.0' encoding='utf-8'?>\n<body><tag /></body>"
|
||||
)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_tostring_xml_declaration_cases(self):
|
||||
elem = ET.XML('<body><tag>ø</tag></body>')
|
||||
preferredencoding = locale.getpreferredencoding()
|
||||
TESTCASES = [
|
||||
# (expected_retval, encoding, xml_declaration)
|
||||
# ... xml_declaration = None
|
||||
@@ -1060,7 +1060,7 @@ class ElementTreeTest(unittest.TestCase):
|
||||
b"<body><tag>ø</tag></body>", 'US-ASCII', True),
|
||||
(b"<?xml version='1.0' encoding='ISO-8859-1'?>\n"
|
||||
b"<body><tag>\xf8</tag></body>", 'ISO-8859-1', True),
|
||||
(f"<?xml version='1.0' encoding='{preferredencoding}'?>\n"
|
||||
("<?xml version='1.0' encoding='utf-8'?>\n"
|
||||
"<body><tag>ø</tag></body>", 'unicode', True),
|
||||
|
||||
]
|
||||
@@ -1102,11 +1102,10 @@ class ElementTreeTest(unittest.TestCase):
|
||||
b"<?xml version='1.0' encoding='us-ascii'?>\n<body><tag /></body>"
|
||||
)
|
||||
|
||||
preferredencoding = locale.getpreferredencoding()
|
||||
stringlist = ET.tostringlist(elem, encoding='unicode', xml_declaration=True)
|
||||
self.assertEqual(
|
||||
''.join(stringlist),
|
||||
f"<?xml version='1.0' encoding='{preferredencoding}'?>\n<body><tag /></body>"
|
||||
"<?xml version='1.0' encoding='utf-8'?>\n<body><tag /></body>"
|
||||
)
|
||||
self.assertRegex(stringlist[0], r"^<\?xml version='1.0' encoding='.+'?>")
|
||||
self.assertEqual(['<body', '>', '<tag', ' />', '</body>'], stringlist[1:])
|
||||
@@ -3914,38 +3913,91 @@ class IOTest(unittest.TestCase):
|
||||
@unittest.expectedFailure
|
||||
def test_write_to_filename(self):
|
||||
self.addCleanup(os_helper.unlink, TESTFN)
|
||||
tree = ET.ElementTree(ET.XML('''<site />'''))
|
||||
tree = ET.ElementTree(ET.XML('''<site>\xf8</site>'''))
|
||||
tree.write(TESTFN)
|
||||
with open(TESTFN, 'rb') as f:
|
||||
self.assertEqual(f.read(), b'''<site />''')
|
||||
self.assertEqual(f.read(), b'''<site>ø</site>''')
|
||||
|
||||
def test_write_to_filename_with_encoding(self):
|
||||
self.addCleanup(os_helper.unlink, TESTFN)
|
||||
tree = ET.ElementTree(ET.XML('''<site>\xf8</site>'''))
|
||||
tree.write(TESTFN, encoding='utf-8')
|
||||
with open(TESTFN, 'rb') as f:
|
||||
self.assertEqual(f.read(), b'''<site>\xc3\xb8</site>''')
|
||||
|
||||
tree.write(TESTFN, encoding='ISO-8859-1')
|
||||
with open(TESTFN, 'rb') as f:
|
||||
self.assertEqual(f.read(), convlinesep(
|
||||
b'''<?xml version='1.0' encoding='ISO-8859-1'?>\n'''
|
||||
b'''<site>\xf8</site>'''))
|
||||
|
||||
def test_write_to_filename_as_unicode(self):
|
||||
self.addCleanup(os_helper.unlink, TESTFN)
|
||||
with open(TESTFN, 'w') as f:
|
||||
encoding = f.encoding
|
||||
os_helper.unlink(TESTFN)
|
||||
|
||||
tree = ET.ElementTree(ET.XML('''<site>\xf8</site>'''))
|
||||
tree.write(TESTFN, encoding='unicode')
|
||||
with open(TESTFN, 'rb') as f:
|
||||
self.assertEqual(f.read(), b"<site>\xc3\xb8</site>")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_write_to_text_file(self):
|
||||
self.addCleanup(os_helper.unlink, TESTFN)
|
||||
tree = ET.ElementTree(ET.XML('''<site />'''))
|
||||
tree = ET.ElementTree(ET.XML('''<site>\xf8</site>'''))
|
||||
with open(TESTFN, 'w', encoding='utf-8') as f:
|
||||
tree.write(f, encoding='unicode')
|
||||
self.assertFalse(f.closed)
|
||||
with open(TESTFN, 'rb') as f:
|
||||
self.assertEqual(f.read(), b'''<site />''')
|
||||
self.assertEqual(f.read(), b'''<site>\xc3\xb8</site>''')
|
||||
|
||||
with open(TESTFN, 'w', encoding='ascii', errors='xmlcharrefreplace') as f:
|
||||
tree.write(f, encoding='unicode')
|
||||
self.assertFalse(f.closed)
|
||||
with open(TESTFN, 'rb') as f:
|
||||
self.assertEqual(f.read(), b'''<site>ø</site>''')
|
||||
|
||||
with open(TESTFN, 'w', encoding='ISO-8859-1') as f:
|
||||
tree.write(f, encoding='unicode')
|
||||
self.assertFalse(f.closed)
|
||||
with open(TESTFN, 'rb') as f:
|
||||
self.assertEqual(f.read(), b'''<site>\xf8</site>''')
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_write_to_binary_file(self):
|
||||
self.addCleanup(os_helper.unlink, TESTFN)
|
||||
tree = ET.ElementTree(ET.XML('''<site />'''))
|
||||
tree = ET.ElementTree(ET.XML('''<site>\xf8</site>'''))
|
||||
with open(TESTFN, 'wb') as f:
|
||||
tree.write(f)
|
||||
self.assertFalse(f.closed)
|
||||
with open(TESTFN, 'rb') as f:
|
||||
self.assertEqual(f.read(), b'''<site />''')
|
||||
self.assertEqual(f.read(), b'''<site>ø</site>''')
|
||||
|
||||
def test_write_to_binary_file_with_encoding(self):
|
||||
self.addCleanup(os_helper.unlink, TESTFN)
|
||||
tree = ET.ElementTree(ET.XML('''<site>\xf8</site>'''))
|
||||
with open(TESTFN, 'wb') as f:
|
||||
tree.write(f, encoding='utf-8')
|
||||
self.assertFalse(f.closed)
|
||||
with open(TESTFN, 'rb') as f:
|
||||
self.assertEqual(f.read(), b'''<site>\xc3\xb8</site>''')
|
||||
|
||||
with open(TESTFN, 'wb') as f:
|
||||
tree.write(f, encoding='ISO-8859-1')
|
||||
self.assertFalse(f.closed)
|
||||
with open(TESTFN, 'rb') as f:
|
||||
self.assertEqual(f.read(),
|
||||
b'''<?xml version='1.0' encoding='ISO-8859-1'?>\n'''
|
||||
b'''<site>\xf8</site>''')
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_write_to_binary_file_with_bom(self):
|
||||
self.addCleanup(os_helper.unlink, TESTFN)
|
||||
tree = ET.ElementTree(ET.XML('''<site />'''))
|
||||
tree = ET.ElementTree(ET.XML('''<site>\xf8</site>'''))
|
||||
# test BOM writing to buffered file
|
||||
with open(TESTFN, 'wb') as f:
|
||||
tree.write(f, encoding='utf-16')
|
||||
@@ -3953,7 +4005,7 @@ class IOTest(unittest.TestCase):
|
||||
with open(TESTFN, 'rb') as f:
|
||||
self.assertEqual(f.read(),
|
||||
'''<?xml version='1.0' encoding='utf-16'?>\n'''
|
||||
'''<site />'''.encode("utf-16"))
|
||||
'''<site>\xf8</site>'''.encode("utf-16"))
|
||||
# test BOM writing to non-buffered file
|
||||
with open(TESTFN, 'wb', buffering=0) as f:
|
||||
tree.write(f, encoding='utf-16')
|
||||
@@ -3961,7 +4013,7 @@ class IOTest(unittest.TestCase):
|
||||
with open(TESTFN, 'rb') as f:
|
||||
self.assertEqual(f.read(),
|
||||
'''<?xml version='1.0' encoding='utf-16'?>\n'''
|
||||
'''<site />'''.encode("utf-16"))
|
||||
'''<site>\xf8</site>'''.encode("utf-16"))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@@ -3974,10 +4026,10 @@ class IOTest(unittest.TestCase):
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_write_to_stringio(self):
|
||||
tree = ET.ElementTree(ET.XML('''<site />'''))
|
||||
tree = ET.ElementTree(ET.XML('''<site>\xf8</site>'''))
|
||||
stream = io.StringIO()
|
||||
tree.write(stream, encoding='unicode')
|
||||
self.assertEqual(stream.getvalue(), '''<site />''')
|
||||
self.assertEqual(stream.getvalue(), '''<site>\xf8</site>''')
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@@ -3990,10 +4042,10 @@ class IOTest(unittest.TestCase):
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_write_to_bytesio(self):
|
||||
tree = ET.ElementTree(ET.XML('''<site />'''))
|
||||
tree = ET.ElementTree(ET.XML('''<site>\xf8</site>'''))
|
||||
raw = io.BytesIO()
|
||||
tree.write(raw)
|
||||
self.assertEqual(raw.getvalue(), b'''<site />''')
|
||||
self.assertEqual(raw.getvalue(), b'''<site>ø</site>''')
|
||||
|
||||
class dummy:
|
||||
pass
|
||||
@@ -4011,12 +4063,12 @@ class IOTest(unittest.TestCase):
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_write_to_user_text_writer(self):
|
||||
tree = ET.ElementTree(ET.XML('''<site />'''))
|
||||
tree = ET.ElementTree(ET.XML('''<site>\xf8</site>'''))
|
||||
stream = io.StringIO()
|
||||
writer = self.dummy()
|
||||
writer.write = stream.write
|
||||
tree.write(writer, encoding='unicode')
|
||||
self.assertEqual(stream.getvalue(), '''<site />''')
|
||||
self.assertEqual(stream.getvalue(), '''<site>\xf8</site>''')
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@@ -4032,12 +4084,12 @@ class IOTest(unittest.TestCase):
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_write_to_user_binary_writer(self):
|
||||
tree = ET.ElementTree(ET.XML('''<site />'''))
|
||||
tree = ET.ElementTree(ET.XML('''<site>\xf8</site>'''))
|
||||
raw = io.BytesIO()
|
||||
writer = self.dummy()
|
||||
writer.write = raw.write
|
||||
tree.write(writer)
|
||||
self.assertEqual(raw.getvalue(), b'''<site />''')
|
||||
self.assertEqual(raw.getvalue(), b'''<site>ø</site>''')
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
|
||||
Vendored
+4
-4
@@ -204,11 +204,11 @@ class ExpatBuilder:
|
||||
buffer = file.read(16*1024)
|
||||
if not buffer:
|
||||
break
|
||||
parser.Parse(buffer, 0)
|
||||
parser.Parse(buffer, False)
|
||||
if first_buffer and self.document.documentElement:
|
||||
self._setup_subset(buffer)
|
||||
first_buffer = False
|
||||
parser.Parse("", True)
|
||||
parser.Parse(b"", True)
|
||||
except ParseEscape:
|
||||
pass
|
||||
doc = self.document
|
||||
@@ -637,7 +637,7 @@ class FragmentBuilder(ExpatBuilder):
|
||||
nsattrs = self._getNSattrs() # get ns decls from node's ancestors
|
||||
document = _FRAGMENT_BUILDER_TEMPLATE % (ident, subset, nsattrs)
|
||||
try:
|
||||
parser.Parse(document, 1)
|
||||
parser.Parse(document, True)
|
||||
except:
|
||||
self.reset()
|
||||
raise
|
||||
@@ -697,7 +697,7 @@ class FragmentBuilder(ExpatBuilder):
|
||||
self.fragment = self.document.createDocumentFragment()
|
||||
self.curNode = self.fragment
|
||||
try:
|
||||
parser.Parse(self._source, 1)
|
||||
parser.Parse(self._source, True)
|
||||
finally:
|
||||
self.curNode = old_cur_node
|
||||
self.document = old_document
|
||||
|
||||
Vendored
+43
-13
@@ -43,10 +43,11 @@ class Node(xml.dom.Node):
|
||||
def __bool__(self):
|
||||
return True
|
||||
|
||||
def toxml(self, encoding=None):
|
||||
return self.toprettyxml("", "", encoding)
|
||||
def toxml(self, encoding=None, standalone=None):
|
||||
return self.toprettyxml("", "", encoding, standalone)
|
||||
|
||||
def toprettyxml(self, indent="\t", newl="\n", encoding=None):
|
||||
def toprettyxml(self, indent="\t", newl="\n", encoding=None,
|
||||
standalone=None):
|
||||
if encoding is None:
|
||||
writer = io.StringIO()
|
||||
else:
|
||||
@@ -56,7 +57,7 @@ class Node(xml.dom.Node):
|
||||
newline='\n')
|
||||
if self.nodeType == Node.DOCUMENT_NODE:
|
||||
# Can pass encoding only to document, to put it into XML header
|
||||
self.writexml(writer, "", indent, newl, encoding)
|
||||
self.writexml(writer, "", indent, newl, encoding, standalone)
|
||||
else:
|
||||
self.writexml(writer, "", indent, newl)
|
||||
if encoding is None:
|
||||
@@ -718,6 +719,14 @@ class Element(Node):
|
||||
Node.unlink(self)
|
||||
|
||||
def getAttribute(self, attname):
|
||||
"""Returns the value of the specified attribute.
|
||||
|
||||
Returns the value of the element's attribute named attname as
|
||||
a string. An empty string is returned if the element does not
|
||||
have such an attribute. Note that an empty string may also be
|
||||
returned as an explicitly given attribute value, use the
|
||||
hasAttribute method to distinguish these two cases.
|
||||
"""
|
||||
if self._attrs is None:
|
||||
return ""
|
||||
try:
|
||||
@@ -823,10 +832,16 @@ class Element(Node):
|
||||
# Restore this since the node is still useful and otherwise
|
||||
# unlinked
|
||||
node.ownerDocument = self.ownerDocument
|
||||
return node
|
||||
|
||||
removeAttributeNodeNS = removeAttributeNode
|
||||
|
||||
def hasAttribute(self, name):
|
||||
"""Checks whether the element has an attribute with the specified name.
|
||||
|
||||
Returns True if the element has an attribute with the specified name.
|
||||
Otherwise, returns False.
|
||||
"""
|
||||
if self._attrs is None:
|
||||
return False
|
||||
return name in self._attrs
|
||||
@@ -837,6 +852,11 @@ class Element(Node):
|
||||
return (namespaceURI, localName) in self._attrsNS
|
||||
|
||||
def getElementsByTagName(self, name):
|
||||
"""Returns all descendant elements with the given tag name.
|
||||
|
||||
Returns the list of all descendant elements (not direct children
|
||||
only) with the specified tag name.
|
||||
"""
|
||||
return _get_elements_by_tagName_helper(self, name, NodeList())
|
||||
|
||||
def getElementsByTagNameNS(self, namespaceURI, localName):
|
||||
@@ -847,22 +867,27 @@ class Element(Node):
|
||||
return "<DOM Element: %s at %#x>" % (self.tagName, id(self))
|
||||
|
||||
def writexml(self, writer, indent="", addindent="", newl=""):
|
||||
"""Write an XML element to a file-like object
|
||||
|
||||
Write the element to the writer object that must provide
|
||||
a write method (e.g. a file or StringIO object).
|
||||
"""
|
||||
# indent = current indentation
|
||||
# addindent = indentation to add to higher levels
|
||||
# newl = newline string
|
||||
writer.write(indent+"<" + self.tagName)
|
||||
|
||||
attrs = self._get_attributes()
|
||||
a_names = sorted(attrs.keys())
|
||||
|
||||
for a_name in a_names:
|
||||
for a_name in attrs.keys():
|
||||
writer.write(" %s=\"" % a_name)
|
||||
_write_data(writer, attrs[a_name].value)
|
||||
writer.write("\"")
|
||||
if self.childNodes:
|
||||
writer.write(">")
|
||||
if (len(self.childNodes) == 1 and
|
||||
self.childNodes[0].nodeType == Node.TEXT_NODE):
|
||||
self.childNodes[0].nodeType in (
|
||||
Node.TEXT_NODE, Node.CDATA_SECTION_NODE)):
|
||||
self.childNodes[0].writexml(writer, '', '', '')
|
||||
else:
|
||||
writer.write(newl)
|
||||
@@ -1786,12 +1811,17 @@ class Document(Node, DocumentLS):
|
||||
raise xml.dom.NotSupportedErr("cannot import document type nodes")
|
||||
return _clone_node(node, deep, self)
|
||||
|
||||
def writexml(self, writer, indent="", addindent="", newl="", encoding=None):
|
||||
if encoding is None:
|
||||
writer.write('<?xml version="1.0" ?>'+newl)
|
||||
else:
|
||||
writer.write('<?xml version="1.0" encoding="%s"?>%s' % (
|
||||
encoding, newl))
|
||||
def writexml(self, writer, indent="", addindent="", newl="", encoding=None,
|
||||
standalone=None):
|
||||
declarations = []
|
||||
|
||||
if encoding:
|
||||
declarations.append(f'encoding="{encoding}"')
|
||||
if standalone is not None:
|
||||
declarations.append(f'standalone="{"yes" if standalone else "no"}"')
|
||||
|
||||
writer.write(f'<?xml version="1.0" {" ".join(declarations)}?>{newl}')
|
||||
|
||||
for node in self.childNodes:
|
||||
node.writexml(writer, indent, addindent, newl)
|
||||
|
||||
|
||||
Vendored
+7
@@ -217,6 +217,13 @@ class DOMEventStream:
|
||||
self.parser.setContentHandler(self.pulldom)
|
||||
|
||||
def __getitem__(self, pos):
|
||||
import warnings
|
||||
warnings.warn(
|
||||
"DOMEventStream's __getitem__ method ignores 'pos' parameter. "
|
||||
"Use iterator protocol instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2
|
||||
)
|
||||
rc = self.getEvent()
|
||||
if rc:
|
||||
return rc
|
||||
|
||||
Vendored
+1
-24
@@ -1,7 +1,6 @@
|
||||
"""Implementation of the DOM Level 3 'LS-Load' feature."""
|
||||
|
||||
import copy
|
||||
import warnings
|
||||
import xml.dom
|
||||
|
||||
from xml.dom.NodeFilter import NodeFilter
|
||||
@@ -80,7 +79,7 @@ class DOMBuilder:
|
||||
settings = self._settings[(_name_xform(name), state)]
|
||||
except KeyError:
|
||||
raise xml.dom.NotSupportedErr(
|
||||
"unsupported feature: %r" % (name,))
|
||||
"unsupported feature: %r" % (name,)) from None
|
||||
else:
|
||||
for name, value in settings:
|
||||
setattr(self._options, name, value)
|
||||
@@ -332,29 +331,10 @@ class DOMBuilderFilter:
|
||||
del NodeFilter
|
||||
|
||||
|
||||
class _AsyncDeprecatedProperty:
|
||||
def warn(self, cls):
|
||||
clsname = cls.__name__
|
||||
warnings.warn(
|
||||
"{cls}.async is deprecated; use {cls}.async_".format(cls=clsname),
|
||||
DeprecationWarning)
|
||||
|
||||
def __get__(self, instance, cls):
|
||||
self.warn(cls)
|
||||
if instance is not None:
|
||||
return instance.async_
|
||||
return False
|
||||
|
||||
def __set__(self, instance, value):
|
||||
self.warn(type(instance))
|
||||
setattr(instance, 'async_', value)
|
||||
|
||||
|
||||
class DocumentLS:
|
||||
"""Mixin to create documents that conform to the load/save spec."""
|
||||
|
||||
async_ = False
|
||||
locals()['async'] = _AsyncDeprecatedProperty() # Avoid DeprecationWarning
|
||||
|
||||
def _get_async(self):
|
||||
return False
|
||||
@@ -384,9 +364,6 @@ class DocumentLS:
|
||||
return snode.toxml()
|
||||
|
||||
|
||||
del _AsyncDeprecatedProperty
|
||||
|
||||
|
||||
class DOMImplementationLS:
|
||||
MODE_SYNCHRONOUS = 1
|
||||
MODE_ASYNCHRONOUS = 2
|
||||
|
||||
Vendored
+50
-8
@@ -42,7 +42,7 @@
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
# See http://www.python.org/psf/license for licensing details.
|
||||
# See https://www.python.org/psf/license for licensing details.
|
||||
|
||||
##
|
||||
# Limited XInclude support for the ElementTree package.
|
||||
@@ -50,18 +50,28 @@
|
||||
|
||||
import copy
|
||||
from . import ElementTree
|
||||
from urllib.parse import urljoin
|
||||
|
||||
XINCLUDE = "{http://www.w3.org/2001/XInclude}"
|
||||
|
||||
XINCLUDE_INCLUDE = XINCLUDE + "include"
|
||||
XINCLUDE_FALLBACK = XINCLUDE + "fallback"
|
||||
|
||||
# For security reasons, the inclusion depth is limited to this read-only value by default.
|
||||
DEFAULT_MAX_INCLUSION_DEPTH = 6
|
||||
|
||||
|
||||
##
|
||||
# Fatal include error.
|
||||
|
||||
class FatalIncludeError(SyntaxError):
|
||||
pass
|
||||
|
||||
|
||||
class LimitedRecursiveIncludeError(FatalIncludeError):
|
||||
pass
|
||||
|
||||
|
||||
##
|
||||
# Default loader. This loader reads an included resource from disk.
|
||||
#
|
||||
@@ -92,13 +102,33 @@ def default_loader(href, parse, encoding=None):
|
||||
# @param loader Optional resource loader. If omitted, it defaults
|
||||
# to {@link default_loader}. If given, it should be a callable
|
||||
# that implements the same interface as <b>default_loader</b>.
|
||||
# @param base_url The base URL of the original file, to resolve
|
||||
# relative include file references.
|
||||
# @param max_depth The maximum number of recursive inclusions.
|
||||
# Limited to reduce the risk of malicious content explosion.
|
||||
# Pass a negative value to disable the limitation.
|
||||
# @throws LimitedRecursiveIncludeError If the {@link max_depth} was exceeded.
|
||||
# @throws FatalIncludeError If the function fails to include a given
|
||||
# resource, or if the tree contains malformed XInclude elements.
|
||||
# @throws OSError If the function fails to load a given resource.
|
||||
# @throws IOError If the function fails to load a given resource.
|
||||
# @returns the node or its replacement if it was an XInclude node
|
||||
|
||||
def include(elem, loader=None):
|
||||
def include(elem, loader=None, base_url=None,
|
||||
max_depth=DEFAULT_MAX_INCLUSION_DEPTH):
|
||||
if max_depth is None:
|
||||
max_depth = -1
|
||||
elif max_depth < 0:
|
||||
raise ValueError("expected non-negative depth or None for 'max_depth', got %r" % max_depth)
|
||||
|
||||
if hasattr(elem, 'getroot'):
|
||||
elem = elem.getroot()
|
||||
if loader is None:
|
||||
loader = default_loader
|
||||
|
||||
_include(elem, loader, base_url, max_depth, set())
|
||||
|
||||
|
||||
def _include(elem, loader, base_url, max_depth, _parent_hrefs):
|
||||
# look for xinclude elements
|
||||
i = 0
|
||||
while i < len(elem):
|
||||
@@ -106,14 +136,24 @@ def include(elem, loader=None):
|
||||
if e.tag == XINCLUDE_INCLUDE:
|
||||
# process xinclude directive
|
||||
href = e.get("href")
|
||||
if base_url:
|
||||
href = urljoin(base_url, href)
|
||||
parse = e.get("parse", "xml")
|
||||
if parse == "xml":
|
||||
if href in _parent_hrefs:
|
||||
raise FatalIncludeError("recursive include of %s" % href)
|
||||
if max_depth == 0:
|
||||
raise LimitedRecursiveIncludeError(
|
||||
"maximum xinclude depth reached when including file %s" % href)
|
||||
_parent_hrefs.add(href)
|
||||
node = loader(href, parse)
|
||||
if node is None:
|
||||
raise FatalIncludeError(
|
||||
"cannot load %r as %r" % (href, parse)
|
||||
)
|
||||
node = copy.copy(node)
|
||||
node = copy.copy(node) # FIXME: this makes little sense with recursive includes
|
||||
_include(node, loader, href, max_depth - 1, _parent_hrefs)
|
||||
_parent_hrefs.remove(href)
|
||||
if e.tail:
|
||||
node.tail = (node.tail or "") + e.tail
|
||||
elem[i] = node
|
||||
@@ -123,11 +163,13 @@ def include(elem, loader=None):
|
||||
raise FatalIncludeError(
|
||||
"cannot load %r as %r" % (href, parse)
|
||||
)
|
||||
if e.tail:
|
||||
text += e.tail
|
||||
if i:
|
||||
node = elem[i-1]
|
||||
node.tail = (node.tail or "") + text + (e.tail or "")
|
||||
node.tail = (node.tail or "") + text
|
||||
else:
|
||||
elem.text = (elem.text or "") + text + (e.tail or "")
|
||||
elem.text = (elem.text or "") + text
|
||||
del elem[i]
|
||||
continue
|
||||
else:
|
||||
@@ -139,5 +181,5 @@ def include(elem, loader=None):
|
||||
"xi:fallback tag must be child of xi:include (%r)" % e.tag
|
||||
)
|
||||
else:
|
||||
include(e, loader)
|
||||
i = i + 1
|
||||
_include(e, loader, base_url, max_depth, _parent_hrefs)
|
||||
i += 1
|
||||
Vendored
+25
-9
@@ -48,7 +48,7 @@
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
# See http://www.python.org/psf/license for licensing details.
|
||||
# See https://www.python.org/psf/license for licensing details.
|
||||
|
||||
##
|
||||
# Implementation module for XPath support. There's usually no reason
|
||||
@@ -65,8 +65,9 @@ xpath_tokenizer_re = re.compile(
|
||||
r"//?|"
|
||||
r"\.\.|"
|
||||
r"\(\)|"
|
||||
r"!=|"
|
||||
r"[/.*:\[\]\(\)@=])|"
|
||||
r"((?:\{[^}]+\})?[^/\[\]\(\)@=\s]+)|"
|
||||
r"((?:\{[^}]+\})?[^/\[\]\(\)@!=\s]+)|"
|
||||
r"\s+"
|
||||
)
|
||||
|
||||
@@ -225,7 +226,6 @@ def prepare_parent(next, token):
|
||||
|
||||
def prepare_predicate(next, token):
|
||||
# FIXME: replace with real parser!!! refs:
|
||||
# http://effbot.org/zone/simple-iterator-parser.htm
|
||||
# http://javascript.crockford.com/tdop/tdop.html
|
||||
signature = []
|
||||
predicate = []
|
||||
@@ -253,15 +253,19 @@ def prepare_predicate(next, token):
|
||||
if elem.get(key) is not None:
|
||||
yield elem
|
||||
return select
|
||||
if signature == "@-='":
|
||||
# [@attribute='value']
|
||||
if signature == "@-='" or signature == "@-!='":
|
||||
# [@attribute='value'] or [@attribute!='value']
|
||||
key = predicate[1]
|
||||
value = predicate[-1]
|
||||
def select(context, result):
|
||||
for elem in result:
|
||||
if elem.get(key) == value:
|
||||
yield elem
|
||||
return select
|
||||
def select_negated(context, result):
|
||||
for elem in result:
|
||||
if (attr_value := elem.get(key)) is not None and attr_value != value:
|
||||
yield elem
|
||||
return select_negated if '!=' in signature else select
|
||||
if signature == "-" and not re.match(r"\-?\d+$", predicate[0]):
|
||||
# [tag]
|
||||
tag = predicate[0]
|
||||
@@ -270,8 +274,10 @@ def prepare_predicate(next, token):
|
||||
if elem.find(tag) is not None:
|
||||
yield elem
|
||||
return select
|
||||
if signature == ".='" or (signature == "-='" and not re.match(r"\-?\d+$", predicate[0])):
|
||||
# [.='value'] or [tag='value']
|
||||
if signature == ".='" or signature == ".!='" or (
|
||||
(signature == "-='" or signature == "-!='")
|
||||
and not re.match(r"\-?\d+$", predicate[0])):
|
||||
# [.='value'] or [tag='value'] or [.!='value'] or [tag!='value']
|
||||
tag = predicate[0]
|
||||
value = predicate[-1]
|
||||
if tag:
|
||||
@@ -281,12 +287,22 @@ def prepare_predicate(next, token):
|
||||
if "".join(e.itertext()) == value:
|
||||
yield elem
|
||||
break
|
||||
def select_negated(context, result):
|
||||
for elem in result:
|
||||
for e in elem.iterfind(tag):
|
||||
if "".join(e.itertext()) != value:
|
||||
yield elem
|
||||
break
|
||||
else:
|
||||
def select(context, result):
|
||||
for elem in result:
|
||||
if "".join(elem.itertext()) == value:
|
||||
yield elem
|
||||
return select
|
||||
def select_negated(context, result):
|
||||
for elem in result:
|
||||
if "".join(elem.itertext()) != value:
|
||||
yield elem
|
||||
return select_negated if '!=' in signature else select
|
||||
if signature == "-" or signature == "-()" or signature == "-()-":
|
||||
# [index] or [last()] or [last()-index]
|
||||
if signature == "-":
|
||||
|
||||
Vendored
+90
-70
@@ -35,7 +35,7 @@
|
||||
|
||||
#---------------------------------------------------------------------
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
# See http://www.python.org/psf/license for licensing details.
|
||||
# See https://www.python.org/psf/license for licensing details.
|
||||
#
|
||||
# ElementTree
|
||||
# Copyright (c) 1999-2008 by Fredrik Lundh. All rights reserved.
|
||||
@@ -76,7 +76,7 @@ __all__ = [
|
||||
"dump",
|
||||
"Element", "ElementTree",
|
||||
"fromstring", "fromstringlist",
|
||||
"iselement", "iterparse",
|
||||
"indent", "iselement", "iterparse",
|
||||
"parse", "ParseError",
|
||||
"PI", "ProcessingInstruction",
|
||||
"QName",
|
||||
@@ -195,6 +195,13 @@ class Element:
|
||||
original tree.
|
||||
|
||||
"""
|
||||
warnings.warn(
|
||||
"elem.copy() is deprecated. Use copy.copy(elem) instead.",
|
||||
DeprecationWarning
|
||||
)
|
||||
return self.__copy__()
|
||||
|
||||
def __copy__(self):
|
||||
elem = self.makeelement(self.tag, self.attrib)
|
||||
elem.text = self.text
|
||||
elem.tail = self.tail
|
||||
@@ -273,19 +280,6 @@ class Element:
|
||||
# assert iselement(element)
|
||||
self._children.remove(subelement)
|
||||
|
||||
def getchildren(self):
|
||||
"""(Deprecated) Return all subelements.
|
||||
|
||||
Elements are returned in document order.
|
||||
|
||||
"""
|
||||
warnings.warn(
|
||||
"This method will be removed in future versions. "
|
||||
"Use 'list(elem)' or iteration over elem instead.",
|
||||
DeprecationWarning, stacklevel=2
|
||||
)
|
||||
return self._children
|
||||
|
||||
def find(self, path, namespaces=None):
|
||||
"""Find first matching element by tag name or path.
|
||||
|
||||
@@ -409,15 +403,6 @@ class Element:
|
||||
for e in self._children:
|
||||
yield from e.iter(tag)
|
||||
|
||||
# compatibility
|
||||
def getiterator(self, tag=None):
|
||||
warnings.warn(
|
||||
"This method will be removed in future versions. "
|
||||
"Use 'elem.iter()' or 'list(elem.iter())' instead.",
|
||||
DeprecationWarning, stacklevel=2
|
||||
)
|
||||
return list(self.iter(tag))
|
||||
|
||||
def itertext(self):
|
||||
"""Create text iterator.
|
||||
|
||||
@@ -617,15 +602,6 @@ class ElementTree:
|
||||
# assert self._root is not None
|
||||
return self._root.iter(tag)
|
||||
|
||||
# compatibility
|
||||
def getiterator(self, tag=None):
|
||||
warnings.warn(
|
||||
"This method will be removed in future versions. "
|
||||
"Use 'tree.iter()' or 'list(tree.iter())' instead.",
|
||||
DeprecationWarning, stacklevel=2
|
||||
)
|
||||
return list(self.iter(tag))
|
||||
|
||||
def find(self, path, namespaces=None):
|
||||
"""Find first matching element by tag name or path.
|
||||
|
||||
@@ -752,16 +728,11 @@ class ElementTree:
|
||||
encoding = "utf-8"
|
||||
else:
|
||||
encoding = "us-ascii"
|
||||
enc_lower = encoding.lower()
|
||||
with _get_writer(file_or_filename, enc_lower) as write:
|
||||
with _get_writer(file_or_filename, encoding) as (write, declared_encoding):
|
||||
if method == "xml" and (xml_declaration or
|
||||
(xml_declaration is None and
|
||||
enc_lower not in ("utf-8", "us-ascii", "unicode"))):
|
||||
declared_encoding = encoding
|
||||
if enc_lower == "unicode":
|
||||
# Retrieve the default encoding for the xml declaration
|
||||
import locale
|
||||
declared_encoding = locale.getpreferredencoding()
|
||||
encoding.lower() != "unicode" and
|
||||
declared_encoding.lower() not in ("utf-8", "us-ascii"))):
|
||||
write("<?xml version='1.0' encoding='%s'?>\n" % (
|
||||
declared_encoding,))
|
||||
if method == "text":
|
||||
@@ -786,19 +757,17 @@ def _get_writer(file_or_filename, encoding):
|
||||
write = file_or_filename.write
|
||||
except AttributeError:
|
||||
# file_or_filename is a file name
|
||||
if encoding == "unicode":
|
||||
file = open(file_or_filename, "w")
|
||||
else:
|
||||
file = open(file_or_filename, "w", encoding=encoding,
|
||||
errors="xmlcharrefreplace")
|
||||
with file:
|
||||
yield file.write
|
||||
if encoding.lower() == "unicode":
|
||||
encoding="utf-8"
|
||||
with open(file_or_filename, "w", encoding=encoding,
|
||||
errors="xmlcharrefreplace") as file:
|
||||
yield file.write, encoding
|
||||
else:
|
||||
# file_or_filename is a file-like object
|
||||
# encoding determines if it is a text or binary writer
|
||||
if encoding == "unicode":
|
||||
if encoding.lower() == "unicode":
|
||||
# use a text writer as is
|
||||
yield write
|
||||
yield write, getattr(file_or_filename, "encoding", None) or "utf-8"
|
||||
else:
|
||||
# wrap a binary writer with TextIOWrapper
|
||||
with contextlib.ExitStack() as stack:
|
||||
@@ -829,7 +798,7 @@ def _get_writer(file_or_filename, encoding):
|
||||
# Keep the original file open when the TextIOWrapper is
|
||||
# destroyed
|
||||
stack.callback(file.detach)
|
||||
yield file.write
|
||||
yield file.write, encoding
|
||||
|
||||
def _namespaces(elem, default_namespace=None):
|
||||
# identify namespaces used in this tree
|
||||
@@ -1081,15 +1050,15 @@ def _escape_attrib(text):
|
||||
text = text.replace(">", ">")
|
||||
if "\"" in text:
|
||||
text = text.replace("\"", """)
|
||||
# The following business with carriage returns is to satisfy
|
||||
# Section 2.11 of the XML specification, stating that
|
||||
# CR or CR LN should be replaced with just LN
|
||||
# Although section 2.11 of the XML specification states that CR or
|
||||
# CR LN should be replaced with just LN, it applies only to EOLNs
|
||||
# which take part of organizing file into lines. Within attributes,
|
||||
# we are replacing these with entity numbers, so they do not count.
|
||||
# http://www.w3.org/TR/REC-xml/#sec-line-ends
|
||||
if "\r\n" in text:
|
||||
text = text.replace("\r\n", "\n")
|
||||
# The current solution, contained in following six lines, was
|
||||
# discussed in issue 17582 and 39011.
|
||||
if "\r" in text:
|
||||
text = text.replace("\r", "\n")
|
||||
#The following four lines are issue 17582
|
||||
text = text.replace("\r", " ")
|
||||
if "\n" in text:
|
||||
text = text.replace("\n", " ")
|
||||
if "\t" in text:
|
||||
@@ -1185,6 +1154,57 @@ def dump(elem):
|
||||
if not tail or tail[-1] != "\n":
|
||||
sys.stdout.write("\n")
|
||||
|
||||
|
||||
def indent(tree, space=" ", level=0):
|
||||
"""Indent an XML document by inserting newlines and indentation space
|
||||
after elements.
|
||||
|
||||
*tree* is the ElementTree or Element to modify. The (root) element
|
||||
itself will not be changed, but the tail text of all elements in its
|
||||
subtree will be adapted.
|
||||
|
||||
*space* is the whitespace to insert for each indentation level, two
|
||||
space characters by default.
|
||||
|
||||
*level* is the initial indentation level. Setting this to a higher
|
||||
value than 0 can be used for indenting subtrees that are more deeply
|
||||
nested inside of a document.
|
||||
"""
|
||||
if isinstance(tree, ElementTree):
|
||||
tree = tree.getroot()
|
||||
if level < 0:
|
||||
raise ValueError(f"Initial indentation level must be >= 0, got {level}")
|
||||
if not len(tree):
|
||||
return
|
||||
|
||||
# Reduce the memory consumption by reusing indentation strings.
|
||||
indentations = ["\n" + level * space]
|
||||
|
||||
def _indent_children(elem, level):
|
||||
# Start a new indentation level for the first child.
|
||||
child_level = level + 1
|
||||
try:
|
||||
child_indentation = indentations[child_level]
|
||||
except IndexError:
|
||||
child_indentation = indentations[level] + space
|
||||
indentations.append(child_indentation)
|
||||
|
||||
if not elem.text or not elem.text.strip():
|
||||
elem.text = child_indentation
|
||||
|
||||
for child in elem:
|
||||
if len(child):
|
||||
_indent_children(child, child_level)
|
||||
if not child.tail or not child.tail.strip():
|
||||
child.tail = child_indentation
|
||||
|
||||
# Dedent after the last child by overwriting the previous indentation.
|
||||
if not child.tail.strip():
|
||||
child.tail = indentations[level]
|
||||
|
||||
_indent_children(tree, 0)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# parsing
|
||||
|
||||
@@ -1221,8 +1241,14 @@ def iterparse(source, events=None, parser=None):
|
||||
# Use the internal, undocumented _parser argument for now; When the
|
||||
# parser argument of iterparse is removed, this can be killed.
|
||||
pullparser = XMLPullParser(events=events, _parser=parser)
|
||||
def iterator():
|
||||
|
||||
def iterator(source):
|
||||
close_source = False
|
||||
try:
|
||||
if not hasattr(source, "read"):
|
||||
source = open(source, "rb")
|
||||
close_source = True
|
||||
yield None
|
||||
while True:
|
||||
yield from pullparser.read_events()
|
||||
# load event buffer
|
||||
@@ -1238,16 +1264,12 @@ def iterparse(source, events=None, parser=None):
|
||||
source.close()
|
||||
|
||||
class IterParseIterator(collections.abc.Iterator):
|
||||
__next__ = iterator().__next__
|
||||
__next__ = iterator(source).__next__
|
||||
it = IterParseIterator()
|
||||
it.root = None
|
||||
del iterator, IterParseIterator
|
||||
|
||||
close_source = False
|
||||
if not hasattr(source, "read"):
|
||||
source = open(source, "rb")
|
||||
close_source = True
|
||||
|
||||
next(it)
|
||||
return it
|
||||
|
||||
|
||||
@@ -1256,7 +1278,7 @@ class XMLPullParser:
|
||||
def __init__(self, events=None, *, _parser=None):
|
||||
# The _parser argument is for internal use only and must not be relied
|
||||
# upon in user code. It will be removed in a future release.
|
||||
# See http://bugs.python.org/issue17741 for more details.
|
||||
# See https://bugs.python.org/issue17741 for more details.
|
||||
|
||||
self._events_queue = collections.deque()
|
||||
self._parser = _parser or XMLParser(target=TreeBuilder())
|
||||
@@ -1533,7 +1555,6 @@ class XMLParser:
|
||||
# Configure pyexpat: buffering, new-style attribute handling.
|
||||
parser.buffer_text = 1
|
||||
parser.ordered_attributes = 1
|
||||
parser.specified_attributes = 1
|
||||
self._doctype = None
|
||||
self.entity = {}
|
||||
try:
|
||||
@@ -1553,7 +1574,6 @@ class XMLParser:
|
||||
for event_name in events_to_report:
|
||||
if event_name == "start":
|
||||
parser.ordered_attributes = 1
|
||||
parser.specified_attributes = 1
|
||||
def handler(tag, attrib_in, event=event_name, append=append,
|
||||
start=self._start):
|
||||
append((event, start(tag, attrib_in)))
|
||||
@@ -1690,14 +1710,14 @@ class XMLParser:
|
||||
def feed(self, data):
|
||||
"""Feed encoded data to parser."""
|
||||
try:
|
||||
self.parser.Parse(data, 0)
|
||||
self.parser.Parse(data, False)
|
||||
except self._error as v:
|
||||
self._raiseerror(v)
|
||||
|
||||
def close(self):
|
||||
"""Finish feeding data to parser and return element structure."""
|
||||
try:
|
||||
self.parser.Parse("", 1) # end of data
|
||||
self.parser.Parse(b"", True) # end of data
|
||||
except self._error as v:
|
||||
self._raiseerror(v)
|
||||
try:
|
||||
|
||||
Vendored
+1
-1
@@ -30,4 +30,4 @@
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# Licensed to PSF under a Contributor Agreement.
|
||||
# See http://www.python.org/psf/license for licensing details.
|
||||
# See https://www.python.org/psf/license for licensing details.
|
||||
Vendored
+5
-5
@@ -67,18 +67,18 @@ if sys.platform[:4] == "java" and sys.registry.containsKey(_key):
|
||||
default_parser_list = sys.registry.getProperty(_key).split(",")
|
||||
|
||||
|
||||
def make_parser(parser_list = []):
|
||||
def make_parser(parser_list=()):
|
||||
"""Creates and returns a SAX parser.
|
||||
|
||||
Creates the first parser it is able to instantiate of the ones
|
||||
given in the list created by doing parser_list +
|
||||
default_parser_list. The lists must contain the names of Python
|
||||
given in the iterable created by chaining parser_list and
|
||||
default_parser_list. The iterables must contain the names of Python
|
||||
modules containing both a SAX parser and a create_parser function."""
|
||||
|
||||
for parser_name in parser_list + default_parser_list:
|
||||
for parser_name in list(parser_list) + default_parser_list:
|
||||
try:
|
||||
return _create_parser(parser_name)
|
||||
except ImportError as e:
|
||||
except ImportError:
|
||||
import sys
|
||||
if parser_name in sys.modules:
|
||||
# The parser module was found, but importing it
|
||||
|
||||
Vendored
+7
-7
@@ -93,7 +93,7 @@ class ExpatParser(xmlreader.IncrementalParser, xmlreader.Locator):
|
||||
self._parser = None
|
||||
self._namespaces = namespaceHandling
|
||||
self._lex_handler_prop = None
|
||||
self._parsing = 0
|
||||
self._parsing = False
|
||||
self._entity_stack = []
|
||||
self._external_ges = 0
|
||||
self._interning = None
|
||||
@@ -203,10 +203,10 @@ class ExpatParser(xmlreader.IncrementalParser, xmlreader.Locator):
|
||||
|
||||
# IncrementalParser methods
|
||||
|
||||
def feed(self, data, isFinal = 0):
|
||||
def feed(self, data, isFinal=False):
|
||||
if not self._parsing:
|
||||
self.reset()
|
||||
self._parsing = 1
|
||||
self._parsing = True
|
||||
self._cont_handler.startDocument()
|
||||
|
||||
try:
|
||||
@@ -237,13 +237,13 @@ class ExpatParser(xmlreader.IncrementalParser, xmlreader.Locator):
|
||||
# If we are completing an external entity, do nothing here
|
||||
return
|
||||
try:
|
||||
self.feed("", isFinal = 1)
|
||||
self.feed(b"", isFinal=True)
|
||||
self._cont_handler.endDocument()
|
||||
self._parsing = 0
|
||||
self._parsing = False
|
||||
# break cycle created by expat handlers pointing to our methods
|
||||
self._parser = None
|
||||
finally:
|
||||
self._parsing = 0
|
||||
self._parsing = False
|
||||
if self._parser is not None:
|
||||
# Keep ErrorColumnNumber and ErrorLineNumber after closing.
|
||||
parser = _ClosedParser()
|
||||
@@ -307,7 +307,7 @@ class ExpatParser(xmlreader.IncrementalParser, xmlreader.Locator):
|
||||
self._parser.SetParamEntityParsing(
|
||||
expat.XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE)
|
||||
|
||||
self._parsing = 0
|
||||
self._parsing = False
|
||||
self._entity_stack = []
|
||||
|
||||
# Locator methods
|
||||
|
||||
Vendored
+45
@@ -340,3 +340,48 @@ all_properties = [property_lexical_handler,
|
||||
property_xml_string,
|
||||
property_encoding,
|
||||
property_interning_dict]
|
||||
|
||||
|
||||
class LexicalHandler:
|
||||
"""Optional SAX2 handler for lexical events.
|
||||
|
||||
This handler is used to obtain lexical information about an XML
|
||||
document, that is, information about how the document was encoded
|
||||
(as opposed to what it contains, which is reported to the
|
||||
ContentHandler), such as comments and CDATA marked section
|
||||
boundaries.
|
||||
|
||||
To set the LexicalHandler of an XMLReader, use the setProperty
|
||||
method with the property identifier
|
||||
'http://xml.org/sax/properties/lexical-handler'."""
|
||||
|
||||
def comment(self, content):
|
||||
"""Reports a comment anywhere in the document (including the
|
||||
DTD and outside the document element).
|
||||
|
||||
content is a string that holds the contents of the comment."""
|
||||
|
||||
def startDTD(self, name, public_id, system_id):
|
||||
"""Report the start of the DTD declarations, if the document
|
||||
has an associated DTD.
|
||||
|
||||
A startEntity event will be reported before declaration events
|
||||
from the external DTD subset are reported, and this can be
|
||||
used to infer from which subset DTD declarations derive.
|
||||
|
||||
name is the name of the document element type, public_id the
|
||||
public identifier of the DTD (or None if none were supplied)
|
||||
and system_id the system identfier of the external subset (or
|
||||
None if none were supplied)."""
|
||||
|
||||
def endDTD(self):
|
||||
"""Signals the end of DTD declarations."""
|
||||
|
||||
def startCDATA(self):
|
||||
"""Reports the beginning of a CDATA marked section.
|
||||
|
||||
The contents of the CDATA marked section will be reported
|
||||
through the characters event."""
|
||||
|
||||
def endCDATA(self):
|
||||
"""Reports the end of a CDATA marked section."""
|
||||
Vendored
+3
-2
@@ -56,8 +56,7 @@ def quoteattr(data, entities={}):
|
||||
the optional entities parameter. The keys and values must all be
|
||||
strings; each key will be replaced with its corresponding value.
|
||||
"""
|
||||
entities = entities.copy()
|
||||
entities.update({'\n': ' ', '\r': ' ', '\t':'	'})
|
||||
entities = {**entities, '\n': ' ', '\r': ' ', '\t':'	'}
|
||||
data = escape(data, entities)
|
||||
if '"' in data:
|
||||
if "'" in data:
|
||||
@@ -340,6 +339,8 @@ def prepare_input_source(source, base=""):
|
||||
"""This function takes an InputSource and an optional base URL and
|
||||
returns a fully resolved InputSource object ready for reading."""
|
||||
|
||||
if isinstance(source, os.PathLike):
|
||||
source = os.fspath(source)
|
||||
if isinstance(source, str):
|
||||
source = xmlreader.InputSource(source)
|
||||
elif hasattr(source, "read"):
|
||||
|
||||
Reference in new issue
Block a user