Skip to content

Commit 72cf6c3

Browse files
Copilotyouknowonegithub-actions[bot]
authored
Add missing xmlparser attributes: namespace_prefixes, ordered_attributes, specified_attributes, intern (RustPython#6494)
* 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 * Remove expectedFailure decorators from passing tests - Tests for buffer_text, namespace_prefixes, ordered_attributes, and specified_attributes now pass --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: youknowone <69878+youknowone@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent be9e44a commit 72cf6c3

2 files changed

Lines changed: 182 additions & 7 deletions

File tree

Lib/test/test_pyexpat.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,24 @@ class SetAttributeTest(unittest.TestCase):
2020
def setUp(self):
2121
self.parser = expat.ParserCreate(namespace_separator='!')
2222

23-
@unittest.expectedFailure # TODO: RUSTPYTHON
2423
def test_buffer_text(self):
2524
self.assertIs(self.parser.buffer_text, False)
2625
for x in 0, 1, 2, 0:
2726
self.parser.buffer_text = x
2827
self.assertIs(self.parser.buffer_text, bool(x))
2928

30-
@unittest.expectedFailure # TODO: RUSTPYTHON
3129
def test_namespace_prefixes(self):
3230
self.assertIs(self.parser.namespace_prefixes, False)
3331
for x in 0, 1, 2, 0:
3432
self.parser.namespace_prefixes = x
3533
self.assertIs(self.parser.namespace_prefixes, bool(x))
3634

37-
@unittest.expectedFailure # TODO: RUSTPYTHON
3835
def test_ordered_attributes(self):
3936
self.assertIs(self.parser.ordered_attributes, False)
4037
for x in 0, 1, 2, 0:
4138
self.parser.ordered_attributes = x
4239
self.assertIs(self.parser.ordered_attributes, bool(x))
4340

44-
@unittest.expectedFailure # TODO: RUSTPYTHON
4541
def test_specified_attributes(self):
4642
self.assertIs(self.parser.specified_attributes, False)
4743
for x in 0, 1, 2, 0:
@@ -244,7 +240,6 @@ def test_parse_bytes(self):
244240
# Issue #6697.
245241
self.assertRaises(AttributeError, getattr, parser, '\uD800')
246242

247-
@unittest.expectedFailure # TODO: RUSTPYTHON
248243
def test_parse_str(self):
249244
out = self.Outputter()
250245
parser = expat.ParserCreate(namespace_separator='!')
@@ -255,7 +250,6 @@ def test_parse_str(self):
255250
operations = out.out
256251
self._verify_parse_output(operations)
257252

258-
@unittest.expectedFailure # TODO: RUSTPYTHON
259253
def test_parse_file(self):
260254
# Try parsing a file
261255
out = self.Outputter()

crates/stdlib/src/pyexpat.rs

Lines changed: 182 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,26 @@ macro_rules! create_property {
2525
};
2626
}
2727

28+
macro_rules! create_bool_property {
29+
($ctx: expr, $attributes: expr, $name: expr, $class: expr, $element: ident) => {
30+
let attr = $ctx.new_static_getset(
31+
$name,
32+
$class,
33+
move |this: &PyExpatLikeXmlParser| this.$element.read().clone(),
34+
move |this: &PyExpatLikeXmlParser,
35+
value: PyObjectRef,
36+
vm: &VirtualMachine|
37+
-> PyResult<()> {
38+
let bool_value = value.is_true(vm)?;
39+
*this.$element.write() = vm.ctx.new_bool(bool_value).into();
40+
Ok(())
41+
},
42+
);
43+
44+
$attributes.insert($ctx.intern_str($name), attr.into());
45+
};
46+
}
47+
2848
#[pymodule(name = "pyexpat")]
2949
mod _pyexpat {
3050
use crate::vm::{
@@ -51,6 +71,29 @@ mod _pyexpat {
5171
character_data: MutableObject,
5272
entity_decl: MutableObject,
5373
buffer_text: MutableObject,
74+
namespace_prefixes: MutableObject,
75+
ordered_attributes: MutableObject,
76+
specified_attributes: MutableObject,
77+
intern: MutableObject,
78+
// Additional handlers (stubs for compatibility)
79+
processing_instruction: MutableObject,
80+
unparsed_entity_decl: MutableObject,
81+
notation_decl: MutableObject,
82+
start_namespace_decl: MutableObject,
83+
end_namespace_decl: MutableObject,
84+
comment: MutableObject,
85+
start_cdata_section: MutableObject,
86+
end_cdata_section: MutableObject,
87+
default: MutableObject,
88+
default_expand: MutableObject,
89+
not_standalone: MutableObject,
90+
external_entity_ref: MutableObject,
91+
start_doctype_decl: MutableObject,
92+
end_doctype_decl: MutableObject,
93+
xml_decl: MutableObject,
94+
element_decl: MutableObject,
95+
attlist_decl: MutableObject,
96+
skipped_entity: MutableObject,
5497
}
5598
type PyExpatLikeXmlParserRef = PyRef<PyExpatLikeXmlParser>;
5699

@@ -71,6 +114,31 @@ mod _pyexpat {
71114
character_data: MutableObject::new(vm.ctx.none()),
72115
entity_decl: MutableObject::new(vm.ctx.none()),
73116
buffer_text: MutableObject::new(vm.ctx.new_bool(false).into()),
117+
namespace_prefixes: MutableObject::new(vm.ctx.new_bool(false).into()),
118+
ordered_attributes: MutableObject::new(vm.ctx.new_bool(false).into()),
119+
specified_attributes: MutableObject::new(vm.ctx.new_bool(false).into()),
120+
// String interning dictionary - used by the parser to intern element/attribute names
121+
// for memory efficiency and faster comparisons. See CPython's pyexpat documentation.
122+
intern: MutableObject::new(vm.ctx.new_dict().into()),
123+
// Additional handlers (stubs for compatibility)
124+
processing_instruction: MutableObject::new(vm.ctx.none()),
125+
unparsed_entity_decl: MutableObject::new(vm.ctx.none()),
126+
notation_decl: MutableObject::new(vm.ctx.none()),
127+
start_namespace_decl: MutableObject::new(vm.ctx.none()),
128+
end_namespace_decl: MutableObject::new(vm.ctx.none()),
129+
comment: MutableObject::new(vm.ctx.none()),
130+
start_cdata_section: MutableObject::new(vm.ctx.none()),
131+
end_cdata_section: MutableObject::new(vm.ctx.none()),
132+
default: MutableObject::new(vm.ctx.none()),
133+
default_expand: MutableObject::new(vm.ctx.none()),
134+
not_standalone: MutableObject::new(vm.ctx.none()),
135+
external_entity_ref: MutableObject::new(vm.ctx.none()),
136+
start_doctype_decl: MutableObject::new(vm.ctx.none()),
137+
end_doctype_decl: MutableObject::new(vm.ctx.none()),
138+
xml_decl: MutableObject::new(vm.ctx.none()),
139+
element_decl: MutableObject::new(vm.ctx.none()),
140+
attlist_decl: MutableObject::new(vm.ctx.none()),
141+
skipped_entity: MutableObject::new(vm.ctx.none()),
74142
}
75143
.into_ref(&vm.ctx))
76144
}
@@ -89,7 +157,120 @@ mod _pyexpat {
89157
character_data
90158
);
91159
create_property!(ctx, attributes, "EntityDeclHandler", class, entity_decl);
92-
create_property!(ctx, attributes, "buffer_text", class, buffer_text);
160+
create_bool_property!(ctx, attributes, "buffer_text", class, buffer_text);
161+
create_bool_property!(
162+
ctx,
163+
attributes,
164+
"namespace_prefixes",
165+
class,
166+
namespace_prefixes
167+
);
168+
create_bool_property!(
169+
ctx,
170+
attributes,
171+
"ordered_attributes",
172+
class,
173+
ordered_attributes
174+
);
175+
create_bool_property!(
176+
ctx,
177+
attributes,
178+
"specified_attributes",
179+
class,
180+
specified_attributes
181+
);
182+
create_property!(ctx, attributes, "intern", class, intern);
183+
// Additional handlers (stubs for compatibility)
184+
create_property!(
185+
ctx,
186+
attributes,
187+
"ProcessingInstructionHandler",
188+
class,
189+
processing_instruction
190+
);
191+
create_property!(
192+
ctx,
193+
attributes,
194+
"UnparsedEntityDeclHandler",
195+
class,
196+
unparsed_entity_decl
197+
);
198+
create_property!(ctx, attributes, "NotationDeclHandler", class, notation_decl);
199+
create_property!(
200+
ctx,
201+
attributes,
202+
"StartNamespaceDeclHandler",
203+
class,
204+
start_namespace_decl
205+
);
206+
create_property!(
207+
ctx,
208+
attributes,
209+
"EndNamespaceDeclHandler",
210+
class,
211+
end_namespace_decl
212+
);
213+
create_property!(ctx, attributes, "CommentHandler", class, comment);
214+
create_property!(
215+
ctx,
216+
attributes,
217+
"StartCdataSectionHandler",
218+
class,
219+
start_cdata_section
220+
);
221+
create_property!(
222+
ctx,
223+
attributes,
224+
"EndCdataSectionHandler",
225+
class,
226+
end_cdata_section
227+
);
228+
create_property!(ctx, attributes, "DefaultHandler", class, default);
229+
create_property!(
230+
ctx,
231+
attributes,
232+
"DefaultHandlerExpand",
233+
class,
234+
default_expand
235+
);
236+
create_property!(
237+
ctx,
238+
attributes,
239+
"NotStandaloneHandler",
240+
class,
241+
not_standalone
242+
);
243+
create_property!(
244+
ctx,
245+
attributes,
246+
"ExternalEntityRefHandler",
247+
class,
248+
external_entity_ref
249+
);
250+
create_property!(
251+
ctx,
252+
attributes,
253+
"StartDoctypeDeclHandler",
254+
class,
255+
start_doctype_decl
256+
);
257+
create_property!(
258+
ctx,
259+
attributes,
260+
"EndDoctypeDeclHandler",
261+
class,
262+
end_doctype_decl
263+
);
264+
create_property!(ctx, attributes, "XmlDeclHandler", class, xml_decl);
265+
create_property!(ctx, attributes, "ElementDeclHandler", class, element_decl);
266+
create_property!(ctx, attributes, "AttlistDeclHandler", class, attlist_decl);
267+
create_property!(
268+
ctx,
269+
attributes,
270+
"SkippedEntityHandler",
271+
class,
272+
skipped_entity
273+
);
93274
}
94275

95276
fn create_config(&self) -> xml::ParserConfig {

0 commit comments

Comments
 (0)