mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Update xml to 3.14.3
This commit is contained in:
1788
Lib/test/test_minidom.py
vendored
Normal file
1788
Lib/test/test_minidom.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
17
Lib/test/test_pulldom.py
vendored
17
Lib/test/test_pulldom.py
vendored
@@ -23,8 +23,8 @@ SMALL_SAMPLE = """<?xml version="1.0"?>
|
||||
|
||||
|
||||
class PullDOMTestCase(unittest.TestCase):
|
||||
# TODO: RUSTPYTHON FileNotFoundError: [Errno 2] No such file or directory (os error 2): 'xmltestdata/test.xml' -> 'None'
|
||||
@unittest.expectedFailure
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; FileNotFoundError: [Errno 2] No such file or directory (os error 2): 'xmltestdata/test.xml' -> 'None'
|
||||
def test_parse(self):
|
||||
"""Minimal test of DOMEventStream.parse()"""
|
||||
|
||||
@@ -41,15 +41,14 @@ class PullDOMTestCase(unittest.TestCase):
|
||||
with open(tstfile, "rb") as fin:
|
||||
list(pulldom.parse(fin))
|
||||
|
||||
# TODO: RUSTPYTHON implement DOM semantic
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; implement DOM semantic
|
||||
def test_parse_semantics(self):
|
||||
"""Test DOMEventStream parsing semantics."""
|
||||
|
||||
items = pulldom.parseString(SMALL_SAMPLE)
|
||||
evt, node = next(items)
|
||||
# Just check the node is a Document:
|
||||
self.assertTrue(hasattr(node, "createElement"))
|
||||
self.assertHasAttr(node, "createElement")
|
||||
self.assertEqual(pulldom.START_DOCUMENT, evt)
|
||||
evt, node = next(items)
|
||||
self.assertEqual(pulldom.START_ELEMENT, evt)
|
||||
@@ -105,8 +104,7 @@ class PullDOMTestCase(unittest.TestCase):
|
||||
#evt, node = next(items)
|
||||
#self.assertEqual(pulldom.END_DOCUMENT, evt)
|
||||
|
||||
# TODO: RUSTPYTHON pulldom.parseString(SMALL_SAMPLE) return iterator with tuple with 2 elements
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; pulldom.parseString(SMALL_SAMPLE) return iterator with tuple with 2 elements
|
||||
def test_expandItem(self):
|
||||
"""Ensure expandItem works as expected."""
|
||||
items = pulldom.parseString(SMALL_SAMPLE)
|
||||
@@ -197,7 +195,7 @@ class ThoroughTestCase(unittest.TestCase):
|
||||
evt, node = next(pd)
|
||||
self.assertEqual(pulldom.START_DOCUMENT, evt)
|
||||
# Just check the node is a Document:
|
||||
self.assertTrue(hasattr(node, "createElement"))
|
||||
self.assertHasAttr(node, "createElement")
|
||||
|
||||
if before_root:
|
||||
evt, node = next(pd)
|
||||
@@ -303,8 +301,7 @@ class SAX2DOMTestCase(unittest.TestCase):
|
||||
def confirm(self, test, testname="Test"):
|
||||
self.assertTrue(test, testname)
|
||||
|
||||
# TODO: RUSTPYTHON read from stream io
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; read from stream io
|
||||
def test_basic(self):
|
||||
"""Ensure SAX2DOM can parse from a stream."""
|
||||
with io.StringIO(SMALL_SAMPLE) as fin:
|
||||
|
||||
273
Lib/test/test_pyexpat.py
vendored
273
Lib/test/test_pyexpat.py
vendored
@@ -1,14 +1,18 @@
|
||||
# XXX TypeErrors on calling handlers, or on bad return values from a
|
||||
# handler, are obscure and unhelpful.
|
||||
|
||||
import abc
|
||||
import functools
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import sysconfig
|
||||
import textwrap
|
||||
import unittest
|
||||
import traceback
|
||||
from io import BytesIO
|
||||
from test import support
|
||||
from test.support import os_helper
|
||||
from test.support import import_helper, os_helper
|
||||
|
||||
from xml.parsers import expat
|
||||
from xml.parsers.expat import errors
|
||||
@@ -261,7 +265,7 @@ class ParseTest(unittest.TestCase):
|
||||
operations = out.out
|
||||
self._verify_parse_output(operations)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_parse_again(self):
|
||||
parser = expat.ParserCreate()
|
||||
file = BytesIO(data)
|
||||
@@ -282,7 +286,7 @@ class NamespaceSeparatorTest(unittest.TestCase):
|
||||
expat.ParserCreate(namespace_separator=None)
|
||||
expat.ParserCreate(namespace_separator=' ')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_illegal(self):
|
||||
with self.assertRaisesRegex(TypeError,
|
||||
r"ParserCreate\(\) argument (2|'namespace_separator') "
|
||||
@@ -309,7 +313,7 @@ class NamespaceSeparatorTest(unittest.TestCase):
|
||||
|
||||
|
||||
class InterningTest(unittest.TestCase):
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test(self):
|
||||
# Test the interning machinery.
|
||||
p = expat.ParserCreate()
|
||||
@@ -325,7 +329,7 @@ class InterningTest(unittest.TestCase):
|
||||
# L should have the same string repeated over and over.
|
||||
self.assertTrue(tag is entry)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_issue9402(self):
|
||||
# create an ExternalEntityParserCreate with buffer text
|
||||
class ExternalOutputter:
|
||||
@@ -383,7 +387,7 @@ class BufferTextTest(unittest.TestCase):
|
||||
parser = expat.ParserCreate()
|
||||
self.assertFalse(parser.buffer_text)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_buffering_enabled(self):
|
||||
# Make sure buffering is turned on
|
||||
self.assertTrue(self.parser.buffer_text)
|
||||
@@ -391,7 +395,7 @@ class BufferTextTest(unittest.TestCase):
|
||||
self.assertEqual(self.stuff, ['123'],
|
||||
"buffered text not properly collapsed")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test1(self):
|
||||
# XXX This test exposes more detail of Expat's text chunking than we
|
||||
# XXX like, but it tests what we need to concisely.
|
||||
@@ -401,7 +405,7 @@ class BufferTextTest(unittest.TestCase):
|
||||
["<a>", "1", "<b>", "2", "\n", "3", "<c>", "4\n5"],
|
||||
"buffering control not reacting as expected")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test2(self):
|
||||
self.parser.Parse(b"<a>1<b/><2><c/> \n 3</a>", True)
|
||||
self.assertEqual(self.stuff, ["1<2> \n 3"],
|
||||
@@ -434,7 +438,7 @@ class BufferTextTest(unittest.TestCase):
|
||||
["<a>", "1", "<b>", "</b>", "2", "<c>", "</c>", "345", "</a>"],
|
||||
"buffered text not properly split")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test7(self):
|
||||
self.setHandlers(["CommentHandler", "EndElementHandler",
|
||||
"StartElementHandler"])
|
||||
@@ -536,7 +540,7 @@ class PositionTest(unittest.TestCase):
|
||||
|
||||
|
||||
class sf1296433Test(unittest.TestCase):
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: Expected type 'str' but 'bytes' found.
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError: Expected type 'str' but 'bytes' found.
|
||||
def test_parse_only_xml_data(self):
|
||||
# https://bugs.python.org/issue1296433
|
||||
#
|
||||
@@ -560,15 +564,15 @@ class ChardataBufferTest(unittest.TestCase):
|
||||
test setting of chardata buffer size
|
||||
"""
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_1025_bytes(self):
|
||||
self.assertEqual(self.small_buffer_test(1025), 2)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_1000_bytes(self):
|
||||
self.assertEqual(self.small_buffer_test(1000), 1)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_wrong_size(self):
|
||||
parser = expat.ParserCreate()
|
||||
parser.buffer_text = 1
|
||||
@@ -581,7 +585,7 @@ class ChardataBufferTest(unittest.TestCase):
|
||||
with self.assertRaises(TypeError):
|
||||
parser.buffer_size = 512.0
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_unchanged_size(self):
|
||||
xml1 = b"<?xml version='1.0' encoding='iso8859'?><s>" + b'a' * 512
|
||||
xml2 = b'a'*512 + b'</s>'
|
||||
@@ -605,7 +609,7 @@ class ChardataBufferTest(unittest.TestCase):
|
||||
self.assertEqual(self.n, 2)
|
||||
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_disabling_buffer(self):
|
||||
xml1 = b"<?xml version='1.0' encoding='iso8859'?><a>" + b'a' * 512
|
||||
xml2 = b'b' * 1024
|
||||
@@ -650,7 +654,7 @@ class ChardataBufferTest(unittest.TestCase):
|
||||
parser.Parse(xml)
|
||||
return self.n
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_change_size_1(self):
|
||||
xml1 = b"<?xml version='1.0' encoding='iso8859'?><a><s>" + b'a' * 1024
|
||||
xml2 = b'aaa</s><s>' + b'a' * 1025 + b'</s></a>'
|
||||
@@ -667,7 +671,7 @@ class ChardataBufferTest(unittest.TestCase):
|
||||
parser.Parse(xml2, True)
|
||||
self.assertEqual(self.n, 2)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_change_size_2(self):
|
||||
xml1 = b"<?xml version='1.0' encoding='iso8859'?><a>a<s>" + b'a' * 1023
|
||||
xml2 = b'aaa</s><s>' + b'a' * 1025 + b'</s></a>'
|
||||
@@ -684,8 +688,25 @@ class ChardataBufferTest(unittest.TestCase):
|
||||
parser.Parse(xml2, True)
|
||||
self.assertEqual(self.n, 4)
|
||||
|
||||
class ElementDeclHandlerTest(unittest.TestCase):
|
||||
def test_trigger_leak(self):
|
||||
# Unfixed, this test would leak the memory of the so-called
|
||||
# "content model" in function ``my_ElementDeclHandler`` of pyexpat.
|
||||
# See https://github.com/python/cpython/issues/140593.
|
||||
data = textwrap.dedent('''\
|
||||
<!DOCTYPE quotations SYSTEM "quotations.dtd" [
|
||||
<!ELEMENT root ANY>
|
||||
]>
|
||||
<root/>
|
||||
''').encode('UTF-8')
|
||||
|
||||
parser = expat.ParserCreate()
|
||||
parser.NotStandaloneHandler = lambda: 1.234 # arbitrary float
|
||||
parser.ElementDeclHandler = lambda _1, _2: None
|
||||
self.assertRaises(TypeError, parser.Parse, data, True)
|
||||
|
||||
class MalformedInputTest(unittest.TestCase):
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test1(self):
|
||||
xml = b"\0\r\n"
|
||||
parser = expat.ParserCreate()
|
||||
@@ -695,7 +716,7 @@ class MalformedInputTest(unittest.TestCase):
|
||||
except expat.ExpatError as e:
|
||||
self.assertEqual(str(e), 'unclosed token: line 2, column 0')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test2(self):
|
||||
# \xc2\x85 is UTF-8 encoded U+0085 (NEXT LINE)
|
||||
xml = b"<?xml version\xc2\x85='1.0'?>\r\n"
|
||||
@@ -705,13 +726,13 @@ class MalformedInputTest(unittest.TestCase):
|
||||
parser.Parse(xml, True)
|
||||
|
||||
class ErrorMessageTest(unittest.TestCase):
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_codes(self):
|
||||
# verify mapping of errors.codes and errors.messages
|
||||
self.assertEqual(errors.XML_ERROR_SYNTAX,
|
||||
errors.messages[errors.codes[errors.XML_ERROR_SYNTAX]])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_expaterror(self):
|
||||
xml = b'<'
|
||||
parser = expat.ParserCreate()
|
||||
@@ -727,7 +748,7 @@ class ForeignDTDTests(unittest.TestCase):
|
||||
"""
|
||||
Tests for the UseForeignDTD method of expat parser objects.
|
||||
"""
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_use_foreign_dtd(self):
|
||||
"""
|
||||
If UseForeignDTD is passed True and a document without an external
|
||||
@@ -756,7 +777,7 @@ class ForeignDTDTests(unittest.TestCase):
|
||||
parser.Parse(b"<?xml version='1.0'?><element/>")
|
||||
self.assertEqual(handler_call_args, [(None, None)])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_ignore_use_foreign_dtd(self):
|
||||
"""
|
||||
If UseForeignDTD is passed True and a document with an external
|
||||
@@ -785,7 +806,7 @@ class ParentParserLifetimeTest(unittest.TestCase):
|
||||
See https://github.com/python/cpython/issues/139400.
|
||||
"""
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'ExternalEntityParserCreate'
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'ExternalEntityParserCreate'
|
||||
def test_parent_parser_outlives_its_subparsers__single(self):
|
||||
parser = expat.ParserCreate()
|
||||
subparser = parser.ExternalEntityParserCreate(None)
|
||||
@@ -794,7 +815,7 @@ class ParentParserLifetimeTest(unittest.TestCase):
|
||||
# while it's still being referenced by a related subparser.
|
||||
del parser
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'ExternalEntityParserCreate'
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'ExternalEntityParserCreate'
|
||||
def test_parent_parser_outlives_its_subparsers__multiple(self):
|
||||
parser = expat.ParserCreate()
|
||||
subparser_one = parser.ExternalEntityParserCreate(None)
|
||||
@@ -804,7 +825,7 @@ class ParentParserLifetimeTest(unittest.TestCase):
|
||||
# while it's still being referenced by a related subparser.
|
||||
del parser
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'ExternalEntityParserCreate'
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'ExternalEntityParserCreate'
|
||||
def test_parent_parser_outlives_its_subparsers__chain(self):
|
||||
parser = expat.ParserCreate()
|
||||
subparser = parser.ExternalEntityParserCreate(None)
|
||||
@@ -817,7 +838,7 @@ class ParentParserLifetimeTest(unittest.TestCase):
|
||||
|
||||
|
||||
class ReparseDeferralTest(unittest.TestCase):
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'GetReparseDeferralEnabled'
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'GetReparseDeferralEnabled'
|
||||
def test_getter_setter_round_trip(self):
|
||||
parser = expat.ParserCreate()
|
||||
enabled = (expat.version_info >= (2, 6, 0))
|
||||
@@ -828,7 +849,7 @@ class ReparseDeferralTest(unittest.TestCase):
|
||||
parser.SetReparseDeferralEnabled(True)
|
||||
self.assertIs(parser.GetReparseDeferralEnabled(), enabled)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'GetReparseDeferralEnabled'
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'GetReparseDeferralEnabled'
|
||||
def test_reparse_deferral_enabled(self):
|
||||
if expat.version_info < (2, 6, 0):
|
||||
self.skipTest(f'Expat {expat.version_info} does not '
|
||||
@@ -853,7 +874,7 @@ class ReparseDeferralTest(unittest.TestCase):
|
||||
|
||||
self.assertEqual(started, ['doc'])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetReparseDeferralEnabled'
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'xmlparser' object has no attribute 'SetReparseDeferralEnabled'
|
||||
def test_reparse_deferral_disabled(self):
|
||||
started = []
|
||||
|
||||
@@ -873,5 +894,199 @@ class ReparseDeferralTest(unittest.TestCase):
|
||||
self.assertEqual(started, ['doc'])
|
||||
|
||||
|
||||
class AttackProtectionTestBase(abc.ABC):
|
||||
"""
|
||||
Base class for testing protections against XML payloads with
|
||||
disproportionate amplification.
|
||||
|
||||
The protections being tested should detect and prevent attacks
|
||||
that leverage disproportionate amplification from small inputs.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def exponential_expansion_payload(*, nrows, ncols, text='.'):
|
||||
"""Create a billion laughs attack payload.
|
||||
|
||||
Be careful: the number of total items is pow(n, k), thereby
|
||||
requiring at least pow(ncols, nrows) * sizeof(text) memory!
|
||||
"""
|
||||
template = textwrap.dedent(f"""\
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE doc [
|
||||
<!ENTITY row0 "{text}">
|
||||
<!ELEMENT doc (#PCDATA)>
|
||||
{{body}}
|
||||
]>
|
||||
<doc>&row{nrows};</doc>
|
||||
""").rstrip()
|
||||
|
||||
body = '\n'.join(
|
||||
f'<!ENTITY row{i + 1} "{f"&row{i};" * ncols}">'
|
||||
for i in range(nrows)
|
||||
)
|
||||
body = textwrap.indent(body, ' ' * 4)
|
||||
return template.format(body=body)
|
||||
|
||||
def test_payload_generation(self):
|
||||
# self-test for exponential_expansion_payload()
|
||||
payload = self.exponential_expansion_payload(nrows=2, ncols=3)
|
||||
self.assertEqual(payload, textwrap.dedent("""\
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE doc [
|
||||
<!ENTITY row0 ".">
|
||||
<!ELEMENT doc (#PCDATA)>
|
||||
<!ENTITY row1 "&row0;&row0;&row0;">
|
||||
<!ENTITY row2 "&row1;&row1;&row1;">
|
||||
]>
|
||||
<doc>&row2;</doc>
|
||||
""").rstrip())
|
||||
|
||||
def assert_root_parser_failure(self, func, /, *args, **kwargs):
|
||||
"""Check that func(*args, **kwargs) is invalid for a sub-parser."""
|
||||
msg = "parser must be a root parser"
|
||||
self.assertRaisesRegex(expat.ExpatError, msg, func, *args, **kwargs)
|
||||
|
||||
@abc.abstractmethod
|
||||
def assert_rejected(self, func, /, *args, **kwargs):
|
||||
"""Assert that func(*args, **kwargs) triggers the attack protection.
|
||||
|
||||
Note: this method must ensure that the attack protection being tested
|
||||
is the one that is actually triggered at runtime, e.g., by matching
|
||||
the exact error message.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_activation_threshold(self, parser, threshold):
|
||||
"""Set the activation threshold for the tested protection."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_maximum_amplification(self, parser, max_factor):
|
||||
"""Set the maximum amplification factor for the tested protection."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def test_set_activation_threshold__threshold_reached(self):
|
||||
"""Test when the activation threshold is exceeded."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def test_set_activation_threshold__threshold_not_reached(self):
|
||||
"""Test when the activation threshold is not exceeded."""
|
||||
|
||||
def test_set_activation_threshold__invalid_threshold_type(self):
|
||||
parser = expat.ParserCreate()
|
||||
setter = functools.partial(self.set_activation_threshold, parser)
|
||||
|
||||
self.assertRaises(TypeError, setter, 1.0)
|
||||
self.assertRaises(TypeError, setter, -1.5)
|
||||
self.assertRaises(ValueError, setter, -5)
|
||||
|
||||
def test_set_activation_threshold__invalid_threshold_range(self):
|
||||
_testcapi = import_helper.import_module("_testcapi")
|
||||
parser = expat.ParserCreate()
|
||||
setter = functools.partial(self.set_activation_threshold, parser)
|
||||
|
||||
self.assertRaises(OverflowError, setter, _testcapi.ULLONG_MAX + 1)
|
||||
|
||||
def test_set_activation_threshold__fail_for_subparser(self):
|
||||
parser = expat.ParserCreate()
|
||||
subparser = parser.ExternalEntityParserCreate(None)
|
||||
setter = functools.partial(self.set_activation_threshold, subparser)
|
||||
self.assert_root_parser_failure(setter, 12345)
|
||||
|
||||
@abc.abstractmethod
|
||||
def test_set_maximum_amplification__amplification_exceeded(self):
|
||||
"""Test when the amplification factor is exceeded."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def test_set_maximum_amplification__amplification_not_exceeded(self):
|
||||
"""Test when the amplification factor is not exceeded."""
|
||||
|
||||
def test_set_maximum_amplification__infinity(self):
|
||||
inf = float('inf') # an 'inf' threshold is allowed by Expat
|
||||
parser = expat.ParserCreate()
|
||||
self.assertIsNone(self.set_maximum_amplification(parser, inf))
|
||||
|
||||
def test_set_maximum_amplification__invalid_max_factor_type(self):
|
||||
parser = expat.ParserCreate()
|
||||
setter = functools.partial(self.set_maximum_amplification, parser)
|
||||
|
||||
self.assertRaises(TypeError, setter, None)
|
||||
self.assertRaises(TypeError, setter, 'abc')
|
||||
|
||||
def test_set_maximum_amplification__invalid_max_factor_range(self):
|
||||
parser = expat.ParserCreate()
|
||||
setter = functools.partial(self.set_maximum_amplification, parser)
|
||||
|
||||
msg = re.escape("'max_factor' must be at least 1.0")
|
||||
self.assertRaisesRegex(expat.ExpatError, msg, setter, float('nan'))
|
||||
self.assertRaisesRegex(expat.ExpatError, msg, setter, 0.99)
|
||||
|
||||
def test_set_maximum_amplification__fail_for_subparser(self):
|
||||
parser = expat.ParserCreate()
|
||||
subparser = parser.ExternalEntityParserCreate(None)
|
||||
setter = functools.partial(self.set_maximum_amplification, subparser)
|
||||
self.assert_root_parser_failure(setter, 123.45)
|
||||
|
||||
|
||||
@unittest.skipIf(expat.version_info < (2, 7, 2), "requires Expat >= 2.7.2")
|
||||
class MemoryProtectionTest(AttackProtectionTestBase, unittest.TestCase):
|
||||
|
||||
# NOTE: with the default Expat configuration, the billion laughs protection
|
||||
# may hit before the allocation limiter if exponential_expansion_payload()
|
||||
# is not carefully parametrized. As such, the payloads should be chosen so
|
||||
# that either the allocation limiter is hit before other protections are
|
||||
# triggered or no protection at all is triggered.
|
||||
|
||||
def assert_rejected(self, func, /, *args, **kwargs):
|
||||
"""Check that func(*args, **kwargs) hits the allocation limit."""
|
||||
msg = r"out of memory: line \d+, column \d+"
|
||||
self.assertRaisesRegex(expat.ExpatError, msg, func, *args, **kwargs)
|
||||
|
||||
def set_activation_threshold(self, parser, threshold):
|
||||
return parser.SetAllocTrackerActivationThreshold(threshold)
|
||||
|
||||
def set_maximum_amplification(self, parser, max_factor):
|
||||
return parser.SetAllocTrackerMaximumAmplification(max_factor)
|
||||
|
||||
def test_set_activation_threshold__threshold_reached(self):
|
||||
parser = expat.ParserCreate()
|
||||
# Choose a threshold expected to be always reached.
|
||||
self.set_activation_threshold(parser, 3)
|
||||
# Check that the threshold is reached by choosing a small factor
|
||||
# and a payload whose peak amplification factor exceeds it.
|
||||
self.assertIsNone(self.set_maximum_amplification(parser, 1.0))
|
||||
payload = self.exponential_expansion_payload(ncols=10, nrows=4)
|
||||
self.assert_rejected(parser.Parse, payload, True)
|
||||
|
||||
def test_set_activation_threshold__threshold_not_reached(self):
|
||||
parser = expat.ParserCreate()
|
||||
# Choose a threshold expected to be never reached.
|
||||
self.set_activation_threshold(parser, pow(10, 5))
|
||||
# Check that the threshold is reached by choosing a small factor
|
||||
# and a payload whose peak amplification factor exceeds it.
|
||||
self.assertIsNone(self.set_maximum_amplification(parser, 1.0))
|
||||
payload = self.exponential_expansion_payload(ncols=10, nrows=4)
|
||||
self.assertIsNotNone(parser.Parse(payload, True))
|
||||
|
||||
def test_set_maximum_amplification__amplification_exceeded(self):
|
||||
parser = expat.ParserCreate()
|
||||
# Unconditionally enable maximum activation factor.
|
||||
self.set_activation_threshold(parser, 0)
|
||||
# Choose a max amplification factor expected to always be exceeded.
|
||||
self.assertIsNone(self.set_maximum_amplification(parser, 1.0))
|
||||
# Craft a payload for which the peak amplification factor is > 1.0.
|
||||
payload = self.exponential_expansion_payload(ncols=1, nrows=2)
|
||||
self.assert_rejected(parser.Parse, payload, True)
|
||||
|
||||
def test_set_maximum_amplification__amplification_not_exceeded(self):
|
||||
parser = expat.ParserCreate()
|
||||
# Unconditionally enable maximum activation factor.
|
||||
self.set_activation_threshold(parser, 0)
|
||||
# Choose a max amplification factor expected to never be exceeded.
|
||||
self.assertIsNone(self.set_maximum_amplification(parser, 1e4))
|
||||
# Craft a payload for which the peak amplification factor is < 1e4.
|
||||
payload = self.exponential_expansion_payload(ncols=1, nrows=2)
|
||||
self.assertIsNotNone(parser.Parse(payload, True))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
1577
Lib/test/test_sax.py
vendored
Normal file
1577
Lib/test/test_sax.py
vendored
Normal file
File diff suppressed because it is too large
Load Diff
635
Lib/test/test_xml_etree.py
vendored
635
Lib/test/test_xml_etree.py
vendored
@@ -155,9 +155,9 @@ class ModuleTest(unittest.TestCase):
|
||||
def test_sanity(self):
|
||||
# Import sanity.
|
||||
|
||||
from xml.etree import ElementTree
|
||||
from xml.etree import ElementInclude
|
||||
from xml.etree import ElementPath
|
||||
from xml.etree import ElementTree # noqa: F401
|
||||
from xml.etree import ElementInclude # noqa: F401
|
||||
from xml.etree import ElementPath # noqa: F401
|
||||
|
||||
def test_all(self):
|
||||
names = ("xml.etree.ElementTree", "_elementtree")
|
||||
@@ -252,8 +252,7 @@ class ElementTreeTest(unittest.TestCase):
|
||||
self.assertTrue(ET.iselement(element), msg="not an element")
|
||||
direlem = dir(element)
|
||||
for attr in 'tag', 'attrib', 'text', 'tail':
|
||||
self.assertTrue(hasattr(element, attr),
|
||||
msg='no %s member' % attr)
|
||||
self.assertHasAttr(element, attr)
|
||||
self.assertIn(attr, direlem,
|
||||
msg='no %s visible by dir' % attr)
|
||||
|
||||
@@ -278,7 +277,7 @@ class ElementTreeTest(unittest.TestCase):
|
||||
# Make sure all standard element methods exist.
|
||||
|
||||
def check_method(method):
|
||||
self.assertTrue(hasattr(method, '__call__'),
|
||||
self.assertHasAttr(method, '__call__',
|
||||
msg="%s not callable" % method)
|
||||
|
||||
check_method(element.append)
|
||||
@@ -339,7 +338,7 @@ class ElementTreeTest(unittest.TestCase):
|
||||
element.attrib = {'A': 'B', 'C': 'D'}
|
||||
self.assertEqual(element.attrib, {'A': 'B', 'C': 'D'})
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_simpleops(self):
|
||||
# Basic method sanity checks.
|
||||
|
||||
@@ -372,9 +371,9 @@ class ElementTreeTest(unittest.TestCase):
|
||||
self.serialize_check(element, '<tag key="value"><subtag /></tag>') # 4
|
||||
element.remove(subelement)
|
||||
self.serialize_check(element, '<tag key="value" />') # 5
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
with self.assertRaisesRegex(ValueError,
|
||||
r'Element\.remove\(.+\): element not found'):
|
||||
element.remove(subelement)
|
||||
self.assertEqual(str(cm.exception), 'list.remove(x): x not in list')
|
||||
self.serialize_check(element, '<tag key="value" />') # 6
|
||||
element[0:0] = [subelement, subelement, subelement]
|
||||
self.serialize_check(element[1], '<subtag />')
|
||||
@@ -394,7 +393,7 @@ class ElementTreeTest(unittest.TestCase):
|
||||
self.serialize_check(ET.XML("<tag><![CDATA[hello]]></tag>"),
|
||||
'<tag>hello</tag>')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_file_init(self):
|
||||
stringfile = io.BytesIO(SAMPLE_XML.encode("utf-8"))
|
||||
tree = ET.ElementTree(file=stringfile)
|
||||
@@ -510,7 +509,7 @@ class ElementTreeTest(unittest.TestCase):
|
||||
elem[:] = tuple([subelem])
|
||||
self.serialize_check(elem, '<tag><subtag key="value" /></tag>')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_parsefile(self):
|
||||
# Test parsing from file.
|
||||
|
||||
@@ -556,7 +555,7 @@ class ElementTreeTest(unittest.TestCase):
|
||||
' <empty-element />\n'
|
||||
'</root>')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_parseliteral(self):
|
||||
element = ET.XML("<html><body>text</body></html>")
|
||||
self.assertEqual(ET.tostring(element, encoding='unicode'),
|
||||
@@ -579,210 +578,6 @@ class ElementTreeTest(unittest.TestCase):
|
||||
self.assertEqual(len(ids), 1)
|
||||
self.assertEqual(ids["body"].tag, 'body')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_iterparse(self):
|
||||
# Test iterparse interface.
|
||||
|
||||
iterparse = ET.iterparse
|
||||
|
||||
context = iterparse(SIMPLE_XMLFILE)
|
||||
self.assertIsNone(context.root)
|
||||
action, elem = next(context)
|
||||
self.assertIsNone(context.root)
|
||||
self.assertEqual((action, elem.tag), ('end', 'element'))
|
||||
self.assertEqual([(action, elem.tag) for action, elem in context], [
|
||||
('end', 'element'),
|
||||
('end', 'empty-element'),
|
||||
('end', 'root'),
|
||||
])
|
||||
self.assertEqual(context.root.tag, 'root')
|
||||
|
||||
context = iterparse(SIMPLE_NS_XMLFILE)
|
||||
self.assertEqual([(action, elem.tag) for action, elem in context], [
|
||||
('end', '{namespace}element'),
|
||||
('end', '{namespace}element'),
|
||||
('end', '{namespace}empty-element'),
|
||||
('end', '{namespace}root'),
|
||||
])
|
||||
|
||||
with open(SIMPLE_XMLFILE, 'rb') as source:
|
||||
context = iterparse(source)
|
||||
action, elem = next(context)
|
||||
self.assertEqual((action, elem.tag), ('end', 'element'))
|
||||
self.assertEqual([(action, elem.tag) for action, elem in context], [
|
||||
('end', 'element'),
|
||||
('end', 'empty-element'),
|
||||
('end', 'root'),
|
||||
])
|
||||
self.assertEqual(context.root.tag, 'root')
|
||||
|
||||
events = ()
|
||||
context = iterparse(SIMPLE_XMLFILE, events)
|
||||
self.assertEqual([(action, elem.tag) for action, elem in context], [])
|
||||
|
||||
events = ()
|
||||
context = iterparse(SIMPLE_XMLFILE, events=events)
|
||||
self.assertEqual([(action, elem.tag) for action, elem in context], [])
|
||||
|
||||
events = ("start", "end")
|
||||
context = iterparse(SIMPLE_XMLFILE, events)
|
||||
self.assertEqual([(action, elem.tag) for action, elem in context], [
|
||||
('start', 'root'),
|
||||
('start', 'element'),
|
||||
('end', 'element'),
|
||||
('start', 'element'),
|
||||
('end', 'element'),
|
||||
('start', 'empty-element'),
|
||||
('end', 'empty-element'),
|
||||
('end', 'root'),
|
||||
])
|
||||
|
||||
events = ("start", "end", "start-ns", "end-ns")
|
||||
context = iterparse(SIMPLE_NS_XMLFILE, events)
|
||||
self.assertEqual([(action, elem.tag) if action in ("start", "end")
|
||||
else (action, elem)
|
||||
for action, elem in context], [
|
||||
('start-ns', ('', 'namespace')),
|
||||
('start', '{namespace}root'),
|
||||
('start', '{namespace}element'),
|
||||
('end', '{namespace}element'),
|
||||
('start', '{namespace}element'),
|
||||
('end', '{namespace}element'),
|
||||
('start', '{namespace}empty-element'),
|
||||
('end', '{namespace}empty-element'),
|
||||
('end', '{namespace}root'),
|
||||
('end-ns', None),
|
||||
])
|
||||
|
||||
events = ('start-ns', 'end-ns')
|
||||
context = iterparse(io.StringIO(r"<root xmlns=''/>"), events)
|
||||
res = [action for action, elem in context]
|
||||
self.assertEqual(res, ['start-ns', 'end-ns'])
|
||||
|
||||
events = ("start", "end", "bogus")
|
||||
with open(SIMPLE_XMLFILE, "rb") as f:
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
iterparse(f, events)
|
||||
self.assertFalse(f.closed)
|
||||
self.assertEqual(str(cm.exception), "unknown event 'bogus'")
|
||||
|
||||
with warnings_helper.check_no_resource_warning(self):
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
iterparse(SIMPLE_XMLFILE, events)
|
||||
self.assertEqual(str(cm.exception), "unknown event 'bogus'")
|
||||
del cm
|
||||
|
||||
source = io.BytesIO(
|
||||
b"<?xml version='1.0' encoding='iso-8859-1'?>\n"
|
||||
b"<body xmlns='http://éffbot.org/ns'\n"
|
||||
b" xmlns:cl\xe9='http://effbot.org/ns'>text</body>\n")
|
||||
events = ("start-ns",)
|
||||
context = iterparse(source, events)
|
||||
self.assertEqual([(action, elem) for action, elem in context], [
|
||||
('start-ns', ('', 'http://\xe9ffbot.org/ns')),
|
||||
('start-ns', ('cl\xe9', 'http://effbot.org/ns')),
|
||||
])
|
||||
|
||||
source = io.StringIO("<document />junk")
|
||||
it = iterparse(source)
|
||||
action, elem = next(it)
|
||||
self.assertEqual((action, elem.tag), ('end', 'document'))
|
||||
with self.assertRaises(ET.ParseError) as cm:
|
||||
next(it)
|
||||
self.assertEqual(str(cm.exception),
|
||||
'junk after document element: line 1, column 12')
|
||||
|
||||
self.addCleanup(os_helper.unlink, TESTFN)
|
||||
with open(TESTFN, "wb") as f:
|
||||
f.write(b"<document />junk")
|
||||
it = iterparse(TESTFN)
|
||||
action, elem = next(it)
|
||||
self.assertEqual((action, elem.tag), ('end', 'document'))
|
||||
with warnings_helper.check_no_resource_warning(self):
|
||||
with self.assertRaises(ET.ParseError) as cm:
|
||||
next(it)
|
||||
self.assertEqual(str(cm.exception),
|
||||
'junk after document element: line 1, column 12')
|
||||
del cm, it
|
||||
|
||||
# Not exhausting the iterator still closes the resource (bpo-43292)
|
||||
with warnings_helper.check_no_resource_warning(self):
|
||||
it = iterparse(SIMPLE_XMLFILE)
|
||||
del it
|
||||
|
||||
with warnings_helper.check_no_resource_warning(self):
|
||||
it = iterparse(SIMPLE_XMLFILE)
|
||||
it.close()
|
||||
del it
|
||||
|
||||
with warnings_helper.check_no_resource_warning(self):
|
||||
it = iterparse(SIMPLE_XMLFILE)
|
||||
action, elem = next(it)
|
||||
self.assertEqual((action, elem.tag), ('end', 'element'))
|
||||
del it, elem
|
||||
|
||||
with warnings_helper.check_no_resource_warning(self):
|
||||
it = iterparse(SIMPLE_XMLFILE)
|
||||
action, elem = next(it)
|
||||
it.close()
|
||||
self.assertEqual((action, elem.tag), ('end', 'element'))
|
||||
del it, elem
|
||||
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
iterparse("nonexistent")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_iterparse_close(self):
|
||||
iterparse = ET.iterparse
|
||||
|
||||
it = iterparse(SIMPLE_XMLFILE)
|
||||
it.close()
|
||||
with self.assertRaises(StopIteration):
|
||||
next(it)
|
||||
it.close() # idempotent
|
||||
|
||||
with open(SIMPLE_XMLFILE, 'rb') as source:
|
||||
it = iterparse(source)
|
||||
it.close()
|
||||
self.assertFalse(source.closed)
|
||||
with self.assertRaises(StopIteration):
|
||||
next(it)
|
||||
it.close() # idempotent
|
||||
|
||||
it = iterparse(SIMPLE_XMLFILE)
|
||||
action, elem = next(it)
|
||||
self.assertEqual((action, elem.tag), ('end', 'element'))
|
||||
it.close()
|
||||
with self.assertRaises(StopIteration):
|
||||
next(it)
|
||||
it.close() # idempotent
|
||||
|
||||
with open(SIMPLE_XMLFILE, 'rb') as source:
|
||||
it = iterparse(source)
|
||||
action, elem = next(it)
|
||||
self.assertEqual((action, elem.tag), ('end', 'element'))
|
||||
it.close()
|
||||
self.assertFalse(source.closed)
|
||||
with self.assertRaises(StopIteration):
|
||||
next(it)
|
||||
it.close() # idempotent
|
||||
|
||||
it = iterparse(SIMPLE_XMLFILE)
|
||||
list(it)
|
||||
it.close()
|
||||
with self.assertRaises(StopIteration):
|
||||
next(it)
|
||||
it.close() # idempotent
|
||||
|
||||
with open(SIMPLE_XMLFILE, 'rb') as source:
|
||||
it = iterparse(source)
|
||||
list(it)
|
||||
it.close()
|
||||
self.assertFalse(source.closed)
|
||||
with self.assertRaises(StopIteration):
|
||||
next(it)
|
||||
it.close() # idempotent
|
||||
|
||||
def test_writefile(self):
|
||||
elem = ET.Element("tag")
|
||||
elem.text = "text"
|
||||
@@ -800,7 +595,7 @@ class ElementTreeTest(unittest.TestCase):
|
||||
elem[0] = ET.PI("key", "value")
|
||||
self.serialize_check(elem, 'text<?key value?><subtag>subtext</subtag>')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_custom_builder(self):
|
||||
# Test parser w. custom builder.
|
||||
|
||||
@@ -862,7 +657,7 @@ class ElementTreeTest(unittest.TestCase):
|
||||
('end-ns', ''),
|
||||
])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_custom_builder_only_end_ns(self):
|
||||
class Builder(list):
|
||||
def end_ns(self, prefix):
|
||||
@@ -894,7 +689,7 @@ class ElementTreeTest(unittest.TestCase):
|
||||
parser2 = ET.XMLParser()
|
||||
self.assertIsInstance(parser2.target, ET.TreeBuilder)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_children(self):
|
||||
# Test Element children iteration
|
||||
|
||||
@@ -1174,7 +969,7 @@ class ElementTreeTest(unittest.TestCase):
|
||||
self.assertRegex(stringlist[0], r"^<\?xml version='1.0' encoding='.+'?>")
|
||||
self.assertEqual(['<body', '>', '<tag', ' />', '</body>'], stringlist[1:])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_encoding(self):
|
||||
def check(encoding, body=''):
|
||||
xml = ("<?xml version='1.0' encoding='%s'?><xml>%s</xml>" %
|
||||
@@ -1256,7 +1051,7 @@ class ElementTreeTest(unittest.TestCase):
|
||||
self.assertEqual(serialize(e, method="html"),
|
||||
'<html><CamelCase>text</CamelCase></html>')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_entity(self):
|
||||
# Test entity handling.
|
||||
|
||||
@@ -1294,7 +1089,7 @@ class ElementTreeTest(unittest.TestCase):
|
||||
self.assertEqual(str(cm.exception),
|
||||
'undefined entity &entity;: line 4, column 10')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_namespace(self):
|
||||
# Test namespace issues.
|
||||
|
||||
@@ -1505,13 +1300,249 @@ class ElementTreeTest(unittest.TestCase):
|
||||
self.assertEqual(serialize(root, method='html'),
|
||||
'<cirriculum status="public" company="example"></cirriculum>')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_attlist_default(self):
|
||||
# Test default attribute values; See BPO 42151.
|
||||
root = ET.fromstring(ATTLIST_XML)
|
||||
self.assertEqual(root[0].attrib,
|
||||
{'{http://www.w3.org/XML/1998/namespace}lang': 'eng'})
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_iterparse(self):
|
||||
return super().test_iterparse()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_iterparse_close(self):
|
||||
return super().test_iterparse_close()
|
||||
|
||||
|
||||
class IterparseTest(unittest.TestCase):
|
||||
# Test iterparse interface.
|
||||
|
||||
def test_basic(self):
|
||||
iterparse = ET.iterparse
|
||||
|
||||
it = iterparse(SIMPLE_XMLFILE)
|
||||
self.assertIsNone(it.root)
|
||||
action, elem = next(it)
|
||||
self.assertIsNone(it.root)
|
||||
self.assertEqual((action, elem.tag), ('end', 'element'))
|
||||
self.assertEqual([(action, elem.tag) for action, elem in it], [
|
||||
('end', 'element'),
|
||||
('end', 'empty-element'),
|
||||
('end', 'root'),
|
||||
])
|
||||
self.assertEqual(it.root.tag, 'root')
|
||||
it.close()
|
||||
|
||||
it = iterparse(SIMPLE_NS_XMLFILE)
|
||||
self.assertEqual([(action, elem.tag) for action, elem in it], [
|
||||
('end', '{namespace}element'),
|
||||
('end', '{namespace}element'),
|
||||
('end', '{namespace}empty-element'),
|
||||
('end', '{namespace}root'),
|
||||
])
|
||||
it.close()
|
||||
|
||||
def test_external_file(self):
|
||||
with open(SIMPLE_XMLFILE, 'rb') as source:
|
||||
it = ET.iterparse(source)
|
||||
action, elem = next(it)
|
||||
self.assertEqual((action, elem.tag), ('end', 'element'))
|
||||
self.assertEqual([(action, elem.tag) for action, elem in it], [
|
||||
('end', 'element'),
|
||||
('end', 'empty-element'),
|
||||
('end', 'root'),
|
||||
])
|
||||
self.assertEqual(it.root.tag, 'root')
|
||||
|
||||
def test_events(self):
|
||||
iterparse = ET.iterparse
|
||||
|
||||
events = ()
|
||||
it = iterparse(SIMPLE_XMLFILE, events)
|
||||
self.assertEqual([(action, elem.tag) for action, elem in it], [])
|
||||
it.close()
|
||||
|
||||
events = ()
|
||||
it = iterparse(SIMPLE_XMLFILE, events=events)
|
||||
self.assertEqual([(action, elem.tag) for action, elem in it], [])
|
||||
it.close()
|
||||
|
||||
events = ("start", "end")
|
||||
it = iterparse(SIMPLE_XMLFILE, events)
|
||||
self.assertEqual([(action, elem.tag) for action, elem in it], [
|
||||
('start', 'root'),
|
||||
('start', 'element'),
|
||||
('end', 'element'),
|
||||
('start', 'element'),
|
||||
('end', 'element'),
|
||||
('start', 'empty-element'),
|
||||
('end', 'empty-element'),
|
||||
('end', 'root'),
|
||||
])
|
||||
it.close()
|
||||
|
||||
def test_namespace_events(self):
|
||||
iterparse = ET.iterparse
|
||||
|
||||
events = ("start", "end", "start-ns", "end-ns")
|
||||
it = iterparse(SIMPLE_NS_XMLFILE, events)
|
||||
self.assertEqual([(action, elem.tag) if action in ("start", "end")
|
||||
else (action, elem)
|
||||
for action, elem in it], [
|
||||
('start-ns', ('', 'namespace')),
|
||||
('start', '{namespace}root'),
|
||||
('start', '{namespace}element'),
|
||||
('end', '{namespace}element'),
|
||||
('start', '{namespace}element'),
|
||||
('end', '{namespace}element'),
|
||||
('start', '{namespace}empty-element'),
|
||||
('end', '{namespace}empty-element'),
|
||||
('end', '{namespace}root'),
|
||||
('end-ns', None),
|
||||
])
|
||||
it.close()
|
||||
|
||||
events = ('start-ns', 'end-ns')
|
||||
it = iterparse(io.BytesIO(br"<root xmlns=''/>"), events)
|
||||
res = [action for action, elem in it]
|
||||
self.assertEqual(res, ['start-ns', 'end-ns'])
|
||||
it.close()
|
||||
|
||||
def test_unknown_events(self):
|
||||
iterparse = ET.iterparse
|
||||
|
||||
events = ("start", "end", "bogus")
|
||||
with open(SIMPLE_XMLFILE, "rb") as f:
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
iterparse(f, events)
|
||||
self.assertFalse(f.closed)
|
||||
self.assertEqual(str(cm.exception), "unknown event 'bogus'")
|
||||
|
||||
with warnings_helper.check_no_resource_warning(self):
|
||||
with self.assertRaises(ValueError) as cm:
|
||||
iterparse(SIMPLE_XMLFILE, events)
|
||||
self.assertEqual(str(cm.exception), "unknown event 'bogus'")
|
||||
del cm
|
||||
gc_collect()
|
||||
|
||||
def test_non_utf8(self):
|
||||
source = io.BytesIO(
|
||||
b"<?xml version='1.0' encoding='iso-8859-1'?>\n"
|
||||
b"<body xmlns='http://éffbot.org/ns'\n"
|
||||
b" xmlns:cl\xe9='http://effbot.org/ns'>text</body>\n")
|
||||
events = ("start-ns",)
|
||||
it = ET.iterparse(source, events)
|
||||
self.assertEqual([(action, elem) for action, elem in it], [
|
||||
('start-ns', ('', 'http://\xe9ffbot.org/ns')),
|
||||
('start-ns', ('cl\xe9', 'http://effbot.org/ns')),
|
||||
])
|
||||
|
||||
def test_parsing_error(self):
|
||||
source = io.BytesIO(b"<document />junk")
|
||||
it = ET.iterparse(source)
|
||||
action, elem = next(it)
|
||||
self.assertEqual((action, elem.tag), ('end', 'document'))
|
||||
with self.assertRaises(ET.ParseError) as cm:
|
||||
next(it)
|
||||
self.assertEqual(str(cm.exception),
|
||||
'junk after document element: line 1, column 12')
|
||||
|
||||
def test_nonexistent_file(self):
|
||||
with self.assertRaises(FileNotFoundError):
|
||||
ET.iterparse("nonexistent")
|
||||
|
||||
def test_resource_warnings_not_exhausted(self):
|
||||
# Not exhausting the iterator still closes the underlying file (bpo-43292)
|
||||
it = ET.iterparse(SIMPLE_XMLFILE)
|
||||
with warnings_helper.check_no_resource_warning(self):
|
||||
del it
|
||||
gc_collect()
|
||||
|
||||
it = ET.iterparse(SIMPLE_XMLFILE)
|
||||
with warnings_helper.check_no_resource_warning(self):
|
||||
action, elem = next(it)
|
||||
self.assertEqual((action, elem.tag), ('end', 'element'))
|
||||
del it, elem
|
||||
gc_collect()
|
||||
|
||||
def test_resource_warnings_failed_iteration(self):
|
||||
self.addCleanup(os_helper.unlink, TESTFN)
|
||||
with open(TESTFN, "wb") as f:
|
||||
f.write(b"<document />junk")
|
||||
|
||||
it = ET.iterparse(TESTFN)
|
||||
action, elem = next(it)
|
||||
self.assertEqual((action, elem.tag), ('end', 'document'))
|
||||
with warnings_helper.check_no_resource_warning(self):
|
||||
with self.assertRaises(ET.ParseError) as cm:
|
||||
next(it)
|
||||
self.assertEqual(str(cm.exception),
|
||||
'junk after document element: line 1, column 12')
|
||||
del cm, it
|
||||
gc_collect()
|
||||
|
||||
def test_resource_warnings_exhausted(self):
|
||||
it = ET.iterparse(SIMPLE_XMLFILE)
|
||||
with warnings_helper.check_no_resource_warning(self):
|
||||
list(it)
|
||||
del it
|
||||
gc_collect()
|
||||
|
||||
def test_close_not_exhausted(self):
|
||||
iterparse = ET.iterparse
|
||||
|
||||
it = iterparse(SIMPLE_XMLFILE)
|
||||
it.close()
|
||||
with self.assertRaises(StopIteration):
|
||||
next(it)
|
||||
it.close() # idempotent
|
||||
|
||||
with open(SIMPLE_XMLFILE, 'rb') as source:
|
||||
it = iterparse(source)
|
||||
it.close()
|
||||
self.assertFalse(source.closed)
|
||||
with self.assertRaises(StopIteration):
|
||||
next(it)
|
||||
it.close() # idempotent
|
||||
|
||||
it = iterparse(SIMPLE_XMLFILE)
|
||||
action, elem = next(it)
|
||||
self.assertEqual((action, elem.tag), ('end', 'element'))
|
||||
it.close()
|
||||
with self.assertRaises(StopIteration):
|
||||
next(it)
|
||||
it.close() # idempotent
|
||||
|
||||
with open(SIMPLE_XMLFILE, 'rb') as source:
|
||||
it = iterparse(source)
|
||||
action, elem = next(it)
|
||||
self.assertEqual((action, elem.tag), ('end', 'element'))
|
||||
it.close()
|
||||
self.assertFalse(source.closed)
|
||||
with self.assertRaises(StopIteration):
|
||||
next(it)
|
||||
it.close() # idempotent
|
||||
|
||||
def test_close_exhausted(self):
|
||||
iterparse = ET.iterparse
|
||||
it = iterparse(SIMPLE_XMLFILE)
|
||||
list(it)
|
||||
it.close()
|
||||
with self.assertRaises(StopIteration):
|
||||
next(it)
|
||||
it.close() # idempotent
|
||||
|
||||
with open(SIMPLE_XMLFILE, 'rb') as source:
|
||||
it = iterparse(source)
|
||||
list(it)
|
||||
it.close()
|
||||
self.assertFalse(source.closed)
|
||||
with self.assertRaises(StopIteration):
|
||||
next(it)
|
||||
it.close() # idempotent
|
||||
|
||||
|
||||
class XMLPullParserTest(unittest.TestCase):
|
||||
|
||||
@@ -1540,7 +1571,7 @@ class XMLPullParserTest(unittest.TestCase):
|
||||
self.assertEqual([(action, elem.tag) for action, elem in events],
|
||||
expected)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_simple_xml(self, chunk_size=None, flush=False):
|
||||
parser = ET.XMLPullParser()
|
||||
self.assert_event_tags(parser, [])
|
||||
@@ -1562,19 +1593,19 @@ class XMLPullParserTest(unittest.TestCase):
|
||||
self.assert_event_tags(parser, [('end', 'root')])
|
||||
self.assertIsNone(parser.close())
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_simple_xml_chunk_1(self):
|
||||
self.test_simple_xml(chunk_size=1, flush=True)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_simple_xml_chunk_5(self):
|
||||
self.test_simple_xml(chunk_size=5, flush=True)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_simple_xml_chunk_22(self):
|
||||
self.test_simple_xml(chunk_size=22)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_feed_while_iterating(self):
|
||||
parser = ET.XMLPullParser()
|
||||
it = parser.read_events()
|
||||
@@ -1587,7 +1618,7 @@ class XMLPullParserTest(unittest.TestCase):
|
||||
with self.assertRaises(StopIteration):
|
||||
next(it)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_simple_xml_with_ns(self):
|
||||
parser = ET.XMLPullParser()
|
||||
self.assert_event_tags(parser, [])
|
||||
@@ -1609,7 +1640,7 @@ class XMLPullParserTest(unittest.TestCase):
|
||||
self.assert_event_tags(parser, [('end', '{namespace}root')])
|
||||
self.assertIsNone(parser.close())
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_ns_events(self):
|
||||
parser = ET.XMLPullParser(events=('start-ns', 'end-ns'))
|
||||
self._feed(parser, "<!-- comment -->\n")
|
||||
@@ -1625,7 +1656,7 @@ class XMLPullParserTest(unittest.TestCase):
|
||||
self.assertEqual(list(parser.read_events()), [('end-ns', None)])
|
||||
self.assertIsNone(parser.close())
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_ns_events_start(self):
|
||||
parser = ET.XMLPullParser(events=('start-ns', 'start', 'end'))
|
||||
self._feed(parser, "<tag xmlns='abc' xmlns:p='xyz'>\n")
|
||||
@@ -1649,7 +1680,7 @@ class XMLPullParserTest(unittest.TestCase):
|
||||
('end', '{abc}tag'),
|
||||
])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_ns_events_start_end(self):
|
||||
parser = ET.XMLPullParser(events=('start-ns', 'start', 'end', 'end-ns'))
|
||||
self._feed(parser, "<tag xmlns='abc' xmlns:p='xyz'>\n")
|
||||
@@ -1677,7 +1708,7 @@ class XMLPullParserTest(unittest.TestCase):
|
||||
('end-ns', None),
|
||||
])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_events(self):
|
||||
parser = ET.XMLPullParser(events=())
|
||||
self._feed(parser, "<root/>\n")
|
||||
@@ -1724,7 +1755,7 @@ class XMLPullParserTest(unittest.TestCase):
|
||||
self._feed(parser, "</root>")
|
||||
self.assertIsNone(parser.close())
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_events_comment(self):
|
||||
parser = ET.XMLPullParser(events=('start', 'comment', 'end'))
|
||||
self._feed(parser, "<!-- text here -->\n")
|
||||
@@ -1744,7 +1775,7 @@ class XMLPullParserTest(unittest.TestCase):
|
||||
self._feed(parser, "<!-- text here -->\n")
|
||||
self.assert_events(parser, [('comment', (ET.Comment, ' text here '))])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_events_pi(self):
|
||||
parser = ET.XMLPullParser(events=('start', 'pi', 'end'))
|
||||
self._feed(parser, "<?pitarget?>\n")
|
||||
@@ -1775,8 +1806,10 @@ class XMLPullParserTest(unittest.TestCase):
|
||||
def test_unknown_event(self):
|
||||
with self.assertRaises(ValueError):
|
||||
ET.XMLPullParser(events=('start', 'end', 'bogus'))
|
||||
with self.assertRaisesRegex(ValueError, "unknown event 'bogus'"):
|
||||
ET.XMLPullParser(events=(x.decode() for x in (b'start', b'end', b'bogus')))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.skipIf(pyexpat.version_info < (2, 6, 0),
|
||||
f'Expat {pyexpat.version_info} does not '
|
||||
'support reparse deferral')
|
||||
@@ -1801,7 +1834,7 @@ class XMLPullParserTest(unittest.TestCase):
|
||||
|
||||
self.assert_event_tags(parser, [('end', 'doc')])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_flush_reparse_deferral_disabled(self):
|
||||
parser = ET.XMLPullParser(events=('start', 'end'))
|
||||
|
||||
@@ -1984,7 +2017,7 @@ class XIncludeTest(unittest.TestCase):
|
||||
else:
|
||||
return None
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_xinclude_default(self):
|
||||
from xml.etree import ElementInclude
|
||||
doc = self.xinclude_loader('default.xml')
|
||||
@@ -1999,7 +2032,7 @@ class XIncludeTest(unittest.TestCase):
|
||||
'</root>\n'
|
||||
'</document>')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_xinclude(self):
|
||||
from xml.etree import ElementInclude
|
||||
|
||||
@@ -2064,7 +2097,7 @@ class XIncludeTest(unittest.TestCase):
|
||||
' </ns0:include>\n'
|
||||
'</div>') # C5
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_xinclude_repeated(self):
|
||||
from xml.etree import ElementInclude
|
||||
|
||||
@@ -2072,7 +2105,7 @@ class XIncludeTest(unittest.TestCase):
|
||||
ElementInclude.include(document, self.xinclude_loader)
|
||||
self.assertEqual(1+4*2, len(document.findall(".//p")))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_xinclude_failures(self):
|
||||
from xml.etree import ElementInclude
|
||||
|
||||
@@ -2177,7 +2210,7 @@ class BugsTest(unittest.TestCase):
|
||||
elem.set("123", 123)
|
||||
check(elem) # attribute value
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_bug_xmltoolkit25(self):
|
||||
# typo in ElementTree.findtext
|
||||
|
||||
@@ -2201,7 +2234,7 @@ class BugsTest(unittest.TestCase):
|
||||
ET.dump(tree)
|
||||
self.assertEqual(stdout.getvalue(), '<doc><table><tbody /></table></doc>\n')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_bug_xmltoolkit39(self):
|
||||
# non-ascii element and attribute names doesn't work
|
||||
|
||||
@@ -2236,7 +2269,7 @@ class BugsTest(unittest.TestCase):
|
||||
b'<doc>舰</doc>')
|
||||
self.assertEqual(serialize(e), '<doc>\u8230</doc>')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_bug_xmltoolkit55(self):
|
||||
# make sure we're reporting the first error, not the last
|
||||
|
||||
@@ -2255,7 +2288,7 @@ class BugsTest(unittest.TestCase):
|
||||
|
||||
self.assertRaises(OSError, ET.parse, ExceptionFile())
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_bug_xmltoolkit62(self):
|
||||
# Don't crash when using custom entities.
|
||||
|
||||
@@ -2288,7 +2321,7 @@ class BugsTest(unittest.TestCase):
|
||||
xmltoolkit63()
|
||||
self.assertEqual(sys.getrefcount(None), count)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_bug_200708_newline(self):
|
||||
# Preserve newlines in attributes.
|
||||
|
||||
@@ -2404,7 +2437,7 @@ class BugsTest(unittest.TestCase):
|
||||
b"<?xml version='1.0' encoding='ascii'?>\n"
|
||||
b'<body>tãg</body>')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_issue6565(self):
|
||||
elem = ET.XML("<body><tag/></body>")
|
||||
self.assertEqual(summarize_list(elem), ['tag'])
|
||||
@@ -2450,7 +2483,7 @@ class BugsTest(unittest.TestCase):
|
||||
self.assertIsInstance(e[0].tail, str)
|
||||
self.assertEqual(e[0].tail, 'changed')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_lost_elem(self):
|
||||
# Issue #25902: Borrowed element can disappear
|
||||
class Tag:
|
||||
@@ -2476,7 +2509,7 @@ class BugsTest(unittest.TestCase):
|
||||
root = ET.XML(xml)
|
||||
self.assertEqual(root.get('b'), text.decode('utf-8'))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_expat224_utf8_bug(self):
|
||||
# bpo-31170: Expat 2.2.3 had a bug in its UTF-8 decoder.
|
||||
# Check that Expat 2.2.4 fixed the bug.
|
||||
@@ -2489,7 +2522,7 @@ class BugsTest(unittest.TestCase):
|
||||
text = b'x' + b'\xc3\xa0' * 1024
|
||||
self.check_expat224_utf8_bug(text)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_expat224_utf8_bug_file(self):
|
||||
with open(UTF8_BUG_XMLFILE, 'rb') as fp:
|
||||
raw = fp.read()
|
||||
@@ -2639,7 +2672,7 @@ class BasicElementTest(ElementTestCase, unittest.TestCase):
|
||||
e[:] = [E('bar')]
|
||||
self.assertRaises(TypeError, copy.deepcopy, e)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_cyclic_gc(self):
|
||||
class Dummy:
|
||||
pass
|
||||
@@ -2977,32 +3010,72 @@ class BadElementTest(ElementTestCase, unittest.TestCase):
|
||||
elem = b.close()
|
||||
self.assertEqual(elem[0].tail, 'ABCDEFGHIJKL')
|
||||
|
||||
def test_subscr(self):
|
||||
# Issue #27863
|
||||
def test_subscr_with_clear(self):
|
||||
# See https://github.com/python/cpython/issues/143200.
|
||||
self.do_test_subscr_with_mutating_slice(use_clear_method=True)
|
||||
|
||||
def test_subscr_with_delete(self):
|
||||
# See https://github.com/python/cpython/issues/72050.
|
||||
self.do_test_subscr_with_mutating_slice(use_clear_method=False)
|
||||
|
||||
def do_test_subscr_with_mutating_slice(self, *, use_clear_method):
|
||||
class X:
|
||||
def __init__(self, i=0):
|
||||
self.i = i
|
||||
def __index__(self):
|
||||
del e[:]
|
||||
return 1
|
||||
if use_clear_method:
|
||||
e.clear()
|
||||
else:
|
||||
del e[:]
|
||||
return self.i
|
||||
|
||||
e = ET.Element('elem')
|
||||
e.append(ET.Element('child'))
|
||||
e[:X()] # shouldn't crash
|
||||
for s in self.get_mutating_slices(X, 10):
|
||||
with self.subTest(s):
|
||||
e = ET.Element('elem')
|
||||
e.extend([ET.Element(f'c{i}') for i in range(10)])
|
||||
e[s] # shouldn't crash
|
||||
|
||||
e.append(ET.Element('child'))
|
||||
e[0:10:X()] # shouldn't crash
|
||||
def test_ass_subscr_with_mutating_slice(self):
|
||||
# See https://github.com/python/cpython/issues/72050
|
||||
# and https://github.com/python/cpython/issues/143200.
|
||||
|
||||
def test_ass_subscr(self):
|
||||
# Issue #27863
|
||||
class X:
|
||||
def __init__(self, i=0):
|
||||
self.i = i
|
||||
def __index__(self):
|
||||
e[:] = []
|
||||
return 1
|
||||
return self.i
|
||||
|
||||
for s in self.get_mutating_slices(X, 10):
|
||||
with self.subTest(s):
|
||||
e = ET.Element('elem')
|
||||
e.extend([ET.Element(f'c{i}') for i in range(10)])
|
||||
e[s] = [] # shouldn't crash
|
||||
|
||||
def get_mutating_slices(self, index_class, n_children):
|
||||
self.assertGreaterEqual(n_children, 10)
|
||||
return [
|
||||
slice(index_class(), None, None),
|
||||
slice(index_class(2), None, None),
|
||||
slice(None, index_class(), None),
|
||||
slice(None, index_class(2), None),
|
||||
slice(0, 2, index_class(1)),
|
||||
slice(0, 2, index_class(2)),
|
||||
slice(0, n_children, index_class(1)),
|
||||
slice(0, n_children, index_class(2)),
|
||||
slice(0, 2 * n_children, index_class(1)),
|
||||
slice(0, 2 * n_children, index_class(2)),
|
||||
]
|
||||
|
||||
def test_ass_subscr_with_mutating_iterable_value(self):
|
||||
class V:
|
||||
def __iter__(self):
|
||||
e.clear()
|
||||
return iter([ET.Element('a'), ET.Element('b')])
|
||||
|
||||
e = ET.Element('elem')
|
||||
for _ in range(10):
|
||||
e.insert(0, ET.Element('child'))
|
||||
|
||||
e[0:10:X()] = [] # shouldn't crash
|
||||
e.extend([ET.Element(f'c{i}') for i in range(10)])
|
||||
e[:] = V()
|
||||
|
||||
def test_treebuilder_start(self):
|
||||
# Issue #27863
|
||||
@@ -3235,7 +3308,7 @@ class ElementTreeTypeTest(unittest.TestCase):
|
||||
|
||||
|
||||
class ElementFindTest(unittest.TestCase):
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_find_simple(self):
|
||||
e = ET.XML(SAMPLE_XML)
|
||||
self.assertEqual(e.find('tag').tag, 'tag')
|
||||
@@ -3259,7 +3332,7 @@ class ElementFindTest(unittest.TestCase):
|
||||
# Issue #16922
|
||||
self.assertEqual(ET.XML('<tag><empty /></tag>').findtext('empty'), '')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_find_xpath(self):
|
||||
LINEAR_XML = '''
|
||||
<body>
|
||||
@@ -3282,7 +3355,7 @@ class ElementFindTest(unittest.TestCase):
|
||||
self.assertRaisesRegex(SyntaxError, 'XPath', e.find, './tag[last()-0]')
|
||||
self.assertRaisesRegex(SyntaxError, 'XPath', e.find, './tag[last()+1]')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_findall(self):
|
||||
e = ET.XML(SAMPLE_XML)
|
||||
e[2] = ET.XML(SAMPLE_SECTION)
|
||||
@@ -3471,7 +3544,7 @@ class ElementFindTest(unittest.TestCase):
|
||||
with self.assertRaisesRegex(SyntaxError, 'cannot use absolute path'):
|
||||
e.findall('/tag')
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_find_through_ElementTree(self):
|
||||
e = ET.XML(SAMPLE_XML)
|
||||
self.assertEqual(ET.ElementTree(e).find('tag').tag, 'tag')
|
||||
@@ -3674,7 +3747,7 @@ class TreeBuilderTest(unittest.TestCase):
|
||||
a = parser.close()
|
||||
self.assertEqual(a.text, "texttail")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_late_tail_mix_pi_comments(self):
|
||||
# Issue #37399: The tail of an ignored comment could overwrite the text before it.
|
||||
# Test appending tails to comments/pis.
|
||||
@@ -3787,7 +3860,7 @@ class TreeBuilderTest(unittest.TestCase):
|
||||
pass
|
||||
self._check_element_factory_class(MyElement)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_doctype(self):
|
||||
class DoctypeParser:
|
||||
_doctype = None
|
||||
@@ -3865,7 +3938,7 @@ class XMLParserTest(unittest.TestCase):
|
||||
parser.feed(self.sample2)
|
||||
parser.close()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_subclass_doctype(self):
|
||||
_doctype = None
|
||||
class MyParserWithDoctype(ET.XMLParser):
|
||||
@@ -3907,7 +3980,7 @@ class XMLParserTest(unittest.TestCase):
|
||||
parser.feed(self.sample2)
|
||||
parser.close()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_parse_string(self):
|
||||
parser = ET.XMLParser(target=ET.TreeBuilder())
|
||||
parser.feed(self.sample3)
|
||||
@@ -4365,13 +4438,13 @@ class ParseErrorTest(unittest.TestCase):
|
||||
except ET.ParseError as e:
|
||||
return e
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_error_position(self):
|
||||
self.assertEqual(self._get_error('foo').position, (1, 0))
|
||||
self.assertEqual(self._get_error('<tag>&foo;</tag>').position, (1, 5))
|
||||
self.assertEqual(self._get_error('foobar<').position, (1, 6))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_error_code(self):
|
||||
import xml.parsers.expat.errors as ERRORS
|
||||
self.assertEqual(self._get_error('foo').code,
|
||||
@@ -4431,7 +4504,7 @@ class NoAcceleratorTest(unittest.TestCase):
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
class BoolTest(unittest.TestCase):
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_warning(self):
|
||||
e = ET.fromstring('<a style="new"></a>')
|
||||
msg = (
|
||||
@@ -4461,7 +4534,7 @@ class C14NTest(unittest.TestCase):
|
||||
#
|
||||
# simple roundtrip tests (from c14n.py)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_simple_roundtrip(self):
|
||||
# Basics
|
||||
self.assertEqual(c14n_roundtrip("<doc/>"), '<doc></doc>')
|
||||
@@ -4502,7 +4575,7 @@ class C14NTest(unittest.TestCase):
|
||||
xml = '<X xmlns="http://nps/a"><Y xmlns:b="http://nsp/b" b:targets="abc,xyz"></Y></X>'
|
||||
self.assertEqual(c14n_roundtrip(xml), xml)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_c14n_exclusion(self):
|
||||
xml = textwrap.dedent("""\
|
||||
<root xmlns:x="http://example.com/x">
|
||||
@@ -4583,7 +4656,7 @@ class C14NTest(unittest.TestCase):
|
||||
# note that this uses generated C14N versions of the standard ET.write
|
||||
# output, not roundtripped C14N (see above).
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_xml_c14n2(self):
|
||||
datadir = findfile("c14n-20", subdir="xmltestdata")
|
||||
full_path = partial(os.path.join, datadir)
|
||||
|
||||
2
Lib/xml/dom/__init__.py
vendored
2
Lib/xml/dom/__init__.py
vendored
@@ -137,4 +137,4 @@ XHTML_NAMESPACE = "http://www.w3.org/1999/xhtml"
|
||||
EMPTY_NAMESPACE = None
|
||||
EMPTY_PREFIX = None
|
||||
|
||||
from .domreg import getDOMImplementation, registerDOMImplementation
|
||||
from .domreg import getDOMImplementation, registerDOMImplementation # noqa: F401
|
||||
|
||||
11
Lib/xml/dom/minidom.py
vendored
11
Lib/xml/dom/minidom.py
vendored
@@ -292,13 +292,6 @@ def _append_child(self, node):
|
||||
childNodes.append(node)
|
||||
node.parentNode = self
|
||||
|
||||
def _in_document(node):
|
||||
# return True iff node is part of a document tree
|
||||
while node is not None:
|
||||
if node.nodeType == Node.DOCUMENT_NODE:
|
||||
return True
|
||||
node = node.parentNode
|
||||
return False
|
||||
|
||||
def _write_data(writer, text, attr):
|
||||
"Writes datachars to writer."
|
||||
@@ -371,6 +364,7 @@ class Attr(Node):
|
||||
def __init__(self, qName, namespaceURI=EMPTY_NAMESPACE, localName=None,
|
||||
prefix=None):
|
||||
self.ownerElement = None
|
||||
self.ownerDocument = None
|
||||
self._name = qName
|
||||
self.namespaceURI = namespaceURI
|
||||
self._prefix = prefix
|
||||
@@ -696,6 +690,7 @@ class Element(Node):
|
||||
|
||||
def __init__(self, tagName, namespaceURI=EMPTY_NAMESPACE, prefix=None,
|
||||
localName=None):
|
||||
self.ownerDocument = None
|
||||
self.parentNode = None
|
||||
self.tagName = self.nodeName = tagName
|
||||
self.prefix = prefix
|
||||
@@ -1555,7 +1550,7 @@ def _clear_id_cache(node):
|
||||
if node.nodeType == Node.DOCUMENT_NODE:
|
||||
node._id_cache.clear()
|
||||
node._id_search_stack = None
|
||||
elif _in_document(node):
|
||||
elif node.ownerDocument:
|
||||
node.ownerDocument._id_cache.clear()
|
||||
node.ownerDocument._id_search_stack= None
|
||||
|
||||
|
||||
6
Lib/xml/etree/ElementTree.py
vendored
6
Lib/xml/etree/ElementTree.py
vendored
@@ -267,7 +267,11 @@ class Element:
|
||||
|
||||
"""
|
||||
# assert iselement(element)
|
||||
self._children.remove(subelement)
|
||||
try:
|
||||
self._children.remove(subelement)
|
||||
except ValueError:
|
||||
# to align the error message with the C implementation
|
||||
raise ValueError("Element.remove(x): element not found") from None
|
||||
|
||||
def find(self, path, namespaces=None):
|
||||
"""Find first matching element by tag name or path.
|
||||
|
||||
14
Lib/xml/sax/__init__.py
vendored
14
Lib/xml/sax/__init__.py
vendored
@@ -21,9 +21,9 @@ expatreader -- Driver that allows use of the Expat parser with SAX.
|
||||
|
||||
from .xmlreader import InputSource
|
||||
from .handler import ContentHandler, ErrorHandler
|
||||
from ._exceptions import SAXException, SAXNotRecognizedException, \
|
||||
SAXParseException, SAXNotSupportedException, \
|
||||
SAXReaderNotAvailable
|
||||
from ._exceptions import (SAXException, SAXNotRecognizedException,
|
||||
SAXParseException, SAXNotSupportedException,
|
||||
SAXReaderNotAvailable)
|
||||
|
||||
|
||||
def parse(source, handler, errorHandler=ErrorHandler()):
|
||||
@@ -55,7 +55,7 @@ default_parser_list = ["xml.sax.expatreader"]
|
||||
# tell modulefinder that importing sax potentially imports expatreader
|
||||
_false = 0
|
||||
if _false:
|
||||
import xml.sax.expatreader
|
||||
import xml.sax.expatreader # noqa: F401
|
||||
|
||||
import os, sys
|
||||
if not sys.flags.ignore_environment and "PY_SAX_PARSER" in os.environ:
|
||||
@@ -92,3 +92,9 @@ def make_parser(parser_list=()):
|
||||
def _create_parser(parser_name):
|
||||
drv_module = __import__(parser_name,{},{},['create_parser'])
|
||||
return drv_module.create_parser()
|
||||
|
||||
|
||||
__all__ = ['ContentHandler', 'ErrorHandler', 'InputSource', 'SAXException',
|
||||
'SAXNotRecognizedException', 'SAXNotSupportedException',
|
||||
'SAXParseException', 'SAXReaderNotAvailable',
|
||||
'default_parser_list', 'make_parser', 'parse', 'parseString']
|
||||
|
||||
2
Lib/xml/sax/handler.py
vendored
2
Lib/xml/sax/handler.py
vendored
@@ -371,7 +371,7 @@ class LexicalHandler:
|
||||
|
||||
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
|
||||
and system_id the system identifier of the external subset (or
|
||||
None if none were supplied)."""
|
||||
|
||||
def endDTD(self):
|
||||
|
||||
Reference in New Issue
Block a user