-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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 listbut 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! |
| #[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
| 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
| #[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 protocolget_item,set_item,del_itemin theItemProtocolwill be improved by using fast-path which is provided by mapping protocol.With mapping protocol, this new issue can be fixed with
PyMapping::checkAccording to
docsandcpython implementationmapping proxy type must created only with mapping type.Documents