forked from Rust-related/RustPython
191 lines
6.0 KiB
Rust
191 lines
6.0 KiB
Rust
use super::{PyDict, PyGenericAlias, PyList, PyStrRef, PyTuple, PyTypeRef};
|
|
use crate::{
|
|
function::{IntoPyObject, OptionalArg},
|
|
protocol::{PyMapping, PyMappingMethods},
|
|
types::{AsMapping, Constructor, Iterable},
|
|
PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol,
|
|
VirtualMachine,
|
|
};
|
|
|
|
#[pyclass(module = false, name = "mappingproxy")]
|
|
#[derive(Debug)]
|
|
pub struct PyMappingProxy {
|
|
mapping: MappingProxyInner,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum MappingProxyInner {
|
|
Class(PyTypeRef),
|
|
Dict(PyObjectRef),
|
|
}
|
|
|
|
impl PyValue for PyMappingProxy {
|
|
fn class(vm: &VirtualMachine) -> &PyTypeRef {
|
|
&vm.ctx.types.mappingproxy_type
|
|
}
|
|
}
|
|
|
|
impl PyMappingProxy {
|
|
pub fn new(class: PyTypeRef) -> Self {
|
|
Self {
|
|
mapping: MappingProxyInner::Class(class),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Constructor for PyMappingProxy {
|
|
type Args = PyObjectRef;
|
|
|
|
fn py_new(cls: PyTypeRef, mapping: Self::Args, vm: &VirtualMachine) -> PyResult {
|
|
if !PyMapping::from(mapping.as_ref()).has_protocol(vm)
|
|
|| mapping.payload_if_subclass::<PyList>(vm).is_some()
|
|
|| mapping.payload_if_subclass::<PyTuple>(vm).is_some()
|
|
{
|
|
Err(vm.new_type_error(format!(
|
|
"mappingproxy() argument must be a mapping, not {}",
|
|
mapping.class()
|
|
)))
|
|
} else {
|
|
Self {
|
|
mapping: MappingProxyInner::Dict(mapping),
|
|
}
|
|
.into_pyresult_with_type(vm, cls)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[pyimpl(with(AsMapping, Iterable, Constructor))]
|
|
impl PyMappingProxy {
|
|
fn get_inner(&self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<PyObjectRef>> {
|
|
let opt = match &self.mapping {
|
|
MappingProxyInner::Class(class) => {
|
|
let key = PyStrRef::try_from_object(vm, key)?;
|
|
class.attributes.read().get(key.as_str()).cloned()
|
|
}
|
|
MappingProxyInner::Dict(obj) => obj.get_item(key, vm).ok(),
|
|
};
|
|
Ok(opt)
|
|
}
|
|
|
|
#[pymethod]
|
|
fn get(
|
|
&self,
|
|
key: PyObjectRef,
|
|
default: OptionalArg,
|
|
vm: &VirtualMachine,
|
|
) -> PyResult<Option<PyObjectRef>> {
|
|
let default = default.into_option();
|
|
let value = self.get_inner(key, vm)?.or(default);
|
|
Ok(value)
|
|
}
|
|
|
|
#[pymethod(magic)]
|
|
pub fn getitem(&self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
|
self.get_inner(key.clone(), vm)?
|
|
.ok_or_else(|| vm.new_key_error(key))
|
|
}
|
|
|
|
#[pymethod(magic)]
|
|
pub fn contains(&self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
|
match &self.mapping {
|
|
MappingProxyInner::Class(class) => {
|
|
let key = PyStrRef::try_from_object(vm, key)?;
|
|
Ok(vm
|
|
.ctx
|
|
.new_bool(class.attributes.read().contains_key(key.as_str()))
|
|
.into())
|
|
}
|
|
MappingProxyInner::Dict(obj) => vm._membership(obj.clone(), key),
|
|
}
|
|
}
|
|
|
|
#[pymethod]
|
|
pub fn items(&self, vm: &VirtualMachine) -> PyResult {
|
|
let obj = match &self.mapping {
|
|
MappingProxyInner::Dict(d) => d.clone(),
|
|
MappingProxyInner::Class(c) => {
|
|
PyDict::from_attributes(c.attributes.read().clone(), vm)?.into_pyobject(vm)
|
|
}
|
|
};
|
|
vm.call_method(&obj, "items", ())
|
|
}
|
|
#[pymethod]
|
|
pub fn keys(&self, vm: &VirtualMachine) -> PyResult {
|
|
let obj = match &self.mapping {
|
|
MappingProxyInner::Dict(d) => d.clone(),
|
|
MappingProxyInner::Class(c) => {
|
|
PyDict::from_attributes(c.attributes.read().clone(), vm)?.into_pyobject(vm)
|
|
}
|
|
};
|
|
vm.call_method(&obj, "keys", ())
|
|
}
|
|
#[pymethod]
|
|
pub fn values(&self, vm: &VirtualMachine) -> PyResult {
|
|
let obj = match &self.mapping {
|
|
MappingProxyInner::Dict(d) => d.clone(),
|
|
MappingProxyInner::Class(c) => {
|
|
PyDict::from_attributes(c.attributes.read().clone(), vm)?.into_pyobject(vm)
|
|
}
|
|
};
|
|
vm.call_method(&obj, "values", ())
|
|
}
|
|
#[pymethod]
|
|
pub fn copy(&self, vm: &VirtualMachine) -> PyResult {
|
|
match &self.mapping {
|
|
MappingProxyInner::Dict(d) => vm.call_method(d, "copy", ()),
|
|
MappingProxyInner::Class(c) => {
|
|
Ok(PyDict::from_attributes(c.attributes.read().clone(), vm)?.into_pyobject(vm))
|
|
}
|
|
}
|
|
}
|
|
#[pymethod(magic)]
|
|
fn repr(&self, vm: &VirtualMachine) -> PyResult<String> {
|
|
let obj = match &self.mapping {
|
|
MappingProxyInner::Dict(d) => d.clone(),
|
|
MappingProxyInner::Class(c) => {
|
|
PyDict::from_attributes(c.attributes.read().clone(), vm)?.into_pyobject(vm)
|
|
}
|
|
};
|
|
Ok(format!("mappingproxy({})", obj.repr(vm)?))
|
|
}
|
|
|
|
#[pyclassmethod(magic)]
|
|
fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
|
|
PyGenericAlias::new(cls, args, vm)
|
|
}
|
|
}
|
|
|
|
impl PyMappingProxy {
|
|
const MAPPING_METHODS: PyMappingMethods = PyMappingMethods {
|
|
length: None,
|
|
subscript: Some(|mapping, needle, vm| {
|
|
mapping.obj_as::<Self>().getitem(needle.to_owned(), vm)
|
|
}),
|
|
ass_subscript: None,
|
|
};
|
|
}
|
|
|
|
impl AsMapping for PyMappingProxy {
|
|
fn as_mapping(_zelf: &crate::PyObjectView<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
|
|
Self::MAPPING_METHODS
|
|
}
|
|
}
|
|
|
|
impl Iterable for PyMappingProxy {
|
|
fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
|
|
let obj = match &zelf.mapping {
|
|
MappingProxyInner::Dict(d) => d.clone(),
|
|
MappingProxyInner::Class(c) => {
|
|
// TODO: something that's much more efficient than this
|
|
PyDict::from_attributes(c.attributes.read().clone(), vm)?.into_pyobject(vm)
|
|
}
|
|
};
|
|
let iter = obj.get_iter(vm)?;
|
|
Ok(iter.into())
|
|
}
|
|
}
|
|
|
|
pub fn init(context: &PyContext) {
|
|
PyMappingProxy::extend_class(context, &context.types.mappingproxy_type)
|
|
}
|