-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Implement Mapping Protocol #3121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
74c8611
to
b9145dd
Compare
could we merge |
3ae9004
to
375c86b
Compare
check for |
d0651db
to
7aec733
Compare
@youknowone I feel a little bit confused with using |
you need only single function roughly: "__getitem__" | "__setitem__" | "__delitem__" => {
let func: slots::AsMappingFunc = |zelf, vm| PyMapping {
get: vm.get_class_attr("__getitem__").unwrap_or(getitem_fallback),
set: vm....
del:...
};
} fn getitem_fallback(zelf: PyObjectRef, key: PyObjectRef) -> PyResult {
super().getitem(key) // something like this. I am not sure how cpython handle it
} |
Ah! I got it. Thanks 😊 |
7aec733
to
b050741
Compare
Now >>>>> mappingproxy = type(type.__dict__)
>>>>> mappingproxy({"ASCII": 256})
<mappingproxy object at 0x55bb16b37560>
>>>>> mappingproxy([1,2,3])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: mappingproxy() argument must be a mapping, not list but update_slot routine for slot which have multiple sub_slots is somewhat horrible now. Another slots in current rustpython has only one function that corresponded to its slot. |
looks great! |
vm/src/slots.rs
Outdated
#[pyslot] | ||
fn tp_as_mapping(zelf: &PyObjectRef, vm: &VirtualMachine) -> PyResult<PyMapping> { | ||
let zelf = zelf | ||
.downcast_ref() | ||
.ok_or_else(|| vm.new_type_error("unexpected payload for as_mapping".to_owned()))?; | ||
Self::as_mapping(zelf, vm) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as_mapping
looks like not requiring any argument. Maybe this will be ok.
#[pyslot] | |
fn tp_as_mapping(zelf: &PyObjectRef, vm: &VirtualMachine) -> PyResult<PyMapping> { | |
let zelf = zelf | |
.downcast_ref() | |
.ok_or_else(|| vm.new_type_error("unexpected payload for as_mapping".to_owned()))?; | |
Self::as_mapping(zelf, vm) | |
} | |
#[pyslot] | |
fn as_mapping() -> PyMapping { | |
PyMapping { | |
length: Some(Self::length), | |
subscript: Some(Self::subscript), | |
ass_subscript: Some(Self::ass_subscript), | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I cannot declare empty parameter function as slot according to below error message 😢 I think function signature for pyslot must be a fn name(&PyObjectRef, &VirtualMachine)
non-primitive cast: `fn() -> std::result::Result<builtins::dict::PyMapping,
pyobjectrc::PyRef<exceptions::types::PyBaseException>> {<Self as slots::AsMapping>::slot_as_mapping}` as `for<'r, 's>
fn(&'r pyobjectrc::PyObjectRef, &'s vm::VirtualMachine) -> std::result::Result<builtins::dict::PyMapping,
pyobjectrc::PyRef<exceptions::types::PyBaseException>>`
Although this is little bit dirty, how about this one?
#[pyslot]
fn slot_as_mapping(_zelf: &PyObjectRef, _vm: &VirtualMachine) -> PyResult<PyMapping> {
Self::as_mapping()
}
fn as_mapping() -> PyResult<PyMapping>;
vm/src/stdlib/posix.rs
Outdated
@@ -1069,7 +1069,7 @@ pub mod module { | |||
#[pyarg(positional)] | |||
args: crate::function::ArgIterable<PyPathLike>, | |||
#[pyarg(positional)] | |||
env: crate::builtins::dict::PyMapping, | |||
env: crate::builtins::dict::PyDictRef, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
env: crate::builtins::dict::PyDictRef, | |
env: crate::protocol::PyMapping, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
with only PyMapping
and its fields, I cannot get its both items and keys together.
As cpython implementation, in my opinion, adding keys
and values
functions in PyMapping
and use PyObjectRef
for env
is more appropriate. how do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
uh, it looks more complicated than I thought before. For current MyMapping Implementation, you are right.
It looks like PyMapping
itself is a wrapper of PyObejctRef
, how I handled PyIter
in #3117. (https://github.com/RustPython/RustPython/pull/3117/files#diff-688a17d340aa55627e0e307b8cef31b99d478c8b41664cd92f5c2572b6688097R13)
And current PyMapping
looks like PyMappingMethods
in CPython - probably the way you originally approached.
How do you think about this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont know that I understand correctly though, did you mean switching current PyMapping
to PyMappingMethods
and creatingPyMapping
as a wrapper of PyObjectRef
with its related methods right?
It seems more appropriate than I thought 😊
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, exactly
b5aadd8
to
203cac9
Compare
As cpython use `range_compute_length` naming and `length` can be conflict with mapping protocol's method
As `getitem` method in other types, get second argument as PyObjectRef.
As `getitem` and `setitem methods in other types, get second argument as PyObjectRef.
Signed-off-by: snowapril <sinjihng@gmail.com>
As in cpython implementation there exists argument type checking before create mapping proxy like below. ```c if (!PyMapping_Check(mapping) || PyList_Check(mapping) || PyTuple_Check(mapping)) // print error ... ``` Signed-off-by: snowapril <sinjihng@gmail.com>
Signed-off-by: snowapril <sinjihng@gmail.com>
a57c9c1
to
5ffbb13
Compare
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(()))?, | ||
} | ||
), | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe we need _PyObject_NextNotImplemented
later
return getitem(self.clone(), key.into_pyobject(vm), vm); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we still need __getitem__
call under this line? I think PyMapping covers the whole cases
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. there exists extra steps (sequence protocol check & method lookup) as cpython implementation
vm/src/stdlib/posix.rs
Outdated
@@ -1049,8 +1050,21 @@ 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: PyMapping, vm: &VirtualMachine) -> PyResult<Vec<CString>> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn envp_from_dict(env: PyMapping, vm: &VirtualMachine) -> PyResult<Vec<CString>> { | |
fn envp_from_dict(env: crate::protocol::PyMapping, vm: &VirtualMachine) -> PyResult<Vec<CString>> { |
to solve unused import warning
} else { | ||
false | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
impl TryFromBorrowedObject for PyMappingMethods
can be placed here.
pub fn methods(&self, vm: &VirtualMachine) -> PyMappingMethods {
let obj_cls = self.0.class();
...
}
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>
Signed-off-by: snowapril <sinjihng@gmail.com>
5ffbb13
to
a81b3f9
Compare
This revision implements
mapping protocol
get_item
,set_item
,del_item
in theItemProtocol
will be improved by using fast-path which is provided by mapping protocol.With mapping protocol, this new issue can be fixed with
PyMapping::check
According to
docs
andcpython implementation
mapping proxy type must created only with mapping type.Documents