From 890868380ae908807f34b0be1606e57c00976d7e Mon Sep 17 00:00:00 2001 From: 1ndahous3 <9205230+1ndahous3@users.noreply.github.com> Date: Sun, 18 Oct 2026 11:41:55 +0300 Subject: [PATCH] Accept bytes-like input in pyexpat.Parse Assisted-by: Codex:GPT-6 --- crates/stdlib/src/pyexpat.rs | 11 ++++------- extra_tests/snippets/stdlib_pyexpat.py | 27 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/crates/stdlib/src/pyexpat.rs b/crates/stdlib/src/pyexpat.rs index 724fdb75926..2ca962e924f 100644 --- a/crates/stdlib/src/pyexpat.rs +++ b/crates/stdlib/src/pyexpat.rs @@ -168,9 +168,9 @@ mod _pyexpat { use crate::vm::{ AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, - builtins::{PyBytesRef, PyException, PyModule, PyStr, PyStrRef, PyType, PyUtf8StrRef}, + builtins::{PyException, PyModule, PyStr, PyStrRef, PyType, PyUtf8StrRef}, extend_module, - function::{ArgBytesLike, Either, IntoFuncArgs, OptionalArg, OptionalOption}, + function::{ArgBytesLike, ArgStrOrBytesLike, IntoFuncArgs, OptionalArg, OptionalOption}, types::Constructor, }; use alloc::collections::VecDeque; @@ -1403,14 +1403,11 @@ mod _pyexpat { #[pymethod(name = "Parse")] fn parse( zelf: &Py, - data: Either, + data: ArgStrOrBytesLike, isfinal: OptionalArg, vm: &VirtualMachine, ) -> PyResult { - let bytes = match data { - Either::A(s) => s.as_bytes().to_vec(), - Either::B(b) => b.as_bytes().to_vec(), - }; + let bytes = data.borrow_bytes().to_vec(); zelf.feed(vm, &bytes, isfinal.unwrap_or(false))?; Ok(1) } diff --git a/extra_tests/snippets/stdlib_pyexpat.py b/extra_tests/snippets/stdlib_pyexpat.py index e8e2652d3db..512a4a529d8 100644 --- a/extra_tests/snippets/stdlib_pyexpat.py +++ b/extra_tests/snippets/stdlib_pyexpat.py @@ -21,3 +21,30 @@ else {expected_attribute: "v", "a": "u"} ) assert starts[1][1] == expected_attributes + +# Parse accepts contiguous buffer objects, including views of mutable input. +for xml in (bytearray(b"ok"), memoryview(b"ok")): + parser = expat.ParserCreate() + text = [] + parser.CharacterDataHandler = text.append + assert parser.Parse(xml, True) == 1 + assert "".join(text) == "ok" + +try: + expat.ParserCreate().Parse(memoryview(b"")[::2], True) +except BufferError: + pass +else: + raise AssertionError("Parse accepted a non-contiguous buffer") + +# Keep the export alive through callbacks without retaining its read lock. +xml = bytearray(b"") +parser = expat.ParserCreate() +parser.StartElementHandler = lambda *_: xml.extend(b" ") +try: + parser.Parse(xml, True) +except BufferError: + pass +else: + raise AssertionError("Parse released its input buffer before the callback") +xml.extend(b" ")