diff --git a/vm/src/bytesinner.rs b/vm/src/bytesinner.rs index 1e4f70fc0b..b24b3e2017 100644 --- a/vm/src/bytesinner.rs +++ b/vm/src/bytesinner.rs @@ -38,11 +38,11 @@ impl TryFromBorrowedObject for PyBytesInner { #[derive(FromArgs)] pub struct ByteInnerNewOptions { #[pyarg(any, optional)] - source: OptionalArg, + pub source: OptionalArg, #[pyarg(any, optional)] - encoding: OptionalArg, + pub encoding: OptionalArg, #[pyarg(any, optional)] - errors: OptionalArg, + pub errors: OptionalArg, } impl ByteInnerNewOptions { diff --git a/vm/src/protocol/iter.rs b/vm/src/protocol/iter.rs index 24f34f6ffc..5c0b397bdf 100644 --- a/vm/src/protocol/iter.rs +++ b/vm/src/protocol/iter.rs @@ -129,16 +129,6 @@ impl TryFromObject for PyIter { } } -impl PyObjectRef { - /// Takes an object and returns an iterator for it. - /// This is typically a new iterator but if the argument is an iterator, this - /// returns itself. - pub fn get_iter(self, vm: &VirtualMachine) -> PyResult { - // PyObject_GetIter - PyIter::try_from_object(vm, self) - } -} - pub enum PyIterReturn { Return(T), StopIteration(Option), diff --git a/vm/src/protocol/mod.rs b/vm/src/protocol/mod.rs index 179e4a8257..38c6a547e5 100644 --- a/vm/src/protocol/mod.rs +++ b/vm/src/protocol/mod.rs @@ -1,6 +1,7 @@ mod buffer; mod iter; mod mapping; +mod object; pub use buffer::{BufferInternal, BufferOptions, BufferResizeGuard, PyBuffer}; pub use iter::{PyIter, PyIterIter, PyIterReturn}; diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs new file mode 100644 index 0000000000..711aa8fa3d --- /dev/null +++ b/vm/src/protocol/object.rs @@ -0,0 +1,148 @@ +//! Object Protocol +//! https://docs.python.org/3/c-api/object.html + +use crate::{ + builtins::{pystr::IntoPyStrRef, PyBytes, PyInt, PyStrRef}, + bytesinner::ByteInnerNewOptions, + common::{hash::PyHash, str::to_ascii}, + function::OptionalArg, + protocol::PyIter, + pyref_type_error, + types::{Constructor, PyComparisonOp}, + PyObjectRef, PyResult, TryFromObject, VirtualMachine, +}; + +// RustPython doesn't need these items +// PyObject *Py_NotImplemented +// Py_RETURN_NOTIMPLEMENTED + +impl PyObjectRef { + // int PyObject_Print(PyObject *o, FILE *fp, int flags) + + pub fn has_attr(self, attr_name: impl IntoPyStrRef, vm: &VirtualMachine) -> PyResult { + self.get_attr(attr_name, vm).map(|o| vm.is_none(&o)) + } + + pub fn get_attr(self, attr_name: impl IntoPyStrRef, vm: &VirtualMachine) -> PyResult { + vm.get_attribute(self, attr_name) + } + + // PyObject *PyObject_GenericGetAttr(PyObject *o, PyObject *name) + + pub fn set_attr( + &self, + attr_name: impl IntoPyStrRef, + attr_value: impl Into, + vm: &VirtualMachine, + ) -> PyResult<()> { + vm.set_attr(self, attr_name, attr_value) + } + + // int PyObject_GenericSetAttr(PyObject *o, PyObject *name, PyObject *value) + + pub fn del_attr(&self, attr_name: impl IntoPyStrRef, vm: &VirtualMachine) -> PyResult<()> { + vm.del_attr(self, attr_name) + } + + // PyObject *PyObject_GenericGetDict(PyObject *o, void *context) + // int PyObject_GenericSetDict(PyObject *o, PyObject *value, void *context) + + pub fn rich_compare(self, other: Self, opid: PyComparisonOp, vm: &VirtualMachine) -> PyResult { + vm.obj_cmp(self, other, opid) + } + + pub fn rich_compare_bool( + &self, + other: &Self, + opid: PyComparisonOp, + vm: &VirtualMachine, + ) -> PyResult { + vm.bool_cmp(self, other, opid) + } + + pub fn repr(&self, vm: &VirtualMachine) -> PyResult { + vm.to_repr(self) + } + + pub fn ascii(&self, vm: &VirtualMachine) -> PyResult { + let repr = vm.to_repr(self)?; + let ascii = to_ascii(repr.as_str()); + Ok(ascii) + } + + pub fn str(&self, vm: &VirtualMachine) -> PyResult { + vm.to_str(self) + } + + pub fn bytes(self, vm: &VirtualMachine) -> PyResult { + let bytes_type = &vm.ctx.types.bytes_type; + match self.downcast_exact::(vm) { + Ok(int) => Err(pyref_type_error(vm, bytes_type, int.as_object())), + Err(obj) => PyBytes::py_new( + bytes_type.clone(), + ByteInnerNewOptions { + source: OptionalArg::Present(obj), + encoding: OptionalArg::Missing, + errors: OptionalArg::Missing, + }, + vm, + ), + } + } + + pub fn is_subclass(&self, cls: &PyObjectRef, vm: &VirtualMachine) -> PyResult { + vm.issubclass(self, cls) + } + + pub fn is_instance(&self, cls: &PyObjectRef, vm: &VirtualMachine) -> PyResult { + vm.isinstance(self, cls) + } + + pub fn hash(&self, vm: &VirtualMachine) -> PyResult { + vm._hash(self) + } + + // const hash_not_implemented: fn(&PyObjectRef, &VirtualMachine) ->PyResult = crate::types::Unhashable::slot_hash; + + pub fn is_true(self, vm: &VirtualMachine) -> PyResult { + self.try_to_bool(vm) + } + + pub fn not(self, vm: &VirtualMachine) -> PyResult { + self.is_true(vm).map(|x| !x) + } + + // type protocol + // PyObject *PyObject_Type(PyObject *o) + + // int PyObject_TypeCheck(PyObject *o, PyTypeObject *type) + + pub fn length(&self, vm: &VirtualMachine) -> PyResult { + vm.obj_len(self) + } + + pub fn length_hint( + self, + defaultvalue: Option, + vm: &VirtualMachine, + ) -> PyResult> { + Ok(vm.length_hint(self)?.or(defaultvalue)) + } + + // item protocol + // PyObject *PyObject_GetItem(PyObject *o, PyObject *key) + // int PyObject_SetItem(PyObject *o, PyObject *key, PyObject *v) + // int PyObject_DelItem(PyObject *o, PyObject *key) + + // PyObject *PyObject_Dir(PyObject *o) + + /// Takes an object and returns an iterator for it. + /// This is typically a new iterator but if the argument is an iterator, this + /// returns itself. + pub fn get_iter(self, vm: &VirtualMachine) -> PyResult { + // PyObject_GetIter + PyIter::try_from_object(vm, self) + } + + // PyObject *PyObject_GetAIter(PyObject *o) +} diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index ea5a1449c3..09abc80afc 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -502,6 +502,8 @@ where fn slot_hash(zelf: &PyObjectRef, vm: &VirtualMachine) -> PyResult { Err(vm.new_type_error(format!("unhashable type: '{}'", zelf.class().name()))) } + + #[cold] fn hash(_zelf: &PyRef, _vm: &VirtualMachine) -> PyResult { unreachable!("slot_hash is implemented for unhashable types"); }