Merge pull request #3121 from Snowapril/mapping-protocol

Implement Mapping Protocol
This commit is contained in:
Jeong YunWon
2021-10-02 18:00:17 +09:00
committed by GitHub
16 changed files with 673 additions and 98 deletions

View File

@@ -17,11 +17,13 @@ mod array {
},
class_or_notimplemented,
function::{ArgBytesLike, ArgIterable, OptionalArg},
protocol::{BufferInternal, BufferOptions, PyBuffer, PyIterReturn, ResizeGuard},
protocol::{
BufferInternal, BufferOptions, PyBuffer, PyIterReturn, PyMappingMethods, ResizeGuard,
},
sliceable::{saturate_index, PySliceableSequence, PySliceableSequenceMut, SequenceIndex},
slots::{
AsBuffer, Comparable, Iterable, IteratorIterable, PyComparisonOp, SlotConstructor,
SlotIterator,
AsBuffer, AsMapping, Comparable, Iterable, IteratorIterable, PyComparisonOp,
SlotConstructor, SlotIterator,
},
IdProtocol, IntoPyObject, IntoPyResult, PyComparisonValue, PyObjectRef, PyRef, PyResult,
PyValue, TryFromObject, TypeProtocol, VirtualMachine,
@@ -665,7 +667,10 @@ mod array {
}
}
#[pyimpl(flags(BASETYPE), with(Comparable, AsBuffer, Iterable, SlotConstructor))]
#[pyimpl(
flags(BASETYPE),
with(Comparable, AsBuffer, AsMapping, Iterable, SlotConstructor)
)]
impl PyArray {
fn read(&self) -> PyRwLockReadGuard<'_, ArrayContentType> {
self.array.read()
@@ -1161,6 +1166,38 @@ mod array {
}
}
impl AsMapping for PyArray {
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),
})
}
fn length(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
Self::downcast_ref(&zelf, vm).map(|zelf| Ok(zelf.len()))?
}
fn subscript(zelf: PyObjectRef, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Self::downcast_ref(&zelf, vm).map(|zelf| zelf.getitem(needle, vm))?
}
fn ass_subscript(
zelf: PyObjectRef,
needle: PyObjectRef,
value: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
match value {
Some(value) => {
Self::downcast(zelf, vm).map(|zelf| Self::setitem(zelf, needle, value, vm))?
}
None => Self::downcast(zelf, vm).map(|zelf| Self::delitem(zelf, needle, vm))?,
}
}
}
impl Iterable for PyArray {
fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
Ok(PyArrayIter {

View File

@@ -17,11 +17,13 @@ use crate::{
ByteInnerTranslateOptions, DecodeArgs, PyBytesInner,
},
function::{ArgBytesLike, ArgIterable, FuncArgs, OptionalArg, OptionalOption},
protocol::{BufferInternal, BufferOptions, PyBuffer, PyIterReturn, ResizeGuard},
protocol::{
BufferInternal, BufferOptions, PyBuffer, PyIterReturn, PyMappingMethods, ResizeGuard,
},
sliceable::{PySliceableSequence, PySliceableSequenceMut, SequenceIndex},
slots::{
AsBuffer, Callable, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp,
SlotIterator, Unhashable,
AsBuffer, AsMapping, Callable, Comparable, Hashable, Iterable, IteratorIterable,
PyComparisonOp, SlotIterator, Unhashable,
},
utils::Either,
IdProtocol, IntoPyObject, PyClassDef, PyClassImpl, PyComparisonValue, PyContext, PyObjectRef,
@@ -97,7 +99,10 @@ pub(crate) fn init(context: &PyContext) {
PyByteArrayIterator::extend_class(context, &context.types.bytearray_iterator_type);
}
#[pyimpl(flags(BASETYPE), with(Hashable, Comparable, AsBuffer, Iterable))]
#[pyimpl(
flags(BASETYPE),
with(Hashable, Comparable, AsBuffer, AsMapping, Iterable)
)]
impl PyByteArray {
#[pyslot]
fn slot_new(cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
@@ -203,9 +208,9 @@ impl PyByteArray {
}
#[pymethod(magic)]
pub fn delitem(&self, needle: SequenceIndex, vm: &VirtualMachine) -> PyResult<()> {
pub fn delitem(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
let elements = &mut self.try_resizable(vm)?.elements;
match needle {
match SequenceIndex::try_from_object_for(vm, needle, Self::NAME)? {
SequenceIndex::Int(int) => {
if let Some(idx) = elements.wrap_index(int) {
elements.remove(idx);
@@ -714,6 +719,41 @@ impl<'a> ResizeGuard<'a> for PyByteArray {
}
}
impl AsMapping for PyByteArray {
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),
})
}
#[inline]
fn length(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
Self::downcast_ref(&zelf, vm).map(|zelf| Ok(zelf.len()))?
}
#[inline]
fn subscript(zelf: PyObjectRef, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Self::downcast_ref(&zelf, vm).map(|zelf| zelf.getitem(needle, vm))?
}
#[inline]
fn ass_subscript(
zelf: PyObjectRef,
needle: PyObjectRef,
value: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
match value {
Some(value) => {
Self::downcast(zelf, vm).map(|zelf| Self::setitem(zelf, needle, value, vm))
}
None => Self::downcast_ref(&zelf, vm).map(|zelf| zelf.delitem(needle, vm)),
}?
}
}
impl Unhashable for PyByteArray {}
impl Iterable for PyByteArray {

View File

@@ -7,10 +7,10 @@ use crate::{
},
common::hash::PyHash,
function::{ArgBytesLike, ArgIterable, OptionalArg, OptionalOption},
protocol::{BufferInternal, BufferOptions, PyBuffer, PyIterReturn},
protocol::{BufferInternal, BufferOptions, PyBuffer, PyIterReturn, PyMappingMethods},
slots::{
AsBuffer, Callable, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp,
SlotConstructor, SlotIterator,
AsBuffer, AsMapping, Callable, Comparable, Hashable, Iterable, IteratorIterable,
PyComparisonOp, SlotConstructor, SlotIterator,
},
utils::Either,
IdProtocol, IntoPyObject, IntoPyResult, PyClassImpl, PyComparisonValue, PyContext, PyObjectRef,
@@ -105,7 +105,7 @@ impl SlotConstructor for PyBytes {
#[pyimpl(
flags(BASETYPE),
with(Hashable, Comparable, AsBuffer, Iterable, SlotConstructor)
with(AsMapping, Hashable, Comparable, AsBuffer, Iterable, SlotConstructor)
)]
impl PyBytes {
#[pymethod(magic)]
@@ -542,6 +542,36 @@ impl BufferInternal for PyRef<PyBytes> {
fn retain(&self) {}
}
impl AsMapping for PyBytes {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
Ok(PyMappingMethods {
length: Some(Self::length),
subscript: Some(Self::subscript),
ass_subscript: None,
})
}
#[inline]
fn length(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
Self::downcast_ref(&zelf, vm).map(|zelf| Ok(zelf.len()))?
}
#[inline]
fn subscript(zelf: PyObjectRef, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Self::downcast_ref(&zelf, vm).map(|zelf| zelf.getitem(needle, vm))?
}
#[cold]
fn ass_subscript(
zelf: PyObjectRef,
_needle: PyObjectRef,
_value: Option<PyObjectRef>,
_vm: &VirtualMachine,
) -> PyResult<()> {
unreachable!("ass_subscript not implemented for {}", zelf.class())
}
}
impl Hashable for PyBytes {
fn hash(zelf: &PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
Ok(zelf.inner.hash(vm))

View File

@@ -4,15 +4,16 @@ use crate::{
common::ascii,
dictdatatype::{self, DictKey},
function::{ArgIterable, FuncArgs, KwArgs, OptionalArg},
protocol::PyIterReturn,
protocol::{PyIterReturn, PyMappingMethods},
slots::{
Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, SlotIterator, Unhashable,
AsMapping, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, SlotIterator,
Unhashable,
},
vm::{ReprGuard, VirtualMachine},
IdProtocol, IntoPyObject, ItemProtocol,
PyArithmeticValue::*,
PyAttributes, PyClassDef, PyClassImpl, PyComparisonValue, PyContext, PyObjectRef, PyRef,
PyResult, PyValue, TryFromObject, TypeProtocol,
PyResult, PyValue, TypeProtocol,
};
use rustpython_common::lock::PyMutex;
use std::fmt;
@@ -51,7 +52,7 @@ impl PyValue for PyDict {
// Python dict methods:
#[allow(clippy::len_without_is_empty)]
#[pyimpl(with(Hashable, Comparable, Iterable), flags(BASETYPE))]
#[pyimpl(with(AsMapping, Hashable, Comparable, Iterable), flags(BASETYPE))]
impl PyDict {
#[pyslot]
fn slot_new(cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
@@ -410,6 +411,39 @@ impl PyDict {
}
}
impl AsMapping for PyDict {
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),
})
}
#[inline]
fn length(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
Self::downcast_ref(&zelf, vm).map(|zelf| Ok(zelf.len()))?
}
#[inline]
fn subscript(zelf: PyObjectRef, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Self::downcast(zelf, vm).map(|zelf| Self::getitem(zelf, needle, vm))?
}
#[inline]
fn ass_subscript(
zelf: PyObjectRef,
needle: PyObjectRef,
value: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
Self::downcast_ref(&zelf, vm).map(|zelf| match value {
Some(value) => zelf.setitem(needle, value, vm),
None => zelf.delitem(needle, vm),
})?
}
}
impl Comparable for PyDict {
fn cmp(
zelf: &PyRef<Self>,
@@ -653,7 +687,7 @@ macro_rules! dict_iterator {
}
impl $name {
fn new(dict: PyDictRef) -> Self {
pub fn new(dict: PyDictRef) -> Self {
$name { dict }
}
}
@@ -897,26 +931,3 @@ pub(crate) fn init(context: &PyContext) {
PyDictItemIterator::extend_class(context, &context.types.dict_itemiterator_type);
PyDictReverseItemIterator::extend_class(context, &context.types.dict_reverseitemiterator_type);
}
pub struct PyMapping {
dict: PyDictRef,
}
impl TryFromObject for PyMapping {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
let dict = vm.ctx.new_dict();
PyDict::merge(
&dict.entries,
OptionalArg::Present(obj),
KwArgs::default(),
vm,
)?;
Ok(PyMapping { dict })
}
}
impl PyMapping {
pub fn into_dict(self) -> PyDictRef {
self.dict
}
}

View File

@@ -4,11 +4,12 @@ use crate::common::lock::{
};
use crate::{
function::{ArgIterable, FuncArgs, OptionalArg},
protocol::PyIterReturn,
protocol::{PyIterReturn, PyMappingMethods},
sequence::{self, SimpleSeq},
sliceable::{PySliceableSequence, PySliceableSequenceMut, SequenceIndex},
slots::{
Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, SlotIterator, Unhashable,
AsMapping, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, SlotIterator,
Unhashable,
},
utils::Either,
vm::{ReprGuard, VirtualMachine},
@@ -77,7 +78,7 @@ pub(crate) struct SortOptions {
pub type PyListRef = PyRef<PyList>;
#[pyimpl(with(Iterable, Hashable, Comparable), flags(BASETYPE))]
#[pyimpl(with(AsMapping, Iterable, Hashable, Comparable), flags(BASETYPE))]
impl PyList {
#[pymethod]
pub(crate) fn append(&self, x: PyObjectRef) {
@@ -341,8 +342,8 @@ impl PyList {
}
#[pymethod(magic)]
fn delitem(&self, subscript: SequenceIndex, vm: &VirtualMachine) -> PyResult<()> {
match subscript {
fn delitem(&self, subscript: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
match SequenceIndex::try_from_object_for(vm, subscript, Self::NAME)? {
SequenceIndex::Int(index) => self.delindex(index, vm),
SequenceIndex::Slice(slice) => self.delslice(slice, vm),
}
@@ -404,6 +405,39 @@ impl PyList {
}
}
impl AsMapping for PyList {
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),
})
}
#[inline]
fn length(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
Self::downcast_ref(&zelf, vm).map(|zelf| Ok(zelf.len()))?
}
#[inline]
fn subscript(zelf: PyObjectRef, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Self::downcast(zelf, vm).map(|zelf| Self::getitem(zelf, needle, vm))?
}
#[inline]
fn ass_subscript(
zelf: PyObjectRef,
needle: PyObjectRef,
value: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
Self::downcast_ref(&zelf, vm).map(|zelf| match value {
Some(value) => zelf.setitem(needle, value, vm),
None => zelf.delitem(needle, vm),
})?
}
}
impl Iterable for PyList {
fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
Ok(PyListIterator {

View File

@@ -1,9 +1,10 @@
use super::{PyDict, PyStrRef, PyTypeRef};
use super::{PyDict, PyList, PyStrRef, PyTuple, PyTypeRef};
use crate::{
function::OptionalArg,
slots::{Iterable, SlotConstructor},
protocol::{PyMapping, PyMappingMethods},
slots::{AsMapping, Iterable, SlotConstructor},
IntoPyObject, ItemProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
TryFromObject, VirtualMachine,
TryFromObject, TypeProtocol, VirtualMachine,
};
#[pyclass(module = false, name = "mappingproxy")]
@@ -36,14 +37,24 @@ impl SlotConstructor for PyMappingProxy {
type Args = PyObjectRef;
fn py_new(cls: PyTypeRef, mapping: Self::Args, vm: &VirtualMachine) -> PyResult {
Self {
mapping: MappingProxyInner::Dict(mapping),
if !PyMapping::check(&mapping)
|| 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)
}
.into_pyresult_with_type(vm, cls)
}
}
#[pyimpl(with(Iterable, SlotConstructor))]
#[pyimpl(with(AsMapping, Iterable, SlotConstructor))]
impl PyMappingProxy {
fn get_inner(&self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<PyObjectRef>> {
let opt = match &self.mapping {
@@ -127,6 +138,37 @@ impl PyMappingProxy {
}
}
}
impl AsMapping for PyMappingProxy {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
Ok(PyMappingMethods {
length: None,
subscript: Some(Self::subscript),
ass_subscript: None,
})
}
#[inline]
fn length(zelf: PyObjectRef, _vm: &VirtualMachine) -> PyResult<usize> {
unreachable!("length not implemented for {}", zelf.class())
}
#[inline]
fn subscript(zelf: PyObjectRef, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Self::downcast_ref(&zelf, vm).map(|zelf| zelf.getitem(needle, vm))?
}
#[cold]
fn ass_subscript(
zelf: PyObjectRef,
_needle: PyObjectRef,
_value: Option<PyObjectRef>,
_vm: &VirtualMachine,
) -> PyResult<()> {
unreachable!("ass_subscript not implemented for {}", zelf.class())
}
}
impl Iterable for PyMappingProxy {
fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
let obj = match &zelf.mapping {

View File

@@ -8,13 +8,13 @@ use crate::common::{
use crate::{
bytesinner::bytes_to_hex,
function::{FuncArgs, OptionalArg},
protocol::{BufferInternal, BufferOptions, PyBuffer},
protocol::{BufferInternal, BufferOptions, PyBuffer, PyMappingMethods},
sliceable::{convert_slice, wrap_index, SequenceIndex},
slots::{AsBuffer, Comparable, Hashable, PyComparisonOp, SlotConstructor},
slots::{AsBuffer, AsMapping, Comparable, Hashable, PyComparisonOp, SlotConstructor},
stdlib::pystruct::FormatSpec,
utils::Either,
IdProtocol, IntoPyObject, PyClassImpl, PyComparisonValue, PyContext, PyObjectRef, PyRef,
PyResult, PyValue, TryFromBorrowedObject, TryFromObject, TypeProtocol, VirtualMachine,
IdProtocol, IntoPyObject, PyClassDef, PyClassImpl, PyComparisonValue, PyContext, PyObjectRef,
PyRef, PyResult, PyValue, TryFromBorrowedObject, TryFromObject, TypeProtocol, VirtualMachine,
};
use crossbeam_utils::atomic::AtomicCell;
use itertools::Itertools;
@@ -53,7 +53,7 @@ impl SlotConstructor for PyMemoryView {
}
}
#[pyimpl(with(Hashable, Comparable, AsBuffer, SlotConstructor))]
#[pyimpl(with(Hashable, Comparable, AsBuffer, AsMapping, SlotConstructor))]
impl PyMemoryView {
#[cfg(debug_assertions)]
fn validate(self) -> Self {
@@ -342,10 +342,10 @@ impl PyMemoryView {
}
#[pymethod(magic)]
fn getitem(zelf: PyRef<Self>, needle: SequenceIndex, vm: &VirtualMachine) -> PyResult {
fn getitem(zelf: PyRef<Self>, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
zelf.try_not_released(vm)?;
match needle {
SequenceIndex::Int(i) => Self::getitem_by_idx(zelf, i, vm),
match SequenceIndex::try_from_object_for(vm, needle, Self::NAME)? {
SequenceIndex::Int(index) => Self::getitem_by_idx(zelf, index, vm),
SequenceIndex::Slice(slice) => Self::getitem_by_slice(zelf, slice, vm),
}
}
@@ -459,7 +459,7 @@ impl PyMemoryView {
#[pymethod(magic)]
fn setitem(
zelf: PyRef<Self>,
needle: SequenceIndex,
needle: PyObjectRef,
value: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult<()> {
@@ -467,8 +467,8 @@ impl PyMemoryView {
if zelf.buffer.options.readonly {
return Err(vm.new_type_error("cannot modify read-only memory".to_owned()));
}
match needle {
SequenceIndex::Int(i) => Self::setitem_by_idx(zelf, i, value, vm),
match SequenceIndex::try_from_object_for(vm, needle, Self::NAME)? {
SequenceIndex::Int(index) => Self::setitem_by_idx(zelf, index, value, vm),
SequenceIndex::Slice(slice) => Self::setitem_by_slice(zelf, slice, value, vm),
}
}
@@ -734,6 +734,41 @@ impl BufferInternal for PyRef<PyMemoryView> {
fn retain(&self) {}
}
impl AsMapping for PyMemoryView {
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),
})
}
#[inline]
fn length(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
Self::downcast_ref(&zelf, vm).map(|zelf| zelf.len(vm))?
}
#[inline]
fn subscript(zelf: PyObjectRef, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Self::downcast(zelf, vm).map(|zelf| Self::getitem(zelf, needle, vm))?
}
#[inline]
fn ass_subscript(
zelf: PyObjectRef,
needle: PyObjectRef,
value: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()> {
match value {
Some(value) => {
Self::downcast(zelf, vm).map(|zelf| Self::setitem(zelf, needle, value, vm))?
}
None => Err(vm.new_type_error("cannot delete memory".to_owned())),
}
}
}
impl Comparable for PyMemoryView {
fn cmp(
zelf: &PyRef<Self>,

View File

@@ -5,13 +5,14 @@ use super::{
use crate::common::{ascii, lock::PyRwLock};
use crate::{
function::{FuncArgs, KwArgs, OptionalArg},
protocol::PyIterReturn,
protocol::{PyIterReturn, PyMappingMethods},
slots::{self, Callable, PyTypeFlags, PyTypeSlots, SlotGetattro, SlotSetattro},
utils::Either,
IdProtocol, PyAttributes, PyClassImpl, PyContext, PyLease, PyObjectRef, PyRef, PyResult,
PyValue, TryFromObject, TypeProtocol, VirtualMachine,
};
use itertools::Itertools;
use num_traits::ToPrimitive;
use std::collections::HashSet;
use std::fmt;
use std::ops::Deref;
@@ -270,6 +271,54 @@ impl PyType {
};
update_slot!(iternext, func);
}
"__len__" | "__getitem__" | "__setitem__" | "__delitem__" => {
macro_rules! then_some_closure {
($cond:expr, $closure:expr) => {
if $cond {
Some($closure)
} else {
None
}
};
}
let func: slots::MappingFunc = |zelf, _vm| {
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)
.map(|length_obj| {
length_obj.as_bigint().to_usize().ok_or_else(|| {
vm.new_value_error(
"__len__() should return >= 0".to_owned(),
)
})
})
.unwrap()
})?
}),
subscript: then_some_closure!(
zelf.has_class_attr("__getitem__"),
|zelf: PyObjectRef, needle: PyObjectRef, vm: &VirtualMachine| {
vm.call_special_method(zelf, "__getitem__", (needle,))
}
),
ass_subscript: then_some_closure!(
zelf.has_class_attr("__setitem__") | zelf.has_class_attr("__delitem__"),
|zelf, needle, value, vm| match value {
Some(value) => vm
.call_special_method(zelf, "__setitem__", (needle, value),)
.map(|_| Ok(()))?,
None => vm
.call_special_method(zelf, "__delitem__", (needle,))
.map(|_| Ok(()))?,
}
),
})
};
update_slot!(as_mapping, func);
// TODO: need to update sequence protocol too
}
_ => {}
}
}

View File

@@ -3,8 +3,10 @@ use crate::builtins::builtins_iter;
use crate::common::hash::PyHash;
use crate::{
function::{FuncArgs, OptionalArg},
protocol::PyIterReturn,
slots::{Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, SlotIterator},
protocol::{PyIterReturn, PyMappingMethods},
slots::{
AsMapping, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, SlotIterator,
},
IdProtocol, IntoPyRef, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue,
TryFromObject, TypeProtocol, VirtualMachine,
};
@@ -99,7 +101,7 @@ impl PyRange {
#[inline]
pub fn is_empty(&self) -> bool {
self.length().is_zero()
self.compute_length().is_zero()
}
#[inline]
@@ -117,7 +119,7 @@ impl PyRange {
}
if index.is_negative() {
let length = self.length();
let length = self.compute_length();
let index: BigInt = &length + index;
if index.is_negative() {
return None;
@@ -144,7 +146,7 @@ impl PyRange {
}
#[inline]
fn length(&self) -> BigInt {
fn compute_length(&self) -> BigInt {
let start = self.start.as_bigint();
let stop = self.stop.as_bigint();
let step = self.step.as_bigint();
@@ -174,7 +176,7 @@ pub fn init(context: &PyContext) {
PyRangeIterator::extend_class(context, &context.types.range_iterator_type);
}
#[pyimpl(with(Hashable, Comparable, Iterable))]
#[pyimpl(with(AsMapping, Hashable, Comparable, Iterable))]
impl PyRange {
fn new(cls: PyTypeRef, stop: PyIntRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
PyRange {
@@ -252,7 +254,7 @@ impl PyRange {
#[pymethod(magic)]
fn len(&self) -> BigInt {
self.length()
self.compute_length()
}
#[pymethod(magic)]
@@ -332,11 +334,11 @@ impl PyRange {
}
#[pymethod(magic)]
fn getitem(&self, subscript: RangeIndex, vm: &VirtualMachine) -> PyResult {
match subscript {
fn getitem(&self, subscript: PyObjectRef, vm: &VirtualMachine) -> PyResult {
match RangeIndex::try_from_object(vm, subscript)? {
RangeIndex::Slice(slice) => {
let (mut substart, mut substop, mut substep) =
slice.inner_indices(&self.length(), vm)?;
slice.inner_indices(&self.compute_length(), vm)?;
let range_step = &self.step;
let range_start = &self.start;
@@ -373,9 +375,39 @@ impl PyRange {
}
}
impl AsMapping for PyRange {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
Ok(PyMappingMethods {
length: Some(Self::length),
subscript: Some(Self::subscript),
ass_subscript: None,
})
}
#[inline]
fn length(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
Self::downcast_ref(&zelf, vm).map(|zelf| Ok(zelf.len().to_usize().unwrap()))?
}
#[inline]
fn subscript(zelf: PyObjectRef, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Self::downcast_ref(&zelf, vm).map(|zelf| zelf.getitem(needle, vm))?
}
#[inline]
fn ass_subscript(
zelf: PyObjectRef,
_needle: PyObjectRef,
_value: Option<PyObjectRef>,
_vm: &VirtualMachine,
) -> PyResult<()> {
unreachable!("ass_subscript not implemented for {}", zelf.class())
}
}
impl Hashable for PyRange {
fn hash(zelf: &PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
let length = zelf.length();
let length = zelf.compute_length();
let elements = if length.is_zero() {
[vm.ctx.new_int(length), vm.ctx.none(), vm.ctx.none()]
} else if length.is_one() {
@@ -407,8 +439,8 @@ impl Comparable for PyRange {
return Ok(true.into());
}
let rhs = class_or_notimplemented!(Self, other);
let lhs_len = zelf.length();
let eq = if lhs_len != rhs.length() {
let lhs_len = zelf.compute_length();
let eq = if lhs_len != rhs.compute_length() {
false
} else if lhs_len.is_zero() {
true

View File

@@ -2,12 +2,12 @@ use super::{PositionIterInternal, PyTypeRef};
use crate::common::hash::PyHash;
use crate::{
function::OptionalArg,
protocol::PyIterReturn,
protocol::{PyIterReturn, PyMappingMethods},
sequence::{self, SimpleSeq},
sliceable::PySliceableSequence,
slots::{
Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp, SlotConstructor,
SlotIterator,
AsMapping, Comparable, Hashable, Iterable, IteratorIterable, PyComparisonOp,
SlotConstructor, SlotIterator,
},
utils::Either,
vm::{ReprGuard, VirtualMachine},
@@ -107,7 +107,10 @@ impl SlotConstructor for PyTuple {
}
}
#[pyimpl(flags(BASETYPE), with(Hashable, Comparable, Iterable, SlotConstructor))]
#[pyimpl(
flags(BASETYPE),
with(AsMapping, Hashable, Comparable, Iterable, SlotConstructor)
)]
impl PyTuple {
/// Creating a new tuple with given boxed slice.
/// NOTE: for usual case, you probably want to use PyTupleRef::with_elements.
@@ -280,6 +283,36 @@ impl PyTuple {
}
}
impl AsMapping for PyTuple {
fn as_mapping(_zelf: &PyRef<Self>, _vm: &VirtualMachine) -> PyResult<PyMappingMethods> {
Ok(PyMappingMethods {
length: Some(Self::length),
subscript: Some(Self::subscript),
ass_subscript: None,
})
}
#[inline]
fn length(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<usize> {
Self::downcast_ref(&zelf, vm).map(|zelf| Ok(zelf.len()))?
}
#[inline]
fn subscript(zelf: PyObjectRef, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Self::downcast(zelf, vm).map(|zelf| Self::getitem(zelf, needle, vm))?
}
#[inline]
fn ass_subscript(
zelf: PyObjectRef,
_needle: PyObjectRef,
_value: Option<PyObjectRef>,
_vm: &VirtualMachine,
) -> PyResult<()> {
unreachable!("ass_subscript not implemented for {}", zelf.class())
}
}
impl Hashable for PyTuple {
fn hash(zelf: &PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyHash> {
crate::utils::hash_iter(zelf.elements.iter(), vm)

135
vm/src/protocol/mapping.rs Normal file
View File

@@ -0,0 +1,135 @@
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)]
#[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<()>>,
}
#[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<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())))
}
}
}

View File

@@ -1,5 +1,7 @@
mod buffer;
mod iter;
mod mapping;
pub use buffer::{BufferInternal, BufferOptions, PyBuffer, ResizeGuard};
pub use iter::{PyIter, PyIterReturn};
pub use mapping::{PyMapping, PyMappingMethods};

View File

@@ -20,6 +20,7 @@ use crate::{
dictdatatype::Dict,
exceptions,
function::{IntoFuncArgs, IntoPyNativeFunc},
protocol::PyMapping,
slots::{PyTypeFlags, PyTypeSlots},
types::{create_type_with_slots, TypeZoo},
VirtualMachine,
@@ -639,6 +640,12 @@ where
T: IntoPyObject,
{
fn get_item(&self, key: T, vm: &VirtualMachine) -> PyResult {
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);
}
}
match vm.get_special_method(self.clone(), "__getitem__")? {
Ok(special_method) => return special_method.invoke((key,), vm),
Err(obj) => {
@@ -656,6 +663,12 @@ where
}
fn set_item(&self, key: T, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
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);
}
}
vm.get_special_method(self.clone(), "__setitem__")?
.map_err(|obj| {
vm.new_type_error(format!(
@@ -668,6 +681,12 @@ where
}
fn del_item(&self, key: T, vm: &VirtualMachine) -> PyResult<()> {
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);
}
}
vm.get_special_method(self.clone(), "__delitem__")?
.map_err(|obj| {
vm.new_type_error(format!(

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};
use crate::protocol::{PyBuffer, PyIterReturn, PyMappingMethods};
use crate::utils::Either;
use crate::VirtualMachine;
use crate::{
@@ -70,6 +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<PyMappingMethods>;
pub(crate) type IterFunc = fn(PyObjectRef, &VirtualMachine) -> PyResult;
pub(crate) type IterNextFunc = fn(&PyObjectRef, &VirtualMachine) -> PyResult<PyIterReturn>;
@@ -85,7 +86,7 @@ pub struct PyTypeSlots {
// Method suites for standard classes
// tp_as_number
// tp_as_sequence
// tp_as_mapping
pub as_mapping: AtomicCell<Option<MappingFunc>>,
// More standard operations (here for binary compatibility)
pub hash: AtomicCell<Option<HashFunc>>,
@@ -532,6 +533,50 @@ pub trait AsBuffer: PyValue {
fn as_buffer(zelf: &PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyBuffer>;
}
#[pyimpl]
pub trait AsMapping: PyValue {
#[pyslot]
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()))?;
Self::as_mapping(zelf, vm)
}
fn downcast(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
zelf.downcast::<Self>().map_err(|obj| {
vm.new_type_error(format!(
"{} type is required, not {}",
Self::class(vm),
obj.class()
))
})
}
fn downcast_ref<'a>(zelf: &'a PyObjectRef, vm: &VirtualMachine) -> PyResult<&'a PyRef<Self>> {
zelf.downcast_ref::<Self>().ok_or_else(|| {
vm.new_type_error(format!(
"{} type is required, not {}",
Self::class(vm),
zelf.class()
))
})
}
fn as_mapping(zelf: &PyRef<Self>, vm: &VirtualMachine) -> PyResult<PyMappingMethods>;
fn length(zelf: PyObjectRef, _vm: &VirtualMachine) -> PyResult<usize>;
fn subscript(zelf: PyObjectRef, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult;
fn ass_subscript(
zelf: PyObjectRef,
needle: PyObjectRef,
value: Option<PyObjectRef>,
vm: &VirtualMachine,
) -> PyResult<()>;
}
#[pyimpl]
pub trait Iterable: PyValue {
#[pyslot]

View File

@@ -1049,8 +1049,24 @@ pub mod module {
}
#[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "macos"))]
fn envp_from_dict(dict: PyDictRef, vm: &VirtualMachine) -> PyResult<Vec<CString>> {
dict.into_iter()
fn envp_from_dict(
env: crate::protocol::PyMapping,
vm: &VirtualMachine,
) -> PyResult<Vec<CString>> {
let keys = env.keys(vm)?;
let values = env.values(vm)?;
let keys = PyListRef::try_from_object(vm, keys)
.map_err(|_| vm.new_type_error("env.keys() is not a list".to_owned()))?
.borrow_vec()
.to_vec();
let values = PyListRef::try_from_object(vm, values)
.map_err(|_| vm.new_type_error("env.values() is not a list".to_owned()))?
.borrow_vec()
.to_vec();
keys.into_iter()
.zip(values.into_iter())
.map(|(k, v)| {
let k = PyPathLike::try_from_object(vm, k)?.into_bytes();
let v = PyPathLike::try_from_object(vm, v)?.into_bytes();
@@ -1085,7 +1101,7 @@ pub mod module {
#[pyarg(positional)]
args: crate::function::ArgIterable<PyPathLike>,
#[pyarg(positional)]
env: crate::builtins::dict::PyMapping,
env: crate::protocol::PyMapping,
#[pyarg(named, default)]
file_actions: Option<crate::function::ArgIterable<PyTupleRef>>,
#[pyarg(named, default)]
@@ -1198,7 +1214,7 @@ pub mod module {
.map(|s| s.as_ptr() as _)
.chain(std::iter::once(std::ptr::null_mut()))
.collect();
let mut env = envp_from_dict(self.env.into_dict(), vm)?;
let mut env = envp_from_dict(self.env, vm)?;
let envp: Vec<*mut libc::c_char> = env
.iter_mut()
.map(|s| s.as_ptr() as _)

View File

@@ -2,10 +2,11 @@
use super::os::errno_err;
use crate::{
builtins::{dict::PyMapping, PyDictRef, PyStrRef},
builtins::{PyListRef, PyStrRef},
exceptions::IntoPyException,
function::OptionalArg,
PyObjectRef, PyResult, PySequence, TryFromObject, VirtualMachine,
protocol::PyMapping,
ItemProtocol, PyObjectRef, PyResult, PySequence, TryFromObject, VirtualMachine,
};
use std::ptr::{null, null_mut};
use winapi::shared::winerror;
@@ -155,7 +156,7 @@ fn _winapi_CreateProcess(
let mut env = args
.env_mapping
.map(|m| getenvironment(m.into_dict(), vm))
.map(|m| getenvironment(m, vm))
.transpose()?;
let env = env.as_mut().map_or_else(null_mut, |v| v.as_mut_ptr());
@@ -216,9 +217,21 @@ fn _winapi_CreateProcess(
))
}
fn getenvironment(env: PyDictRef, vm: &VirtualMachine) -> PyResult<Vec<u16>> {
fn getenvironment(env: PyMapping, vm: &VirtualMachine) -> PyResult<Vec<u16>> {
let keys = env.keys(vm)?;
let values = env.values(vm)?;
let keys = PyListRef::try_from_object(vm, keys)?.borrow_vec().to_vec();
let values = PyListRef::try_from_object(vm, values)?
.borrow_vec()
.to_vec();
if keys.len() != values.len() {
return Err(vm.new_runtime_error("environment changed size during iteration".to_owned()));
}
let mut out = widestring::WideString::new();
for (k, v) in env {
for (k, v) in keys.into_iter().zip(values.into_iter()) {
let k = PyStrRef::try_from_object(vm, k)?;
let k = k.as_str();
let v = PyStrRef::try_from_object(vm, v)?;
@@ -252,10 +265,11 @@ impl Drop for AttrList {
fn getattributelist(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<AttrList>> {
<Option<PyMapping>>::try_from_object(vm, obj)?
.map(|d| {
let d = d.into_dict();
let handlelist = d
.get_item_option("handle_list", vm)?
.map(|mapping| {
let handlelist = mapping
.into_object()
.get_item("handle_list", vm)
.ok()
.and_then(|obj| {
<Option<PySequence<usize>>>::try_from_object(vm, obj)
.map(|s| match s {
@@ -265,6 +279,7 @@ fn getattributelist(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<At
.transpose()
})
.transpose()?;
let attr_count = handlelist.is_some() as u32;
let mut size = 0;
let ret = unsafe {