add PyMapping as PyObjectRef wrapper

Modify original `PyMapping` to `PyMappingMethods` and create `PyMapping`
struct as `PyObjectRef` wrapper for implementing `PyMapping_*`
functions. https://docs.python.org/3/c-api/mapping.html

Signed-off-by: snowapril <sinjihng@gmail.com>
This commit is contained in:
snowapril committed 2021-10-02 16:45:34 +09:00
1 parent 4fd0681498
commit aa2a7f01ee
14 files changed
+166 -63

No files matched your search

+5 -3
View File
@@ -17,7 +17,9 @@ mod array {
},
class_or_notimplemented,
function::{ArgBytesLike, ArgIterable, OptionalArg},
protocol::{BufferInternal, BufferOptions, PyBuffer, PyIterReturn, PyMapping, ResizeGuard},
protocol::{
BufferInternal, BufferOptions, PyBuffer, PyIterReturn, PyMappingMethods, ResizeGuard,
},
sliceable::{saturate_index, PySliceableSequence, PySliceableSequenceMut, SequenceIndex},
slots::{
AsBuffer, AsMapping, Comparable, Iterable, IteratorIterable, PyComparisonOp,
@@ -1165,8 +1167,8 @@ mod array {
}
impl AsMapping for PyArray {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
Ok(PyMapping {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
Ok(PyMappingMethods {
length: Some(Self::length),
subscript: Some(Self::subscript),
ass_subscript: Some(Self::ass_subscript),
+5 -3
View File
@@ -15,7 +15,9 @@ use crate::{
ByteInnerTranslateOptions, DecodeArgs, PyBytesInner,
},
function::{ArgBytesLike, ArgIterable, FuncArgs, OptionalArg, OptionalOption},
protocol::{BufferInternal, BufferOptions, PyBuffer, PyIterReturn, PyMapping, ResizeGuard},
protocol::{
BufferInternal, BufferOptions, PyBuffer, PyIterReturn, PyMappingMethods, ResizeGuard,
},
sliceable::{PySliceableSequence, PySliceableSequenceMut, SequenceIndex},
slots::{
AsBuffer, AsMapping, Callable, Comparable, Hashable, Iterable, IteratorIterable,
@@ -716,8 +718,8 @@ impl<'a> ResizeGuard<'a> for PyByteArray {
}
impl AsMapping for PyByteArray {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
Ok(PyMapping {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
Ok(PyMappingMethods {
length: Some(Self::length),
subscript: Some(Self::subscript),
ass_subscript: Some(Self::ass_subscript),
+3 -3
View File
@@ -7,7 +7,7 @@ use crate::{
},
common::hash::PyHash,
function::{ArgBytesLike, ArgIterable, OptionalArg, OptionalOption},
protocol::{BufferInternal, BufferOptions, PyBuffer, PyIterReturn, PyMapping},
protocol::{BufferInternal, BufferOptions, PyBuffer, PyIterReturn, PyMappingMethods},
slots::{
AsBuffer, AsMapping, Callable, Comparable, Hashable, Iterable, IteratorIterable,
PyComparisonOp, SlotConstructor, SlotIterator,
@@ -541,8 +541,8 @@ impl BufferInternal for PyRef<PyBytes> {
}
impl AsMapping for PyBytes {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
Ok(PyMapping {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
Ok(PyMappingMethods {
length: Some(Self::length),
subscript: Some(Self::subscript),
ass_subscript: None,
+4 -4
View File
@@ -4,7 +4,7 @@ use crate::{
common::ascii,
dictdatatype::{self, DictKey},
function::{ArgIterable, FuncArgs, KwArgs, OptionalArg},
protocol::{PyIterReturn, PyMapping},
protocol::{PyIterReturn, PyMappingMethods},
slots::{
AsMapping, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, SlotIterator,
Unhashable,
@@ -412,8 +412,8 @@ impl PyDict {
}
impl AsMapping for PyDict {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
Ok(PyMapping {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
Ok(PyMappingMethods {
length: Some(Self::length),
subscript: Some(Self::subscript),
ass_subscript: Some(Self::ass_subscript),
@@ -685,7 +685,7 @@ macro_rules! dict_iterator {
}
impl $name {
fn new(dict: PyDictRef) -> Self {
pub fn new(dict: PyDictRef) -> Self {
$name { dict }
}
}
+3 -3
View File
@@ -8,7 +8,7 @@ use crate::common::lock::{
};
use crate::{
function::{ArgIterable, FuncArgs, OptionalArg},
protocol::{PyIterReturn, PyMapping},
protocol::{PyIterReturn, PyMappingMethods},
sequence::{self, SimpleSeq},
sliceable::{PySliceableSequence, PySliceableSequenceMut, SequenceIndex},
slots::{
@@ -418,8 +418,8 @@ impl PyList {
}
impl AsMapping for PyList {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
Ok(PyMapping {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
Ok(PyMappingMethods {
length: Some(Self::length),
subscript: Some(Self::subscript),
ass_subscript: Some(Self::ass_subscript),
+4 -4
View File
@@ -1,7 +1,7 @@
use super::{PyDict, PyList, PyStrRef, PyTuple, PyTypeRef};
use crate::{
function::OptionalArg,
protocol::PyMapping,
protocol::{PyMapping, PyMappingMethods},
slots::{AsMapping, Iterable, SlotConstructor},
IntoPyObject, ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
TryFromObject, TypeProtocol, VirtualMachine,
@@ -37,7 +37,7 @@ impl SlotConstructor for PyMappingProxy {
type Args = PyObjectRef;
fn py_new(cls: PyTypeRef, mapping: Self::Args, vm: &VirtualMachine) -> PyResult {
if !PyMapping::check(&mapping, vm)
if !PyMapping::check(&mapping)
|| mapping.payload_if_subclass::<PyList>(vm).is_some()
|| mapping.payload_if_subclass::<PyTuple>(vm).is_some()
{
@@ -140,8 +140,8 @@ impl PyMappingProxy {
}
impl AsMapping for PyMappingProxy {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
Ok(PyMapping {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
Ok(PyMappingMethods {
length: None,
subscript: Some(Self::subscript),
ass_subscript: None,
+3 -3
View File
@@ -8,7 +8,7 @@ use crate::common::{
use crate::{
bytesinner::bytes_to_hex,
function::{FuncArgs, OptionalArg},
protocol::{BufferInternal, BufferOptions, PyBuffer, PyMapping},
protocol::{BufferInternal, BufferOptions, PyBuffer, PyMappingMethods},
sliceable::{convert_slice, wrap_index, SequenceIndex},
slots::{AsBuffer, AsMapping, Comparable, Hashable, PyComparisonOp, SlotConstructor},
stdlib::pystruct::FormatSpec,
@@ -735,8 +735,8 @@ impl BufferInternal for PyRef<PyMemoryView> {
}
impl AsMapping for PyMemoryView {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
Ok(PyMapping {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
Ok(PyMappingMethods {
length: Some(Self::length),
subscript: Some(Self::subscript),
ass_subscript: Some(Self::ass_subscript),
+2 -2
View File
@@ -5,7 +5,7 @@ use super::{
use crate::common::{ascii, lock::PyRwLock};
use crate::{
function::{FuncArgs, KwArgs, OptionalArg},
protocol::{PyIterReturn, PyMapping},
protocol::{PyIterReturn, PyMappingMethods},
slots::{self, Callable, PyTypeFlags, PyTypeSlots, SlotGetattro, SlotSetattro},
utils::Either,
IdProtocol, PyAttributes, PyClassImpl, PyContext, PyLease, PyObjectRef, PyRef, PyResult,
@@ -283,7 +283,7 @@ impl PyType {
}
let func: slots::MappingFunc = |zelf, _vm| {
Ok(PyMapping {
Ok(PyMappingMethods {
length: then_some_closure!(zelf.has_class_attr("__len__"), |zelf, vm| {
vm.call_special_method(zelf, "__len__", ()).map(|obj| {
obj.payload_if_subclass::<PyInt>(vm)
+3 -3
View File
@@ -2,7 +2,7 @@ use super::{PyInt, PyIntRef, PySlice, PySliceRef, PyTypeRef};
use crate::common::hash::PyHash;
use crate::{
function::{FuncArgs, OptionalArg},
protocol::{PyIterReturn, PyMapping},
protocol::{PyIterReturn, PyMappingMethods},
slots::{
AsMapping, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, SlotIterator,
},
@@ -375,8 +375,8 @@ impl PyRange {
}
impl AsMapping for PyRange {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
Ok(PyMapping {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
Ok(PyMappingMethods {
length: Some(Self::length),
subscript: Some(Self::subscript),
ass_subscript: None,
+3 -3
View File
@@ -6,7 +6,7 @@ use super::{
use crate::common::hash::PyHash;
use crate::{
function::OptionalArg,
protocol::{PyIterReturn, PyMapping},
protocol::{PyIterReturn, PyMappingMethods},
sequence::{self, SimpleSeq},
sliceable::PySliceableSequence,
slots::{
@@ -288,8 +288,8 @@ impl PyTuple {
}
impl AsMapping for PyTuple {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
Ok(PyMapping {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
Ok(PyMappingMethods {
length: Some(Self::length),
subscript: Some(Self::subscript),
ass_subscript: None,
+120 -21
View File
@@ -1,36 +1,135 @@
//! Mapping protocol
use crate::{vm::VirtualMachine, PyObjectRef, PyResult, TryFromBorrowedObject, TypeProtocol};
use crate::{
builtins::dict::{PyDictKeys, PyDictRef, PyDictValues},
builtins::list::PyList,
vm::VirtualMachine,
IdProtocol, IntoPyObject, PyObjectRef, PyResult, TryFromObject, TypeProtocol,
};
use std::borrow::Borrow;
use std::ops::Deref;
// Mapping protocol
// https://docs.python.org/3/c-api/mapping.html
#[allow(clippy::type_complexity)]
pub struct PyMapping {
#[derive(Default)]
pub struct PyMappingMethods {
pub length: Option<fn(PyObjectRef, &VirtualMachine) -> PyResult<usize>>,
pub subscript: Option<fn(PyObjectRef, PyObjectRef, &VirtualMachine) -> PyResult>,
pub ass_subscript:
Option<fn(PyObjectRef, PyObjectRef, Option<PyObjectRef>, &VirtualMachine) -> PyResult<()>>,
}
impl PyMapping {
pub fn check(cls: &PyObjectRef, vm: &VirtualMachine) -> bool {
if let Ok(mapping) = PyMapping::try_from_borrowed_object(vm, cls) {
mapping.subscript.is_some()
} else {
false
#[derive(Debug, Clone)]
#[repr(transparent)]
pub struct PyMapping<T = PyObjectRef>(T)
where
T: Borrow<PyObjectRef>;
impl PyMapping<PyObjectRef> {
pub fn into_object(self) -> PyObjectRef {
self.0
}
pub fn check(obj: &PyObjectRef) -> bool {
obj.class()
.mro_find_map(|x| x.slots.as_mapping.load())
.is_some()
}
pub fn methods(&self, vm: &VirtualMachine) -> PyMappingMethods {
let obj_cls = self.0.class();
for cls in obj_cls.iter_mro() {
if let Some(f) = cls.slots.as_mapping.load() {
return f(&self.0, vm).unwrap();
}
}
PyMappingMethods::default()
}
}
impl TryFromBorrowedObject for PyMapping {
fn try_from_borrowed_object(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<Self> {
let obj_cls = obj.class();
for cls in obj_cls.iter_mro() {
if let Some(f) = cls.slots.as_mapping.load() {
return f(obj, vm);
}
impl<T> PyMapping<T>
where
T: Borrow<PyObjectRef>,
{
pub fn new(obj: T) -> Self {
Self(obj)
}
pub fn keys(&self, vm: &VirtualMachine) -> PyResult {
if self.0.borrow().is(&vm.ctx.types.dict_type) {
Ok(
PyDictKeys::new(PyDictRef::try_from_object(vm, self.0.borrow().clone())?)
.into_pyobject(vm),
)
} else {
Self::method_output_as_list(self.0.borrow(), "keys", vm)
}
}
pub fn values(&self, vm: &VirtualMachine) -> PyResult {
if self.0.borrow().is(&vm.ctx.types.dict_type) {
Ok(
PyDictValues::new(PyDictRef::try_from_object(vm, self.0.borrow().clone())?)
.into_pyobject(vm),
)
} else {
Self::method_output_as_list(self.0.borrow(), "values", vm)
}
}
fn method_output_as_list(
obj: &PyObjectRef,
method_name: &str,
vm: &VirtualMachine,
) -> PyResult {
let meth_output = vm.call_method(obj, method_name, ())?;
if meth_output.is(&vm.ctx.types.list_type) {
return Ok(meth_output);
}
let iter = meth_output.clone().get_iter(vm).map_err(|_| {
vm.new_type_error(format!(
"{}.{}() returned a non-iterable (type {})",
obj.class(),
method_name,
meth_output.class()
))
})?;
Ok(PyList::from(vm.extract_elements(&iter)?).into_pyobject(vm))
}
}
impl<T> Borrow<PyObjectRef> for PyMapping<T>
where
T: Borrow<PyObjectRef>,
{
fn borrow(&self) -> &PyObjectRef {
self.0.borrow()
}
}
impl<T> Deref for PyMapping<T>
where
T: Borrow<PyObjectRef>,
{
type Target = PyObjectRef;
fn deref(&self) -> &Self::Target {
self.0.borrow()
}
}
impl IntoPyObject for PyMapping<PyObjectRef> {
fn into_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
self.into_object()
}
}
impl TryFromObject for PyMapping<PyObjectRef> {
fn try_from_object(vm: &VirtualMachine, mapping: PyObjectRef) -> PyResult<Self> {
if Self::check(&mapping) {
Ok(Self::new(mapping))
} else {
Err(vm.new_type_error(format!("{} is not a mapping object", mapping.class())))
}
Err(vm.new_type_error(format!(
"a dict-like object is required, not '{}'",
obj_cls.name()
)))
}
}
+1 -1
View File
@@ -4,4 +4,4 @@ mod mapping;
pub use buffer::{BufferInternal, BufferOptions, PyBuffer, ResizeGuard};
pub use iter::{PyIter, PyIterReturn};
pub(crate) use mapping::PyMapping;
pub use mapping::{PyMapping, PyMappingMethods};
+6 -6
View File
@@ -640,8 +640,8 @@ where
T: IntoPyObject,
{
fn get_item(&self, key: T, vm: &VirtualMachine) -> PyResult {
if let Ok(map) = PyMapping::try_from_borrowed_object(vm, self) {
if let Some(getitem) = map.subscript {
if let Ok(mapping) = PyMapping::try_from_object(vm, self.clone()) {
if let Some(getitem) = mapping.methods(vm).subscript {
return getitem(self.clone(), key.into_pyobject(vm), vm);
}
}
@@ -663,8 +663,8 @@ where
}
fn set_item(&self, key: T, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
if let Ok(map) = PyMapping::try_from_borrowed_object(vm, self) {
if let Some(setitem) = map.ass_subscript {
if let Ok(mapping) = PyMapping::try_from_object(vm, self.clone()) {
if let Some(setitem) = mapping.methods(vm).ass_subscript {
return setitem(self.clone(), key.into_pyobject(vm), Some(value), vm);
}
}
@@ -681,8 +681,8 @@ where
}
fn del_item(&self, key: T, vm: &VirtualMachine) -> PyResult<()> {
if let Ok(map) = PyMapping::try_from_borrowed_object(vm, self) {
if let Some(setitem) = map.ass_subscript {
if let Ok(mapping) = PyMapping::try_from_object(vm, self.clone()) {
if let Some(setitem) = mapping.methods(vm).ass_subscript {
return setitem(self.clone(), key.into_pyobject(vm), None, vm);
}
}
+4 -4
View File
@@ -2,7 +2,7 @@ use crate::builtins::{PyStrRef, PyTypeRef};
use crate::common::hash::PyHash;
use crate::common::lock::PyRwLock;
use crate::function::{FromArgs, FuncArgs, OptionalArg};
use crate::protocol::{PyBuffer, PyIterReturn, PyMapping};
use crate::protocol::{PyBuffer, PyIterReturn, PyMappingMethods};
use crate::utils::Either;
use crate::VirtualMachine;
use crate::{
@@ -70,7 +70,7 @@ pub(crate) type GetattroFunc = fn(PyObjectRef, PyStrRef, &VirtualMachine) -> PyR
pub(crate) type SetattroFunc =
fn(&PyObjectRef, PyStrRef, Option<PyObjectRef>, &VirtualMachine) -> PyResult<()>;
pub(crate) type BufferFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult<PyBuffer>;
pub(crate) type MappingFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult<PyMapping>;
pub(crate) type MappingFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult<PyMappingMethods>;
pub(crate) type IterFunc = fn(PyObjectRef, &VirtualMachine) -> PyResult;
pub(crate) type IterNextFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult<PyIterReturn>;
@@ -536,7 +536,7 @@ pub trait AsBuffer: PyValue {
#[pyimpl]
pub trait AsMapping: PyValue {
#[pyslot]
fn slot_as_mapping(zelf: &PyObjectRef, vm: &VirtualMachine) -> PyResult<PyMapping> {
fn slot_as_mapping(zelf: &PyObjectRef, vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
let zelf = zelf
.downcast_ref()
.ok_or_else(|| vm.new_type_error("unexpected payload for as_mapping".to_owned()))?;
@@ -563,7 +563,7 @@ pub trait AsMapping: PyValue {
})
}
fn as_mapping(zelf: &PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyMapping>;
fn as_mapping(zelf: &PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyMappingMethods>;
fn length(zelf: PyObjectRef, _vm: &VirtualMachine) -> PyResult<usize>;