mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
add initial pyexpat implementation
This commit is contained in:
1 parent
2aa67a8881
commit
d382009a5f
5 files changed
+216
-2
No files matched your search
Generated
+7
@@ -2112,6 +2112,7 @@ dependencies = [
|
||||
"widestring",
|
||||
"winapi",
|
||||
"winreg",
|
||||
"xml-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2960,6 +2961,12 @@ dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b07db065a5cf61a7e4ba64f29e67db906fb1787316516c4e6e5ff0fea1efcd8a"
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
|
||||
Vendored
+1
-2
@@ -62,8 +62,7 @@ import os
|
||||
import re
|
||||
import struct
|
||||
from warnings import warn
|
||||
# XXX RUSTPYTHON TODO: pyexpat
|
||||
# from xml.parsers.expat import ParserCreate
|
||||
from xml.parsers.expat import ParserCreate
|
||||
|
||||
|
||||
PlistFormat = enum.Enum('PlistFormat', 'FMT_XML FMT_BINARY', module=__name__)
|
||||
|
||||
@@ -34,6 +34,7 @@ sha-1 = "0.9.4"
|
||||
sha2 = "0.9.3"
|
||||
sha3 = "0.9.1"
|
||||
blake2 = "0.9.1"
|
||||
xml-rs = "0.8"
|
||||
|
||||
num-complex = { version = "0.4.0", features = ["serde"] }
|
||||
num-bigint = { version = "0.4.0", features = ["serde"] }
|
||||
|
||||
@@ -26,6 +26,7 @@ mod marshal;
|
||||
mod math;
|
||||
mod operator;
|
||||
mod platform;
|
||||
mod pyexpat;
|
||||
pub(crate) mod pystruct;
|
||||
mod random;
|
||||
// TODO: maybe make this an extension module, if we ever get those
|
||||
@@ -119,6 +120,7 @@ pub fn get_module_inits() -> StdlibMap {
|
||||
"marshal" => marshal::make_module,
|
||||
"math" => math::make_module,
|
||||
"_operator" => operator::make_module,
|
||||
"pyexpat" => pyexpat::make_module,
|
||||
"_platform" => platform::make_module,
|
||||
"_random" => random::make_module,
|
||||
"_sre" => sre::make_module,
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
/* Pyexpat builtin module
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
use crate::vm::VirtualMachine;
|
||||
use crate::PyObjectRef;
|
||||
|
||||
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
|
||||
let module = _pyexpat::make_module(vm);
|
||||
|
||||
extend_module!(vm, module, {
|
||||
"errors" => _errors::make_module(vm),
|
||||
"model" => _model::make_module(vm)
|
||||
});
|
||||
|
||||
module
|
||||
}
|
||||
|
||||
#[pymodule(name = "pyexpat")]
|
||||
mod _pyexpat {
|
||||
use crate::builtins::{PyStr, PyStrRef, PyTypeRef};
|
||||
use crate::byteslike::PyBytesLike;
|
||||
use crate::function::{IntoFuncArgs, OptionalArg};
|
||||
use crate::pyobject::StaticType;
|
||||
use crate::{
|
||||
IntoPyObject, IntoPyRef, ItemProtocol, PyObjectRef, PyRef, PyResult, PyValue,
|
||||
TryFromObject, VirtualMachine,
|
||||
};
|
||||
|
||||
use rustpython_common::lock::PyRwLock;
|
||||
use std::io::Cursor;
|
||||
use xml::reader::XmlEvent;
|
||||
type MutableObject = PyRwLock<PyObjectRef>;
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name = "xmlparser", module = false)]
|
||||
#[derive(Debug)]
|
||||
struct PyExpatLikeXmlParser {
|
||||
start_element: MutableObject,
|
||||
end_element: MutableObject,
|
||||
character_data: MutableObject,
|
||||
entity_decl: MutableObject,
|
||||
}
|
||||
type PyExpatLikeXmlParserRef = PyRef<PyExpatLikeXmlParser>;
|
||||
|
||||
impl PyValue for PyExpatLikeXmlParser {
|
||||
fn class(_vm: &VirtualMachine) -> &PyTypeRef {
|
||||
Self::static_type()
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn invoke_handler<T>(vm: &VirtualMachine, handler: &MutableObject, args: T)
|
||||
where
|
||||
T: IntoFuncArgs,
|
||||
{
|
||||
vm.invoke(&handler.read().clone(), args).ok();
|
||||
}
|
||||
|
||||
#[pyimpl]
|
||||
impl PyExpatLikeXmlParser {
|
||||
fn new(vm: &VirtualMachine) -> PyResult<PyExpatLikeXmlParserRef> {
|
||||
Ok(PyExpatLikeXmlParser {
|
||||
start_element: MutableObject::new(vm.ctx.none()),
|
||||
end_element: MutableObject::new(vm.ctx.none()),
|
||||
character_data: MutableObject::new(vm.ctx.none()),
|
||||
entity_decl: MutableObject::new(vm.ctx.none()),
|
||||
}
|
||||
.into_ref(vm))
|
||||
}
|
||||
|
||||
#[pyproperty(name = "StartElementHandler")]
|
||||
fn start_element_handler(&self) -> PyObjectRef {
|
||||
self.start_element.read().clone()
|
||||
}
|
||||
|
||||
#[pyproperty(setter, name = "StartElementHandler")]
|
||||
fn set_start_element_handler(&self, func: PyObjectRef) {
|
||||
let mut handler = self.start_element.write();
|
||||
*handler = func;
|
||||
}
|
||||
|
||||
#[pyproperty(name = "EndElementHandler")]
|
||||
fn end_element_handler(&self) -> PyObjectRef {
|
||||
self.end_element.read().clone()
|
||||
}
|
||||
|
||||
#[pyproperty(setter, name = "EndElementHandler")]
|
||||
fn set_end_element_handler(&self, func: PyObjectRef) {
|
||||
let mut handler = self.end_element.write();
|
||||
*handler = func;
|
||||
}
|
||||
|
||||
#[pyproperty(name = "CharacterDataHandler")]
|
||||
fn character_data_handler(&self) -> PyObjectRef {
|
||||
self.character_data.read().clone()
|
||||
}
|
||||
|
||||
#[pyproperty(setter, name = "CharacterDataHandler")]
|
||||
fn set_character_data_handler(&self, func: PyObjectRef) {
|
||||
let mut handler = self.character_data.write();
|
||||
*handler = func;
|
||||
}
|
||||
|
||||
#[pyproperty(name = "EntityDeclHandler")]
|
||||
fn entity_decl(&self) -> PyObjectRef {
|
||||
self.entity_decl.read().clone()
|
||||
}
|
||||
|
||||
#[pyproperty(setter, name = "EntityDeclHandler")]
|
||||
fn set_decl_handler(&self, func: PyObjectRef) {
|
||||
let mut handler = self.entity_decl.write();
|
||||
*handler = func;
|
||||
}
|
||||
|
||||
fn create_config(&self) -> xml::ParserConfig {
|
||||
xml::ParserConfig::new()
|
||||
.cdata_to_characters(true)
|
||||
.coalesce_characters(false)
|
||||
.whitespace_to_characters(true)
|
||||
}
|
||||
|
||||
fn do_parse<T>(&self, vm: &VirtualMachine, parser: xml::EventReader<T>)
|
||||
where
|
||||
T: std::io::Read,
|
||||
{
|
||||
for e in parser {
|
||||
match e {
|
||||
Ok(XmlEvent::StartElement {
|
||||
name, attributes, ..
|
||||
}) => {
|
||||
let dict = vm.ctx.new_dict();
|
||||
for attribute in attributes {
|
||||
dict.set_item(
|
||||
attribute.name.local_name.as_str(),
|
||||
vm.ctx.new_str(attribute.value),
|
||||
vm,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let name_str = PyStr::from(name.local_name).into_ref(vm);
|
||||
invoke_handler(vm, &self.start_element, (name_str, dict.into_object()));
|
||||
}
|
||||
Ok(XmlEvent::EndElement { name, .. }) => {
|
||||
let name_str = PyStr::from(name.local_name).into_ref(vm);
|
||||
invoke_handler(vm, &self.end_element, (name_str,));
|
||||
}
|
||||
Ok(XmlEvent::Characters(chars)) => {
|
||||
let str = PyStr::from(chars).into_ref(vm);
|
||||
invoke_handler(vm, &self.character_data, (str,));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(name = "Parse")]
|
||||
fn parse(&self, data: PyStrRef, _isfinal: OptionalArg<bool>, vm: &VirtualMachine) {
|
||||
let reader = Cursor::<Vec<u8>>::new(data.as_str().as_bytes().to_vec());
|
||||
let parser = self.create_config().create_reader(reader);
|
||||
self.do_parse(vm, parser);
|
||||
}
|
||||
|
||||
#[pymethod(name = "ParseFile")]
|
||||
fn parse_file(&self, file: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
// todo: read chunks at a time
|
||||
let read_res = vm.call_method(&file, "read", ())?;
|
||||
let bytes_like = PyBytesLike::try_from_object(vm, read_res)?;
|
||||
let buf = bytes_like.borrow_buf().to_vec();
|
||||
let reader = Cursor::new(buf);
|
||||
let parser = self.create_config().create_reader(reader);
|
||||
self.do_parse(vm, parser);
|
||||
|
||||
// todo: return value
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(FromArgs)]
|
||||
#[allow(dead_code)]
|
||||
struct ParserCreateArgs {
|
||||
#[pyarg(any, optional)]
|
||||
encoding: OptionalArg<PyStrRef>,
|
||||
#[pyarg(any, optional)]
|
||||
namespace_separator: OptionalArg<PyStrRef>,
|
||||
#[pyarg(any, optional)]
|
||||
intern: OptionalArg<PyStrRef>,
|
||||
}
|
||||
|
||||
#[pyfunction(name = "ParserCreate")]
|
||||
fn parser_create(
|
||||
_args: ParserCreateArgs,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyExpatLikeXmlParserRef> {
|
||||
PyExpatLikeXmlParser::new(vm)
|
||||
}
|
||||
}
|
||||
|
||||
#[pymodule(name = "model")]
|
||||
mod _model {}
|
||||
|
||||
#[pymodule(name = "errors")]
|
||||
mod _errors {}
|
||||
Reference in new issue
Block a user