Skip to content

Commit af7b112

Browse files
committed
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>
1 parent 4fd0681 commit af7b112

File tree

14 files changed

+164
-54
lines changed

14 files changed

+164
-54
lines changed

stdlib/src/array.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ mod array {
1717
},
1818
class_or_notimplemented,
1919
function::{ArgBytesLike, ArgIterable, OptionalArg},
20-
protocol::{BufferInternal, BufferOptions, PyBuffer, PyIterReturn, PyMapping, ResizeGuard},
20+
protocol::{
21+
BufferInternal, BufferOptions, PyBuffer, PyIterReturn, PyMappingMethods, ResizeGuard,
22+
},
2123
sliceable::{saturate_index, PySliceableSequence, PySliceableSequenceMut, SequenceIndex},
2224
slots::{
2325
AsBuffer, AsMapping, Comparable, Iterable, IteratorIterable, PyComparisonOp,
@@ -1165,8 +1167,8 @@ mod array {
11651167
}
11661168

11671169
impl AsMapping for PyArray {
1168-
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
1169-
Ok(PyMapping {
1170+
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
1171+
Ok(PyMappingMethods {
11701172
length: Some(Self::length),
11711173
subscript: Some(Self::subscript),
11721174
ass_subscript: Some(Self::ass_subscript),

vm/src/builtins/bytearray.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@ use crate::{
1515
ByteInnerTranslateOptions, DecodeArgs, PyBytesInner,
1616
},
1717
function::{ArgBytesLike, ArgIterable, FuncArgs, OptionalArg, OptionalOption},
18-
protocol::{BufferInternal, BufferOptions, PyBuffer, PyIterReturn, PyMapping, ResizeGuard},
18+
protocol::{
19+
BufferInternal, BufferOptions, PyBuffer, PyIterReturn, PyMappingMethods, ResizeGuard,
20+
},
1921
sliceable::{PySliceableSequence, PySliceableSequenceMut, SequenceIndex},
2022
slots::{
2123
AsBuffer, AsMapping, Callable, Comparable, Hashable, Iterable, IteratorIterable,
@@ -716,8 +718,8 @@ impl<'a> ResizeGuard<'a> for PyByteArray {
716718
}
717719

718720
impl AsMapping for PyByteArray {
719-
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
720-
Ok(PyMapping {
721+
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
722+
Ok(PyMappingMethods {
721723
length: Some(Self::length),
722724
subscript: Some(Self::subscript),
723725
ass_subscript: Some(Self::ass_subscript),

vm/src/builtins/bytes.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
},
88
common::hash::PyHash,
99
function::{ArgBytesLike, ArgIterable, OptionalArg, OptionalOption},
10-
protocol::{BufferInternal, BufferOptions, PyBuffer, PyIterReturn, PyMapping},
10+
protocol::{BufferInternal, BufferOptions, PyBuffer, PyIterReturn, PyMappingMethods},
1111
slots::{
1212
AsBuffer, AsMapping, Callable, Comparable, Hashable, Iterable, IteratorIterable,
1313
PyComparisonOp, SlotConstructor, SlotIterator,
@@ -541,8 +541,8 @@ impl BufferInternal for PyRef<PyBytes> {
541541
}
542542

543543
impl AsMapping for PyBytes {
544-
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
545-
Ok(PyMapping {
544+
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
545+
Ok(PyMappingMethods {
546546
length: Some(Self::length),
547547
subscript: Some(Self::subscript),
548548
ass_subscript: None,

vm/src/builtins/dict.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::{
44
common::ascii,
55
dictdatatype::{self, DictKey},
66
function::{ArgIterable, FuncArgs, KwArgs, OptionalArg},
7-
protocol::{PyIterReturn, PyMapping},
7+
protocol::{PyIterReturn, PyMappingMethods},
88
slots::{
99
AsMapping, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, SlotIterator,
1010
Unhashable,
@@ -412,8 +412,8 @@ impl PyDict {
412412
}
413413

414414
impl AsMapping for PyDict {
415-
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
416-
Ok(PyMapping {
415+
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
416+
Ok(PyMappingMethods {
417417
length: Some(Self::length),
418418
subscript: Some(Self::subscript),
419419
ass_subscript: Some(Self::ass_subscript),
@@ -685,7 +685,7 @@ macro_rules! dict_iterator {
685685
}
686686

687687
impl $name {
688-
fn new(dict: PyDictRef) -> Self {
688+
pub fn new(dict: PyDictRef) -> Self {
689689
$name { dict }
690690
}
691691
}

vm/src/builtins/list.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::common::lock::{
88
};
99
use crate::{
1010
function::{ArgIterable, FuncArgs, OptionalArg},
11-
protocol::{PyIterReturn, PyMapping},
11+
protocol::{PyIterReturn, PyMappingMethods},
1212
sequence::{self, SimpleSeq},
1313
sliceable::{PySliceableSequence, PySliceableSequenceMut, SequenceIndex},
1414
slots::{
@@ -418,8 +418,8 @@ impl PyList {
418418
}
419419

420420
impl AsMapping for PyList {
421-
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
422-
Ok(PyMapping {
421+
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
422+
Ok(PyMappingMethods {
423423
length: Some(Self::length),
424424
subscript: Some(Self::subscript),
425425
ass_subscript: Some(Self::ass_subscript),

vm/src/builtins/mappingproxy.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use super::{PyDict, PyList, PyStrRef, PyTuple, PyTypeRef};
22
use crate::{
33
function::OptionalArg,
4-
protocol::PyMapping,
4+
protocol::{PyMapping, PyMappingMethods},
55
slots::{AsMapping, Iterable, SlotConstructor},
66
IntoPyObject, ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
77
TryFromObject, TypeProtocol, VirtualMachine,
@@ -140,8 +140,8 @@ impl PyMappingProxy {
140140
}
141141

142142
impl AsMapping for PyMappingProxy {
143-
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
144-
Ok(PyMapping {
143+
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
144+
Ok(PyMappingMethods {
145145
length: None,
146146
subscript: Some(Self::subscript),
147147
ass_subscript: None,

vm/src/builtins/memory.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::common::{
88
use crate::{
99
bytesinner::bytes_to_hex,
1010
function::{FuncArgs, OptionalArg},
11-
protocol::{BufferInternal, BufferOptions, PyBuffer, PyMapping},
11+
protocol::{BufferInternal, BufferOptions, PyBuffer, PyMappingMethods},
1212
sliceable::{convert_slice, wrap_index, SequenceIndex},
1313
slots::{AsBuffer, AsMapping, Comparable, Hashable, PyComparisonOp, SlotConstructor},
1414
stdlib::pystruct::FormatSpec,
@@ -735,8 +735,8 @@ impl BufferInternal for PyRef<PyMemoryView> {
735735
}
736736

737737
impl AsMapping for PyMemoryView {
738-
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
739-
Ok(PyMapping {
738+
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
739+
Ok(PyMappingMethods {
740740
length: Some(Self::length),
741741
subscript: Some(Self::subscript),
742742
ass_subscript: Some(Self::ass_subscript),

vm/src/builtins/pytype.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use super::{
55
use crate::common::{ascii, lock::PyRwLock};
66
use crate::{
77
function::{FuncArgs, KwArgs, OptionalArg},
8-
protocol::{PyIterReturn, PyMapping},
8+
protocol::{PyIterReturn, PyMappingMethods},
99
slots::{self, Callable, PyTypeFlags, PyTypeSlots, SlotGetattro, SlotSetattro},
1010
utils::Either,
1111
IdProtocol, PyAttributes, PyClassImpl, PyContext, PyLease, PyObjectRef, PyRef, PyResult,
@@ -283,7 +283,7 @@ impl PyType {
283283
}
284284

285285
let func: slots::MappingFunc = |zelf, _vm| {
286-
Ok(PyMapping {
286+
Ok(PyMappingMethods {
287287
length: then_some_closure!(zelf.has_class_attr("__len__"), |zelf, vm| {
288288
vm.call_special_method(zelf, "__len__", ()).map(|obj| {
289289
obj.payload_if_subclass::<PyInt>(vm)

vm/src/builtins/range.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::{PyInt, PyIntRef, PySlice, PySliceRef, PyTypeRef};
22
use crate::common::hash::PyHash;
33
use crate::{
44
function::{FuncArgs, OptionalArg},
5-
protocol::{PyIterReturn, PyMapping},
5+
protocol::{PyIterReturn, PyMappingMethods},
66
slots::{
77
AsMapping, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, SlotIterator,
88
},
@@ -375,8 +375,8 @@ impl PyRange {
375375
}
376376

377377
impl AsMapping for PyRange {
378-
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
379-
Ok(PyMapping {
378+
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
379+
Ok(PyMappingMethods {
380380
length: Some(Self::length),
381381
subscript: Some(Self::subscript),
382382
ass_subscript: None,

vm/src/builtins/tuple.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use super::{
66
use crate::common::hash::PyHash;
77
use crate::{
88
function::OptionalArg,
9-
protocol::{PyIterReturn, PyMapping},
9+
protocol::{PyIterReturn, PyMappingMethods},
1010
sequence::{self, SimpleSeq},
1111
sliceable::PySliceableSequence,
1212
slots::{
@@ -288,8 +288,8 @@ impl PyTuple {
288288
}
289289

290290
impl AsMapping for PyTuple {
291-
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMapping> {
292-
Ok(PyMapping {
291+
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
292+
Ok(PyMappingMethods {
293293
length: Some(Self::length),
294294
subscript: Some(Self::subscript),
295295
ass_subscript: None,

vm/src/protocol/mapping.rs

+121-15
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,24 @@
1-
//! Mapping protocol
2-
3-
use crate::{vm::VirtualMachine, PyObjectRef, PyResult, TryFromBorrowedObject, TypeProtocol};
1+
use crate::{
2+
builtins::dict::{PyDictKeys, PyDictRef, PyDictValues},
3+
builtins::list::PyList,
4+
vm::VirtualMachine,
5+
IdProtocol, IntoPyObject, PyObjectRef, PyResult, TryFromBorrowedObject, TryFromObject,
6+
TypeProtocol,
7+
};
8+
use std::borrow::Borrow;
9+
use std::ops::Deref;
410

11+
// Mapping protocol
12+
// https://docs.python.org/3/c-api/mapping.html
513
#[allow(clippy::type_complexity)]
6-
pub struct PyMapping {
14+
pub struct PyMappingMethods {
715
pub length: Option<fn(PyObjectRef, &VirtualMachine) -> PyResult<usize>>,
816
pub subscript: Option<fn(PyObjectRef, PyObjectRef, &VirtualMachine) -> PyResult>,
917
pub ass_subscript:
1018
Option<fn(PyObjectRef, PyObjectRef, Option<PyObjectRef>, &VirtualMachine) -> PyResult<()>>,
1119
}
1220

13-
impl PyMapping {
14-
pub fn check(cls: &PyObjectRef, vm: &VirtualMachine) -> bool {
15-
if let Ok(mapping) = PyMapping::try_from_borrowed_object(vm, cls) {
16-
mapping.subscript.is_some()
17-
} else {
18-
false
19-
}
20-
}
21-
}
22-
23-
impl TryFromBorrowedObject for PyMapping {
21+
impl TryFromBorrowedObject for PyMappingMethods {
2422
fn try_from_borrowed_object(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<Self> {
2523
let obj_cls = obj.class();
2624
for cls in obj_cls.iter_mro() {
@@ -34,3 +32,111 @@ impl TryFromBorrowedObject for PyMapping {
3432
)))
3533
}
3634
}
35+
36+
#[derive(Debug, Clone)]
37+
#[repr(transparent)]
38+
pub struct PyMapping<T = PyObjectRef>(T)
39+
where
40+
T: Borrow<PyObjectRef>;
41+
42+
impl PyMapping<PyObjectRef> {
43+
pub fn into_object(self) -> PyObjectRef {
44+
self.0
45+
}
46+
47+
pub fn check(obj: &PyObjectRef, vm: &VirtualMachine) -> bool {
48+
if let Ok(mapping) = PyMappingMethods::try_from_borrowed_object(vm, obj) {
49+
mapping.subscript.is_some()
50+
} else {
51+
false
52+
}
53+
}
54+
}
55+
56+
impl<T> PyMapping<T>
57+
where
58+
T: Borrow<PyObjectRef>,
59+
{
60+
pub fn new(obj: T) -> Self {
61+
Self(obj)
62+
}
63+
64+
pub fn keys(&self, vm: &VirtualMachine) -> PyResult {
65+
if self.0.borrow().is(&vm.ctx.types.dict_type) {
66+
Ok(
67+
PyDictKeys::new(PyDictRef::try_from_object(vm, self.0.borrow().clone())?)
68+
.into_pyobject(vm),
69+
)
70+
} else {
71+
Self::method_output_as_list(self.0.borrow(), "keys", vm)
72+
}
73+
}
74+
75+
pub fn values(&self, vm: &VirtualMachine) -> PyResult {
76+
if self.0.borrow().is(&vm.ctx.types.dict_type) {
77+
Ok(
78+
PyDictValues::new(PyDictRef::try_from_object(vm, self.0.borrow().clone())?)
79+
.into_pyobject(vm),
80+
)
81+
} else {
82+
Self::method_output_as_list(self.0.borrow(), "values", vm)
83+
}
84+
}
85+
86+
fn method_output_as_list(
87+
obj: &PyObjectRef,
88+
method_name: &str,
89+
vm: &VirtualMachine,
90+
) -> PyResult {
91+
let meth_output = vm.call_method(obj, method_name, ())?;
92+
if meth_output.is(&vm.ctx.types.list_type) {
93+
return Ok(meth_output);
94+
}
95+
96+
let iter = meth_output.clone().get_iter(vm).map_err(|_| {
97+
vm.new_type_error(format!(
98+
"{}.{}() returned a non-iterable (type {})",
99+
obj.class(),
100+
method_name,
101+
meth_output.class()
102+
))
103+
})?;
104+
105+
Ok(PyList::from(vm.extract_elements(&iter)?).into_pyobject(vm))
106+
}
107+
}
108+
109+
impl<T> Borrow<PyObjectRef> for PyMapping<T>
110+
where
111+
T: Borrow<PyObjectRef>,
112+
{
113+
fn borrow(&self) -> &PyObjectRef {
114+
self.0.borrow()
115+
}
116+
}
117+
118+
impl<T> Deref for PyMapping<T>
119+
where
120+
T: Borrow<PyObjectRef>,
121+
{
122+
type Target = PyObjectRef;
123+
fn deref(&self) -> &Self::Target {
124+
self.0.borrow()
125+
}
126+
}
127+
128+
impl IntoPyObject for PyMapping<PyObjectRef> {
129+
fn into_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
130+
self.into_object()
131+
}
132+
}
133+
134+
impl TryFromObject for PyMapping<PyObjectRef> {
135+
fn try_from_object(vm: &VirtualMachine, mapping: PyObjectRef) -> PyResult<Self> {
136+
if Self::check(&mapping, vm) {
137+
Ok(Self::new(mapping))
138+
} else {
139+
Err(vm.new_type_error(format!("{} is not a mapping object", mapping.class())))
140+
}
141+
}
142+
}

vm/src/protocol/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ mod mapping;
44

55
pub use buffer::{BufferInternal, BufferOptions, PyBuffer, ResizeGuard};
66
pub use iter::{PyIter, PyIterReturn};
7-
pub(crate) use mapping::PyMapping;
7+
pub(crate) use mapping::{PyMapping, PyMappingMethods};

0 commit comments

Comments
 (0)