From 3fcd777fd15f320a8c650ab35e2632ee51fe831e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Dec 2025 14:34:32 +0000 Subject: [PATCH 1/6] Initial plan From 9dd0626cba49a494bbceb426d98668ffe33960fe Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Dec 2025 14:51:59 +0000 Subject: [PATCH 2/6] Add namespace_prefixes and other missing xmlparser attributes - Added namespace_prefixes, ordered_attributes, specified_attributes (boolean attributes) - Added intern dictionary attribute - Added stub handlers for all missing handler types to ensure compatibility - Created bool_property macro to ensure boolean attributes are converted correctly Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com> --- crates/stdlib/src/pyexpat.rs | 87 +++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/crates/stdlib/src/pyexpat.rs b/crates/stdlib/src/pyexpat.rs index 871ba7d598..934adb1936 100644 --- a/crates/stdlib/src/pyexpat.rs +++ b/crates/stdlib/src/pyexpat.rs @@ -25,6 +25,22 @@ macro_rules! create_property { }; } +macro_rules! create_bool_property { + ($ctx: expr, $attributes: expr, $name: expr, $class: expr, $element: ident) => { + let attr = $ctx.new_static_getset( + $name, + $class, + move |this: &PyExpatLikeXmlParser| this.$element.read().clone(), + move |this: &PyExpatLikeXmlParser, value: PyObjectRef, vm: &VirtualMachine| { + let bool_value = value.is_true(vm).unwrap_or(false); + *this.$element.write() = vm.ctx.new_bool(bool_value).into(); + }, + ); + + $attributes.insert($ctx.intern_str($name), attr.into()); + }; +} + #[pymodule(name = "pyexpat")] mod _pyexpat { use crate::vm::{ @@ -51,6 +67,29 @@ mod _pyexpat { character_data: MutableObject, entity_decl: MutableObject, buffer_text: MutableObject, + namespace_prefixes: MutableObject, + ordered_attributes: MutableObject, + specified_attributes: MutableObject, + intern: MutableObject, + // Additional handlers (stubs for compatibility) + processing_instruction: MutableObject, + unparsed_entity_decl: MutableObject, + notation_decl: MutableObject, + start_namespace_decl: MutableObject, + end_namespace_decl: MutableObject, + comment: MutableObject, + start_cdata_section: MutableObject, + end_cdata_section: MutableObject, + default: MutableObject, + default_expand: MutableObject, + not_standalone: MutableObject, + external_entity_ref: MutableObject, + start_doctype_decl: MutableObject, + end_doctype_decl: MutableObject, + xml_decl: MutableObject, + element_decl: MutableObject, + attlist_decl: MutableObject, + skipped_entity: MutableObject, } type PyExpatLikeXmlParserRef = PyRef; @@ -71,6 +110,29 @@ mod _pyexpat { character_data: MutableObject::new(vm.ctx.none()), entity_decl: MutableObject::new(vm.ctx.none()), buffer_text: MutableObject::new(vm.ctx.new_bool(false).into()), + namespace_prefixes: MutableObject::new(vm.ctx.new_bool(false).into()), + ordered_attributes: MutableObject::new(vm.ctx.new_bool(false).into()), + specified_attributes: MutableObject::new(vm.ctx.new_bool(false).into()), + intern: MutableObject::new(vm.ctx.new_dict().into()), + // Additional handlers (stubs for compatibility) + processing_instruction: MutableObject::new(vm.ctx.none()), + unparsed_entity_decl: MutableObject::new(vm.ctx.none()), + notation_decl: MutableObject::new(vm.ctx.none()), + start_namespace_decl: MutableObject::new(vm.ctx.none()), + end_namespace_decl: MutableObject::new(vm.ctx.none()), + comment: MutableObject::new(vm.ctx.none()), + start_cdata_section: MutableObject::new(vm.ctx.none()), + end_cdata_section: MutableObject::new(vm.ctx.none()), + default: MutableObject::new(vm.ctx.none()), + default_expand: MutableObject::new(vm.ctx.none()), + not_standalone: MutableObject::new(vm.ctx.none()), + external_entity_ref: MutableObject::new(vm.ctx.none()), + start_doctype_decl: MutableObject::new(vm.ctx.none()), + end_doctype_decl: MutableObject::new(vm.ctx.none()), + xml_decl: MutableObject::new(vm.ctx.none()), + element_decl: MutableObject::new(vm.ctx.none()), + attlist_decl: MutableObject::new(vm.ctx.none()), + skipped_entity: MutableObject::new(vm.ctx.none()), } .into_ref(&vm.ctx)) } @@ -89,7 +151,30 @@ mod _pyexpat { character_data ); create_property!(ctx, attributes, "EntityDeclHandler", class, entity_decl); - create_property!(ctx, attributes, "buffer_text", class, buffer_text); + create_bool_property!(ctx, attributes, "buffer_text", class, buffer_text); + create_bool_property!(ctx, attributes, "namespace_prefixes", class, namespace_prefixes); + create_bool_property!(ctx, attributes, "ordered_attributes", class, ordered_attributes); + create_bool_property!(ctx, attributes, "specified_attributes", class, specified_attributes); + create_property!(ctx, attributes, "intern", class, intern); + // Additional handlers (stubs for compatibility) + create_property!(ctx, attributes, "ProcessingInstructionHandler", class, processing_instruction); + create_property!(ctx, attributes, "UnparsedEntityDeclHandler", class, unparsed_entity_decl); + create_property!(ctx, attributes, "NotationDeclHandler", class, notation_decl); + create_property!(ctx, attributes, "StartNamespaceDeclHandler", class, start_namespace_decl); + create_property!(ctx, attributes, "EndNamespaceDeclHandler", class, end_namespace_decl); + create_property!(ctx, attributes, "CommentHandler", class, comment); + create_property!(ctx, attributes, "StartCdataSectionHandler", class, start_cdata_section); + create_property!(ctx, attributes, "EndCdataSectionHandler", class, end_cdata_section); + create_property!(ctx, attributes, "DefaultHandler", class, default); + create_property!(ctx, attributes, "DefaultHandlerExpand", class, default_expand); + create_property!(ctx, attributes, "NotStandaloneHandler", class, not_standalone); + create_property!(ctx, attributes, "ExternalEntityRefHandler", class, external_entity_ref); + create_property!(ctx, attributes, "StartDoctypeDeclHandler", class, start_doctype_decl); + create_property!(ctx, attributes, "EndDoctypeDeclHandler", class, end_doctype_decl); + create_property!(ctx, attributes, "XmlDeclHandler", class, xml_decl); + create_property!(ctx, attributes, "ElementDeclHandler", class, element_decl); + create_property!(ctx, attributes, "AttlistDeclHandler", class, attlist_decl); + create_property!(ctx, attributes, "SkippedEntityHandler", class, skipped_entity); } fn create_config(&self) -> xml::ParserConfig { From 18a27f6f9eb611a37023d6f757e6b4b8c443a32f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Dec 2025 14:56:36 +0000 Subject: [PATCH 3/6] Remove expectedFailure decorators from passing tests - Tests for buffer_text, namespace_prefixes, ordered_attributes, and specified_attributes now pass Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com> --- Lib/test/test_pyexpat.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py index 80485cc74b..ab93f701b0 100644 --- a/Lib/test/test_pyexpat.py +++ b/Lib/test/test_pyexpat.py @@ -20,28 +20,24 @@ class SetAttributeTest(unittest.TestCase): def setUp(self): self.parser = expat.ParserCreate(namespace_separator='!') - @unittest.expectedFailure # TODO: RUSTPYTHON def test_buffer_text(self): self.assertIs(self.parser.buffer_text, False) for x in 0, 1, 2, 0: self.parser.buffer_text = x self.assertIs(self.parser.buffer_text, bool(x)) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_namespace_prefixes(self): self.assertIs(self.parser.namespace_prefixes, False) for x in 0, 1, 2, 0: self.parser.namespace_prefixes = x self.assertIs(self.parser.namespace_prefixes, bool(x)) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_ordered_attributes(self): self.assertIs(self.parser.ordered_attributes, False) for x in 0, 1, 2, 0: self.parser.ordered_attributes = x self.assertIs(self.parser.ordered_attributes, bool(x)) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_specified_attributes(self): self.assertIs(self.parser.specified_attributes, False) for x in 0, 1, 2, 0: From 143e52f2aa6e8aba6a26f3a54968eb0b437051dd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Dec 2025 15:03:27 +0000 Subject: [PATCH 4/6] Address code review feedback - Use proper error propagation in bool_property setter instead of unwrap_or - Add comment explaining the purpose of the intern dictionary attribute Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com> --- crates/stdlib/src/pyexpat.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/stdlib/src/pyexpat.rs b/crates/stdlib/src/pyexpat.rs index 934adb1936..47ba6fbf67 100644 --- a/crates/stdlib/src/pyexpat.rs +++ b/crates/stdlib/src/pyexpat.rs @@ -31,9 +31,10 @@ macro_rules! create_bool_property { $name, $class, move |this: &PyExpatLikeXmlParser| this.$element.read().clone(), - move |this: &PyExpatLikeXmlParser, value: PyObjectRef, vm: &VirtualMachine| { - let bool_value = value.is_true(vm).unwrap_or(false); + move |this: &PyExpatLikeXmlParser, value: PyObjectRef, vm: &VirtualMachine| -> PyResult<()> { + let bool_value = value.is_true(vm)?; *this.$element.write() = vm.ctx.new_bool(bool_value).into(); + Ok(()) }, ); @@ -113,6 +114,8 @@ mod _pyexpat { namespace_prefixes: MutableObject::new(vm.ctx.new_bool(false).into()), ordered_attributes: MutableObject::new(vm.ctx.new_bool(false).into()), specified_attributes: MutableObject::new(vm.ctx.new_bool(false).into()), + // String interning dictionary - used by the parser to intern element/attribute names + // for memory efficiency and faster comparisons. See CPython's pyexpat documentation. intern: MutableObject::new(vm.ctx.new_dict().into()), // Additional handlers (stubs for compatibility) processing_instruction: MutableObject::new(vm.ctx.none()), From e6bf9de59128e996f7b804f539ebe4ccc4958a08 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 24 Dec 2025 23:00:05 +0000 Subject: [PATCH 5/6] Auto-format: cargo fmt --all --- crates/stdlib/src/pyexpat.rs | 125 ++++++++++++++++++++++++++++++----- 1 file changed, 109 insertions(+), 16 deletions(-) diff --git a/crates/stdlib/src/pyexpat.rs b/crates/stdlib/src/pyexpat.rs index 47ba6fbf67..699fa21852 100644 --- a/crates/stdlib/src/pyexpat.rs +++ b/crates/stdlib/src/pyexpat.rs @@ -31,7 +31,10 @@ macro_rules! create_bool_property { $name, $class, move |this: &PyExpatLikeXmlParser| this.$element.read().clone(), - move |this: &PyExpatLikeXmlParser, value: PyObjectRef, vm: &VirtualMachine| -> PyResult<()> { + move |this: &PyExpatLikeXmlParser, + value: PyObjectRef, + vm: &VirtualMachine| + -> PyResult<()> { let bool_value = value.is_true(vm)?; *this.$element.write() = vm.ctx.new_bool(bool_value).into(); Ok(()) @@ -155,29 +158,119 @@ mod _pyexpat { ); create_property!(ctx, attributes, "EntityDeclHandler", class, entity_decl); create_bool_property!(ctx, attributes, "buffer_text", class, buffer_text); - create_bool_property!(ctx, attributes, "namespace_prefixes", class, namespace_prefixes); - create_bool_property!(ctx, attributes, "ordered_attributes", class, ordered_attributes); - create_bool_property!(ctx, attributes, "specified_attributes", class, specified_attributes); + create_bool_property!( + ctx, + attributes, + "namespace_prefixes", + class, + namespace_prefixes + ); + create_bool_property!( + ctx, + attributes, + "ordered_attributes", + class, + ordered_attributes + ); + create_bool_property!( + ctx, + attributes, + "specified_attributes", + class, + specified_attributes + ); create_property!(ctx, attributes, "intern", class, intern); // Additional handlers (stubs for compatibility) - create_property!(ctx, attributes, "ProcessingInstructionHandler", class, processing_instruction); - create_property!(ctx, attributes, "UnparsedEntityDeclHandler", class, unparsed_entity_decl); + create_property!( + ctx, + attributes, + "ProcessingInstructionHandler", + class, + processing_instruction + ); + create_property!( + ctx, + attributes, + "UnparsedEntityDeclHandler", + class, + unparsed_entity_decl + ); create_property!(ctx, attributes, "NotationDeclHandler", class, notation_decl); - create_property!(ctx, attributes, "StartNamespaceDeclHandler", class, start_namespace_decl); - create_property!(ctx, attributes, "EndNamespaceDeclHandler", class, end_namespace_decl); + create_property!( + ctx, + attributes, + "StartNamespaceDeclHandler", + class, + start_namespace_decl + ); + create_property!( + ctx, + attributes, + "EndNamespaceDeclHandler", + class, + end_namespace_decl + ); create_property!(ctx, attributes, "CommentHandler", class, comment); - create_property!(ctx, attributes, "StartCdataSectionHandler", class, start_cdata_section); - create_property!(ctx, attributes, "EndCdataSectionHandler", class, end_cdata_section); + create_property!( + ctx, + attributes, + "StartCdataSectionHandler", + class, + start_cdata_section + ); + create_property!( + ctx, + attributes, + "EndCdataSectionHandler", + class, + end_cdata_section + ); create_property!(ctx, attributes, "DefaultHandler", class, default); - create_property!(ctx, attributes, "DefaultHandlerExpand", class, default_expand); - create_property!(ctx, attributes, "NotStandaloneHandler", class, not_standalone); - create_property!(ctx, attributes, "ExternalEntityRefHandler", class, external_entity_ref); - create_property!(ctx, attributes, "StartDoctypeDeclHandler", class, start_doctype_decl); - create_property!(ctx, attributes, "EndDoctypeDeclHandler", class, end_doctype_decl); + create_property!( + ctx, + attributes, + "DefaultHandlerExpand", + class, + default_expand + ); + create_property!( + ctx, + attributes, + "NotStandaloneHandler", + class, + not_standalone + ); + create_property!( + ctx, + attributes, + "ExternalEntityRefHandler", + class, + external_entity_ref + ); + create_property!( + ctx, + attributes, + "StartDoctypeDeclHandler", + class, + start_doctype_decl + ); + create_property!( + ctx, + attributes, + "EndDoctypeDeclHandler", + class, + end_doctype_decl + ); create_property!(ctx, attributes, "XmlDeclHandler", class, xml_decl); create_property!(ctx, attributes, "ElementDeclHandler", class, element_decl); create_property!(ctx, attributes, "AttlistDeclHandler", class, attlist_decl); - create_property!(ctx, attributes, "SkippedEntityHandler", class, skipped_entity); + create_property!( + ctx, + attributes, + "SkippedEntityHandler", + class, + skipped_entity + ); } fn create_config(&self) -> xml::ParserConfig { From c9be4ac52e591b0ff42e20790f15f2ae1a77886f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 24 Dec 2025 23:29:37 +0000 Subject: [PATCH 6/6] Remove expectedFailure from test_parse_str and test_parse_file These tests now pass with the added xmlparser attributes. Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com> --- Lib/test/test_pyexpat.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/Lib/test/test_pyexpat.py b/Lib/test/test_pyexpat.py index ab93f701b0..015e749726 100644 --- a/Lib/test/test_pyexpat.py +++ b/Lib/test/test_pyexpat.py @@ -240,7 +240,6 @@ def test_parse_bytes(self): # Issue #6697. self.assertRaises(AttributeError, getattr, parser, '\uD800') - @unittest.expectedFailure # TODO: RUSTPYTHON def test_parse_str(self): out = self.Outputter() parser = expat.ParserCreate(namespace_separator='!') @@ -251,7 +250,6 @@ def test_parse_str(self): operations = out.out self._verify_parse_output(operations) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_parse_file(self): # Try parsing a file out = self.Outputter()