diff --git a/benches/microbenchmarks.rs b/benches/microbenchmarks.rs index 6e6103ccf..eaf125a01 100644 --- a/benches/microbenchmarks.rs +++ b/benches/microbenchmarks.rs @@ -3,9 +3,7 @@ use criterion::{ Criterion, Throughput, }; use rustpython_compiler::Mode; -use rustpython_vm::{ - common::ascii, InitParameter, Interpreter, PyObjectWrap, PyResult, PySettings, -}; +use rustpython_vm::{common::ascii, InitParameter, Interpreter, PyResult, PySettings}; use std::{ ffi, fs, io, path::{Path, PathBuf}, diff --git a/stdlib/src/array.rs b/stdlib/src/array.rs index 6542f68f0..5ff4a1c88 100644 --- a/stdlib/src/array.rs +++ b/stdlib/src/array.rs @@ -480,7 +480,7 @@ mod array { $f_swap(self) } fn to_object(self, vm: &VirtualMachine) -> PyObjectRef { - $f_to(self).into_object(vm) + $f_to(self).into_pyobject(vm) } } )*}; @@ -1117,7 +1117,7 @@ mod array { let typecode = vm.ctx.new_str(array.typecode_str()); let bytes = vm.ctx.new_bytes(array.get_bytes().to_vec()); let code = MachineFormatCode::from_typecode(array.typecode()).unwrap(); - let code = PyInt::from(u8::from(code)).into_object(vm); + let code = PyInt::from(u8::from(code)).into_pyobject(vm); let module = vm.import("array", None, 0)?; let func = module.get_attr("_array_reconstructor", vm)?; Ok(( @@ -1265,7 +1265,7 @@ mod array { position: AtomicUsize::new(0), array: zelf, } - .into_object(vm)) + .into_pyobject(vm)) } } diff --git a/stdlib/src/hashlib.rs b/stdlib/src/hashlib.rs index 06956a02d..52c98abd8 100644 --- a/stdlib/src/hashlib.rs +++ b/stdlib/src/hashlib.rs @@ -77,7 +77,7 @@ mod hashlib { #[pyslot] fn slot_new(_cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult { - Ok(PyHasher::new("md5", HashWrapper::md5()).into_object(vm)) + Ok(PyHasher::new("md5", HashWrapper::md5()).into_pyobject(vm)) } #[pyproperty] diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs index 792e5cf6c..f8d912925 100644 --- a/vm/src/builtins/bytearray.rs +++ b/vm/src/builtins/bytearray.rs @@ -33,8 +33,8 @@ use crate::{ IterNextIterable, Iterable, PyComparisonOp, Unconstructible, Unhashable, }, utils::Either, - AsObject, PyContext, PyObject, PyObjectRef, PyObjectView, PyObjectWrap, PyRef, PyResult, - PyValue, TryFromBorrowedObject, TryFromObject, VirtualMachine, + AsObject, PyContext, PyObject, PyObjectRef, PyObjectView, PyRef, PyResult, PyValue, + TryFromBorrowedObject, TryFromObject, VirtualMachine, }; use bstr::ByteSlice; use std::{borrow::Cow, mem::size_of}; @@ -751,7 +751,7 @@ static BUFFER_METHODS: BufferMethods = BufferMethods { impl AsBuffer for PyByteArray { fn as_buffer(zelf: &PyObjectView, _vm: &VirtualMachine) -> PyResult { Ok(PyBuffer::new( - zelf.to_owned().into_object(), + zelf.to_owned().into(), BufferDescriptor::simple(zelf.len(), false), &BUFFER_METHODS, )) @@ -789,12 +789,12 @@ impl PyByteArray { Self::sequence_downcast(seq) .inner() .concat(other, vm) - .map(|x| PyByteArray::from(x).into_object(vm)) + .map(|x| PyByteArray::from(x).into_pyobject(vm)) }), repeat: Some(|seq, n, vm| { Self::sequence_downcast(seq) .mul(n as isize, vm) - .map(|x| x.into_object(vm)) + .map(|x| x.into_pyobject(vm)) }), item: Some(|seq, i, vm| { Self::sequence_downcast(seq) @@ -833,7 +833,7 @@ impl Iterable for PyByteArray { Ok(PyByteArrayIterator { internal: PyMutex::new(PositionIterInternal::new(zelf, 0)), } - .into_object(vm)) + .into_pyobject(vm)) } } diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index 22b4ccc9d..1fcc21d6c 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -20,8 +20,8 @@ use crate::{ IterNextIterable, Iterable, PyComparisonOp, Unconstructible, }, utils::Either, - AsObject, PyContext, PyObject, PyObjectRef, PyObjectView, PyObjectWrap, PyRef, PyResult, - PyValue, TryFromBorrowedObject, TryFromObject, VirtualMachine, + AsObject, PyContext, PyObject, PyObjectRef, PyObjectView, PyRef, PyResult, PyValue, + TryFromBorrowedObject, TryFromObject, VirtualMachine, }; use bstr::ByteSlice; use std::{borrow::Cow, mem::size_of, ops::Deref}; @@ -567,7 +567,7 @@ static BUFFER_METHODS: BufferMethods = BufferMethods { impl AsBuffer for PyBytes { fn as_buffer(zelf: &PyObjectView, _vm: &VirtualMachine) -> PyResult { let buf = PyBuffer::new( - zelf.to_owned().into_object(), + zelf.to_owned().into(), BufferDescriptor::simple(zelf.len(), true), &BUFFER_METHODS, ); @@ -657,7 +657,7 @@ impl Iterable for PyBytes { Ok(PyBytesIterator { internal: PyMutex::new(PositionIterInternal::new(zelf, 0)), } - .into_object(vm)) + .into_pyobject(vm)) } } diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs index b84e6736b..f8a0fa4b1 100644 --- a/vm/src/builtins/dict.rs +++ b/vm/src/builtins/dict.rs @@ -374,7 +374,7 @@ impl PyDict { if let Ok(other) = dicted { let other_cp = other.copy(); PyDict::merge_dict(&other_cp.entries, zelf, vm)?; - return Ok(other_cp.into_object(vm)); + return Ok(other_cp.into_pyobject(vm)); } Ok(vm.ctx.not_implemented()) } @@ -385,7 +385,7 @@ impl PyDict { if let Ok(other) = dicted { let self_cp = self.copy(); PyDict::merge_dict(&self_cp.entries, other, vm)?; - return Ok(self_cp.into_object(vm)); + return Ok(self_cp.into_pyobject(vm)); } Ok(vm.ctx.not_implemented()) } @@ -502,7 +502,7 @@ impl Unhashable for PyDict {} impl Iterable for PyDict { fn iter(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - Ok(PyDictKeyIterator::new(zelf).into_object(vm)) + Ok(PyDictKeyIterator::new(zelf).into_pyobject(vm)) } } @@ -737,7 +737,7 @@ macro_rules! dict_view { impl Iterable for $name { fn iter(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - Ok($iter_name::new(zelf.dict.clone()).into_object(vm)) + Ok($iter_name::new(zelf.dict.clone()).into_pyobject(vm)) } } @@ -1011,7 +1011,7 @@ trait ViewSetOps: DictView { } ref _set @ PySet => { let inner = Self::to_set(zelf.to_owned(), vm)?; - let zelf_set = PySet { inner }.into_object(vm); + let zelf_set = PySet { inner }.into_pyobject(vm); PySet::cmp(zelf_set.downcast_ref().unwrap(), other, op, vm) } _ => { diff --git a/vm/src/builtins/frame.rs b/vm/src/builtins/frame.rs index 91676c68b..74b151a96 100644 --- a/vm/src/builtins/frame.rs +++ b/vm/src/builtins/frame.rs @@ -7,7 +7,7 @@ use crate::{ frame::{Frame, FrameRef}, pyclass::PyClassImpl, types::{Constructor, Unconstructible}, - AsObject, PyContext, PyObjectRef, PyObjectWrap, PyRef, PyResult, VirtualMachine, + AsObject, PyContext, PyObjectRef, PyRef, PyResult, VirtualMachine, }; pub fn init(context: &PyContext) { @@ -46,7 +46,7 @@ impl FrameRef { #[pyproperty] fn f_locals(self, vm: &VirtualMachine) -> PyResult { - self.locals(vm).map(|x| x.into_object()) + self.locals(vm).map(Into::into) } #[pyproperty] diff --git a/vm/src/builtins/function.rs b/vm/src/builtins/function.rs index cde240ad3..c64a515f1 100644 --- a/vm/src/builtins/function.rs +++ b/vm/src/builtins/function.rs @@ -311,9 +311,9 @@ impl PyFunction { let is_gen = code.flags.contains(bytecode::CodeFlags::IS_GENERATOR); let is_coro = code.flags.contains(bytecode::CodeFlags::IS_COROUTINE); match (is_gen, is_coro) { - (true, false) => Ok(PyGenerator::new(frame, self.name()).into_object(vm)), - (false, true) => Ok(PyCoroutine::new(frame, self.name()).into_object(vm)), - (true, true) => Ok(PyAsyncGen::new(frame, self.name()).into_object(vm)), + (true, false) => Ok(PyGenerator::new(frame, self.name()).into_pyobject(vm)), + (false, true) => Ok(PyCoroutine::new(frame, self.name()).into_pyobject(vm)), + (true, true) => Ok(PyAsyncGen::new(frame, self.name()).into_pyobject(vm)), (false, false) => vm.run_frame_full(frame), } } diff --git a/vm/src/builtins/genericalias.rs b/vm/src/builtins/genericalias.rs index e3b81f8fc..6414161a1 100644 --- a/vm/src/builtins/genericalias.rs +++ b/vm/src/builtins/genericalias.rs @@ -142,7 +142,10 @@ impl PyGenericAlias { vm, )?; - Ok(PyGenericAlias::new(self.origin.clone(), new_args.to_pyobject(vm), vm).into_object(vm)) + Ok( + PyGenericAlias::new(self.origin.clone(), new_args.to_pyobject(vm), vm) + .into_pyobject(vm), + ) } #[pymethod(magic)] diff --git a/vm/src/builtins/int.rs b/vm/src/builtins/int.rs index 07c19b85f..9690e6432 100644 --- a/vm/src/builtins/int.rs +++ b/vm/src/builtins/int.rs @@ -58,7 +58,7 @@ impl PyValue for PyInt { &vm.ctx.types.int_type } - fn into_object(self, vm: &VirtualMachine) -> PyObjectRef { + fn into_pyobject(self, vm: &VirtualMachine) -> PyObjectRef { vm.ctx.new_int(self.value).into() } diff --git a/vm/src/builtins/iter.rs b/vm/src/builtins/iter.rs index 47d255479..c2f529944 100644 --- a/vm/src/builtins/iter.rs +++ b/vm/src/builtins/iter.rs @@ -191,10 +191,10 @@ impl PySequenceIterator { if let IterStatus::Active(obj) = &internal.status { let seq = PySequence::with_methods(obj, self.seq_methods.clone()); seq.length(vm) - .map(|x| PyInt::from(x).into_object(vm)) + .map(|x| PyInt::from(x).into_pyobject(vm)) .unwrap_or_else(|_| vm.ctx.not_implemented()) } else { - PyInt::from(0).into_object(vm) + PyInt::from(0).into_pyobject(vm) } } diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs index 7f3f22626..d3545d646 100644 --- a/vm/src/builtins/list.rs +++ b/vm/src/builtins/list.rs @@ -16,8 +16,7 @@ use crate::{ }, utils::collection_repr, vm::{ReprGuard, VirtualMachine}, - AsObject, PyContext, PyObject, PyObjectRef, PyObjectView, PyObjectWrap, PyRef, PyResult, - PyValue, + AsObject, PyContext, PyObject, PyObjectRef, PyObjectView, PyRef, PyResult, PyValue, }; use std::{borrow::Cow, fmt, ops::DerefMut}; @@ -412,12 +411,12 @@ impl PyList { concat: Some(|seq, other, vm| { Self::sequence_downcast(seq) .concat(other, vm) - .map(|x| x.into_object()) + .map(|x| x.into()) }), repeat: Some(|seq, n, vm| { Self::sequence_downcast(seq) .mul(n as isize, vm) - .map(|x| x.into_object()) + .map(|x| x.into()) }), item: Some(|seq, i, vm| { Self::sequence_downcast(seq) @@ -443,7 +442,7 @@ impl PyList { inplace_repeat: Some(|seq, n, vm| { let zelf = Self::sequence_downcast(seq); zelf.borrow_vec_mut().imul(vm, n as isize)?; - Ok(zelf.to_owned().into_object()) + Ok(zelf.to_owned().into()) }), }; } @@ -453,7 +452,7 @@ impl Iterable for PyList { Ok(PyListIterator { internal: PyMutex::new(PositionIterInternal::new(zelf, 0)), } - .into_object(vm)) + .into_pyobject(vm)) } } diff --git a/vm/src/builtins/memory.rs b/vm/src/builtins/memory.rs index 6b4c64f78..bcc116cc4 100644 --- a/vm/src/builtins/memory.rs +++ b/vm/src/builtins/memory.rs @@ -19,8 +19,8 @@ use crate::{ sliceable::wrap_index, types::{AsBuffer, AsMapping, AsSequence, Comparable, Constructor, Hashable, PyComparisonOp}, utils::Either, - AsObject, PyContext, PyObject, PyObjectRef, PyObjectView, PyObjectWrap, PyRef, PyResult, - PyValue, TryFromBorrowedObject, TryFromObject, VirtualMachine, + AsObject, PyContext, PyObject, PyObjectRef, PyObjectView, PyRef, PyResult, PyValue, + TryFromBorrowedObject, TryFromObject, VirtualMachine, }; use crossbeam_utils::atomic::AtomicCell; use itertools::Itertools; @@ -255,7 +255,7 @@ impl PyMemoryView { other.init_slice(slice, 0, vm)?; other.init_len(); - Ok(other.into_ref(vm).into_object()) + Ok(other.into_ref(vm).into()) } fn getitem_by_multi_idx(&self, indexes: &[isize], vm: &VirtualMachine) -> PyResult { @@ -270,7 +270,7 @@ impl PyMemoryView { if zelf.desc.ndim() == 0 { // 0-d memoryview can be referenced using mv[...] or mv[()] only if needle.is(&vm.ctx.ellipsis) { - return Ok(zelf.into_object()); + return Ok(zelf.into()); } if let Some(tuple) = needle.payload::() { if tuple.is_empty() { @@ -539,9 +539,7 @@ impl PyMemoryView { let mut v = Vec::with_capacity(shape); for _ in 0..shape { - let obj = self - ._to_list(bytes, index + suboffset, dim + 1, vm)? - .into_object(); + let obj = self._to_list(bytes, index + suboffset, dim + 1, vm)?.into(); v.push(obj); index += stride; } @@ -938,7 +936,7 @@ impl AsBuffer for PyMemoryView { Err(vm.new_value_error("operation forbidden on released memoryview object".to_owned())) } else { Ok(PyBuffer::new( - zelf.to_owned().into_object(), + zelf.to_owned().into(), zelf.desc.clone(), &BUFFER_METHODS, )) diff --git a/vm/src/builtins/pystr.rs b/vm/src/builtins/pystr.rs index b79a7b094..26f4a0f1f 100644 --- a/vm/src/builtins/pystr.rs +++ b/vm/src/builtins/pystr.rs @@ -1264,7 +1264,7 @@ impl Iterable for PyStr { Ok(PyStrIterator { internal: PyMutex::new((PositionIterInternal::new(zelf, 0), 0)), } - .into_object(vm)) + .into_pyobject(vm)) } } diff --git a/vm/src/builtins/pysuper.rs b/vm/src/builtins/pysuper.rs index 064a96ac2..ad7709c00 100644 --- a/vm/src/builtins/pysuper.rs +++ b/vm/src/builtins/pysuper.rs @@ -171,7 +171,7 @@ impl GetDescriptor for PySuper { } let zelf_class = zelf.as_object().class(); if zelf_class.is(&vm.ctx.types.super_type) { - Ok(PySuper::new(zelf.typ.clone(), obj, vm)?.into_object(vm)) + Ok(PySuper::new(zelf.typ.clone(), obj, vm)?.into_pyobject(vm)) } else { let obj = vm.unwrap_or_none(zelf.obj.clone().map(|(o, _)| o)); vm.invoke(&zelf.class(), (zelf.typ.clone(), obj)) diff --git a/vm/src/builtins/pytype.rs b/vm/src/builtins/pytype.rs index a0eb3b3d2..2554a5787 100644 --- a/vm/src/builtins/pytype.rs +++ b/vm/src/builtins/pytype.rs @@ -477,19 +477,19 @@ impl PyType { let mut attributes = dict.to_attributes(); if let Some(f) = attributes.get_mut("__new__") { if f.class().is(&vm.ctx.types.function_type) { - *f = PyStaticMethod::from(f.clone()).into_object(vm); + *f = PyStaticMethod::from(f.clone()).into_pyobject(vm); } } if let Some(f) = attributes.get_mut("__init_subclass__") { if f.class().is(&vm.ctx.types.function_type) { - *f = PyClassMethod::from(f.clone()).into_object(vm); + *f = PyClassMethod::from(f.clone()).into_pyobject(vm); } } if let Some(f) = attributes.get_mut("__class_getitem__") { if f.class().is(&vm.ctx.types.function_type) { - *f = PyClassMethod::from(f.clone()).into_object(vm); + *f = PyClassMethod::from(f.clone()).into_pyobject(vm); } } diff --git a/vm/src/builtins/range.rs b/vm/src/builtins/range.rs index 1e7936b18..d6a416152 100644 --- a/vm/src/builtins/range.rs +++ b/vm/src/builtins/range.rs @@ -242,7 +242,7 @@ impl PyRange { // always fit in a usize. length: length.to_usize().unwrap_or(0), } - .into_object(vm) + .into_pyobject(vm) } else { PyLongRangeIterator { index: AtomicCell::new(0), @@ -250,7 +250,7 @@ impl PyRange { step, length, } - .into_object(vm) + .into_pyobject(vm) }, ) } @@ -284,7 +284,7 @@ impl PyRange { } } else { iter_search( - self.clone().into_object(vm), + self.clone().into_pyobject(vm), needle, SearchType::Contains, vm, @@ -315,8 +315,13 @@ impl PyRange { // Fallback to iteration. Ok(BigInt::from_bytes_be( Sign::Plus, - &iter_search(self.clone().into_object(vm), needle, SearchType::Index, vm)? - .to_be_bytes(), + &iter_search( + self.clone().into_pyobject(vm), + needle, + SearchType::Index, + vm, + )? + .to_be_bytes(), )) } } @@ -332,7 +337,7 @@ impl PyRange { } else { // Dealing with classes who might compare equal with ints in their // __eq__, slow search. - iter_search(self.clone().into_object(vm), item, SearchType::Count, vm) + iter_search(self.clone().into_pyobject(vm), item, SearchType::Count, vm) } } @@ -493,7 +498,7 @@ impl Iterable for PyRange { // always fit in a usize. length: length.to_usize().unwrap_or(0), } - .into_object(vm)) + .into_pyobject(vm)) } else { Ok(PyLongRangeIterator { index: AtomicCell::new(0), @@ -501,7 +506,7 @@ impl Iterable for PyRange { step: step.clone(), length, } - .into_object(vm)) + .into_pyobject(vm)) } } } diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs index 4413a5b4f..233f93766 100644 --- a/vm/src/builtins/set.rs +++ b/vm/src/builtins/set.rs @@ -338,7 +338,7 @@ impl PySetInner { &PyFrozenSet { inner: set.inner.copy(), } - .into_object(vm), + .into_pyobject(vm), vm, ) // If operation raised KeyError, report original set (set.remove) @@ -688,7 +688,7 @@ impl Unhashable for PySet {} impl Iterable for PySet { fn iter(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - Ok(zelf.inner.iter().into_object(vm)) + Ok(zelf.inner.iter().into_pyobject(vm)) } } @@ -933,7 +933,7 @@ impl Comparable for PyFrozenSet { impl Iterable for PyFrozenSet { fn iter(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - Ok(zelf.inner.iter().into_object(vm)) + Ok(zelf.inner.iter().into_pyobject(vm)) } } diff --git a/vm/src/builtins/tuple.rs b/vm/src/builtins/tuple.rs index 6aa60d28e..c28320717 100644 --- a/vm/src/builtins/tuple.rs +++ b/vm/src/builtins/tuple.rs @@ -394,7 +394,7 @@ impl Iterable for PyTuple { Ok(PyTupleIterator { internal: PyMutex::new(PositionIterInternal::new(zelf, 0)), } - .into_object(vm)) + .into_pyobject(vm)) } } diff --git a/vm/src/builtins/weakref.rs b/vm/src/builtins/weakref.rs index 2425ec3f0..faef972c5 100644 --- a/vm/src/builtins/weakref.rs +++ b/vm/src/builtins/weakref.rs @@ -7,8 +7,7 @@ use crate::{ function::OptionalArg, pyclass::PyClassImpl, types::{Callable, Comparable, Constructor, Hashable, PyComparisonOp}, - AsObject, PyContext, PyObject, PyObjectRef, PyObjectWrap, PyRef, PyResult, PyValue, - VirtualMachine, + AsObject, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, VirtualMachine, }; pub use crate::pyobject::PyWeak; @@ -44,7 +43,8 @@ impl Constructor for PyWeak { vm: &VirtualMachine, ) -> PyResult { let weak = referent.downgrade_with_typ(callback.into_option(), cls, vm)?; - Ok(weak.into_object()) + let pyref_weak: PyRef = weak.into(); + Ok(pyref_weak.into()) } } diff --git a/vm/src/convert/into_object.rs b/vm/src/convert/into_object.rs new file mode 100644 index 000000000..655edae3e --- /dev/null +++ b/vm/src/convert/into_object.rs @@ -0,0 +1,12 @@ +use crate::PyObjectRef; + +pub trait IntoObject +where + Self: Into, +{ + fn into_object(self) -> PyObjectRef { + self.into() + } +} + +impl IntoObject for T where T: Into {} diff --git a/vm/src/convert/mod.rs b/vm/src/convert/mod.rs index a381d0c85..209b296f7 100644 --- a/vm/src/convert/mod.rs +++ b/vm/src/convert/mod.rs @@ -1,7 +1,9 @@ +mod into_object; mod to_pyobject; mod transmute_from; mod try_from; +pub use into_object::IntoObject; pub use to_pyobject::{ToPyException, ToPyObject, ToPyResult}; pub use transmute_from::TransmuteFromObject; pub use try_from::{TryFromBorrowedObject, TryFromObject}; diff --git a/vm/src/frame.rs b/vm/src/frame.rs index b023ea90b..99ad67c7d 100644 --- a/vm/src/frame.rs +++ b/vm/src/frame.rs @@ -8,7 +8,7 @@ use crate::{ PySlice, PyStr, PyStrRef, PyTraceback, PyTypeRef, }, bytecode, - convert::ToPyResult, + convert::{IntoObject, ToPyResult}, coroutine::Coro, exceptions::ExceptionCtor, function::{ArgMapping, FuncArgs}, @@ -16,8 +16,8 @@ use crate::{ scope::Scope, stdlib::builtins, types::PyComparisonOp, - AsObject, PyMethod, PyObject, PyObjectRef, PyObjectWrap, PyRef, PyResult, PyValue, - TryFromObject, VirtualMachine, + AsObject, PyMethod, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, + VirtualMachine, }; use indexmap::IndexMap; use itertools::Itertools; @@ -727,7 +727,7 @@ impl ExecutingFrame<'_> { bytecode::Instruction::YieldValue => { let value = self.pop_value(); let value = if self.code.flags.contains(bytecode::CodeFlags::IS_COROUTINE) { - PyAsyncGenWrappedValue(value).into_object(vm) + PyAsyncGenWrappedValue(value).into_pyobject(vm) } else { value }; @@ -1606,7 +1606,7 @@ impl ExecutingFrame<'_> { defaults, kw_only_defaults, ) - .into_object(vm); + .into_pyobject(vm); func_obj.set_attr("__doc__", vm.ctx.none(), vm)?; diff --git a/vm/src/function.rs b/vm/src/function.rs deleted file mode 100644 index a7c9eea28..000000000 --- a/vm/src/function.rs +++ /dev/null @@ -1,738 +0,0 @@ -mod argument; -mod arithmetic; -mod buffer; -mod number; - -use crate::{ - builtins::{PyBaseExceptionRef, PyTupleRef, PyTypeRef}, - convert::{ToPyObject, ToPyResult}, - pyobject::PyThreadingConstraint, - AsObject, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, VirtualMachine, -}; -use indexmap::IndexMap; -use itertools::Itertools; -use std::{marker::PhantomData, ops::RangeInclusive}; - -pub use argument::{ArgCallable, ArgIterable, ArgMapping, ArgSequence}; -pub use arithmetic::{PyArithmeticValue, PyComparisonValue}; -pub use buffer::{ArgAsciiBuffer, ArgBytesLike, ArgMemoryBuffer, ArgStrOrBytesLike}; -pub use number::{ArgIntoBool, ArgIntoComplex, ArgIntoFloat}; - -pub trait IntoFuncArgs: Sized { - fn into_args(self, vm: &VirtualMachine) -> FuncArgs; - fn into_method_args(self, obj: PyObjectRef, vm: &VirtualMachine) -> FuncArgs { - let mut args = self.into_args(vm); - args.prepend_arg(obj); - args - } -} - -impl IntoFuncArgs for T -where - T: Into, -{ - fn into_args(self, _vm: &VirtualMachine) -> FuncArgs { - self.into() - } -} - -// A tuple of values that each implement `ToPyObject` represents a sequence of -// arguments that can be bound and passed to a built-in function. -macro_rules! into_func_args_from_tuple { - ($(($n:tt, $T:ident)),*) => { - impl<$($T,)*> IntoFuncArgs for ($($T,)*) - where - $($T: ToPyObject,)* - { - #[inline] - fn into_args(self, vm: &VirtualMachine) -> FuncArgs { - let ($($n,)*) = self; - PosArgs::new(vec![$($n.to_pyobject(vm),)*]).into() - } - - #[inline] - fn into_method_args(self, obj: PyObjectRef, vm: &VirtualMachine) -> FuncArgs { - let ($($n,)*) = self; - PosArgs::new(vec![obj, $($n.to_pyobject(vm),)*]).into() - } - } - }; -} - -into_func_args_from_tuple!((v1, T1)); -into_func_args_from_tuple!((v1, T1), (v2, T2)); -into_func_args_from_tuple!((v1, T1), (v2, T2), (v3, T3)); -into_func_args_from_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4)); -into_func_args_from_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5)); - -/// The `FuncArgs` struct is one of the most used structs then creating -/// a rust function that can be called from python. It holds both positional -/// arguments, as well as keyword arguments passed to the function. -#[derive(Debug, Default, Clone)] -pub struct FuncArgs { - pub args: Vec, - // sorted map, according to https://www.python.org/dev/peps/pep-0468/ - pub kwargs: IndexMap, -} - -/// Conversion from vector of python objects to function arguments. -impl From for FuncArgs -where - A: Into, -{ - fn from(args: A) -> Self { - FuncArgs { - args: args.into().into_vec(), - kwargs: IndexMap::new(), - } - } -} - -impl From for FuncArgs { - fn from(kwargs: KwArgs) -> Self { - FuncArgs { - args: Vec::new(), - kwargs: kwargs.0, - } - } -} - -impl FromArgs for FuncArgs { - fn from_args(_vm: &VirtualMachine, args: &mut FuncArgs) -> Result { - Ok(std::mem::take(args)) - } -} - -impl FuncArgs { - pub fn new(args: A, kwargs: K) -> Self - where - A: Into, - K: Into, - { - let PosArgs(args) = args.into(); - let KwArgs(kwargs) = kwargs.into(); - Self { args, kwargs } - } - - pub fn with_kwargs_names(mut args: A, kwarg_names: KW) -> Self - where - A: ExactSizeIterator, - KW: ExactSizeIterator, - { - // last `kwarg_names.len()` elements of args in order of appearance in the call signature - let total_argc = args.len(); - let kwargc = kwarg_names.len(); - let posargc = total_argc - kwargc; - - let posargs = args.by_ref().take(posargc).collect(); - - let kwargs = kwarg_names.zip_eq(args).collect::>(); - - FuncArgs { - args: posargs, - kwargs, - } - } - - pub fn prepend_arg(&mut self, item: PyObjectRef) { - self.args.reserve_exact(1); - self.args.insert(0, item) - } - - pub fn shift(&mut self) -> PyObjectRef { - self.args.remove(0) - } - - pub fn get_kwarg(&self, key: &str, default: PyObjectRef) -> PyObjectRef { - self.kwargs - .get(key) - .cloned() - .unwrap_or_else(|| default.clone()) - } - - pub fn get_optional_kwarg(&self, key: &str) -> Option { - self.kwargs.get(key).cloned() - } - - pub fn get_optional_kwarg_with_type( - &self, - key: &str, - ty: PyTypeRef, - vm: &VirtualMachine, - ) -> PyResult> { - match self.get_optional_kwarg(key) { - Some(kwarg) => { - if kwarg.fast_isinstance(&ty) { - Ok(Some(kwarg)) - } else { - let expected_ty_name = &ty.name(); - let kwarg_class = kwarg.class(); - let actual_ty_name = &kwarg_class.name(); - Err(vm.new_type_error(format!( - "argument of type {} is required for named parameter `{}` (got: {})", - expected_ty_name, key, actual_ty_name - ))) - } - } - None => Ok(None), - } - } - - pub fn take_positional(&mut self) -> Option { - if self.args.is_empty() { - None - } else { - Some(self.args.remove(0)) - } - } - - pub fn take_positional_keyword(&mut self, name: &str) -> Option { - self.take_positional().or_else(|| self.take_keyword(name)) - } - - pub fn take_keyword(&mut self, name: &str) -> Option { - self.kwargs.swap_remove(name) - } - - pub fn remaining_keywords(&mut self) -> impl Iterator + '_ { - self.kwargs.drain(..) - } - - /// Binds these arguments to their respective values. - /// - /// If there is an insufficient number of arguments, there are leftover - /// arguments after performing the binding, or if an argument is not of - /// the expected type, a TypeError is raised. - /// - /// If the given `FromArgs` includes any conversions, exceptions raised - /// during the conversion will halt the binding and return the error. - pub fn bind(mut self, vm: &VirtualMachine) -> PyResult { - let given_args = self.args.len(); - let bound = T::from_args(vm, &mut self) - .map_err(|e| e.into_exception(T::arity(), given_args, vm))?; - - if !self.args.is_empty() { - Err(vm.new_type_error(format!( - "Expected at most {} arguments ({} given)", - T::arity().end(), - given_args, - ))) - } else if let Some(err) = self.check_kwargs_empty(vm) { - Err(err) - } else { - Ok(bound) - } - } - - pub fn check_kwargs_empty(&self, vm: &VirtualMachine) -> Option { - self.kwargs - .keys() - .next() - .map(|k| vm.new_type_error(format!("Unexpected keyword argument {}", k))) - } -} - -/// An error encountered while binding arguments to the parameters of a Python -/// function call. -pub enum ArgumentError { - /// The call provided fewer positional arguments than the function requires. - TooFewArgs, - /// The call provided more positional arguments than the function accepts. - TooManyArgs, - /// The function doesn't accept a keyword argument with the given name. - InvalidKeywordArgument(String), - /// The function require a keyword argument with the given name, but one wasn't provided - RequiredKeywordArgument(String), - /// An exception was raised while binding arguments to the function - /// parameters. - Exception(PyBaseExceptionRef), -} - -impl From for ArgumentError { - fn from(ex: PyBaseExceptionRef) -> Self { - ArgumentError::Exception(ex) - } -} - -impl ArgumentError { - fn into_exception( - self, - arity: RangeInclusive, - num_given: usize, - vm: &VirtualMachine, - ) -> PyBaseExceptionRef { - match self { - ArgumentError::TooFewArgs => vm.new_type_error(format!( - "Expected at least {} arguments ({} given)", - arity.start(), - num_given - )), - ArgumentError::TooManyArgs => vm.new_type_error(format!( - "Expected at most {} arguments ({} given)", - arity.end(), - num_given - )), - ArgumentError::InvalidKeywordArgument(name) => { - vm.new_type_error(format!("{} is an invalid keyword argument", name)) - } - ArgumentError::RequiredKeywordArgument(name) => { - vm.new_type_error(format!("Required keyqord only argument {}", name)) - } - ArgumentError::Exception(ex) => ex, - } - } -} - -/// Implemented by any type that can be accepted as a parameter to a built-in -/// function. -/// -pub trait FromArgs: Sized { - /// The range of positional arguments permitted by the function signature. - /// - /// Returns an empty range if not applicable. - fn arity() -> RangeInclusive { - 0..=0 - } - - /// Extracts this item from the next argument(s). - fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result; -} - -pub trait FromArgOptional { - type Inner: TryFromObject; - fn from_inner(x: Self::Inner) -> Self; -} -impl FromArgOptional for OptionalArg { - type Inner = T; - fn from_inner(x: T) -> Self { - Self::Present(x) - } -} -impl FromArgOptional for T { - type Inner = Self; - fn from_inner(x: Self) -> Self { - x - } -} - -/// A map of keyword arguments to their values. -/// -/// A built-in function with a `KwArgs` parameter is analogous to a Python -/// function with `**kwargs`. All remaining keyword arguments are extracted -/// (and hence the function will permit an arbitrary number of them). -/// -/// `KwArgs` optionally accepts a generic type parameter to allow type checks -/// or conversions of each argument. -/// -/// Note: -/// -/// KwArgs is only for functions that accept arbitrary keyword arguments. For -/// functions that accept only *specific* named arguments, a rust struct with -/// an appropriate FromArgs implementation must be created. -#[derive(Clone)] -pub struct KwArgs(IndexMap); - -impl KwArgs { - pub fn new(map: IndexMap) -> Self { - KwArgs(map) - } - - pub fn pop_kwarg(&mut self, name: &str) -> Option { - self.0.remove(name) - } -} -impl FromIterator<(String, T)> for KwArgs { - fn from_iter>(iter: I) -> Self { - KwArgs(iter.into_iter().collect()) - } -} -impl Default for KwArgs { - fn default() -> Self { - KwArgs(IndexMap::new()) - } -} - -impl FromArgs for KwArgs -where - T: TryFromObject, -{ - fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result { - let mut kwargs = IndexMap::new(); - for (name, value) in args.remaining_keywords() { - kwargs.insert(name, value.try_into_value(vm)?); - } - Ok(KwArgs(kwargs)) - } -} - -impl IntoIterator for KwArgs { - type Item = (String, T); - type IntoIter = indexmap::map::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -/// A list of positional argument values. -/// -/// A built-in function with a `PosArgs` parameter is analogous to a Python -/// function with `*args`. All remaining positional arguments are extracted -/// (and hence the function will permit an arbitrary number of them). -/// -/// `PosArgs` optionally accepts a generic type parameter to allow type checks -/// or conversions of each argument. -#[derive(Clone)] -pub struct PosArgs(Vec); - -impl PosArgs { - pub fn new(args: Vec) -> Self { - Self(args) - } - - pub fn into_vec(self) -> Vec { - self.0 - } - - pub fn iter(&self) -> std::slice::Iter { - self.0.iter() - } -} - -impl From> for PosArgs { - fn from(v: Vec) -> Self { - Self(v) - } -} - -impl From<()> for PosArgs { - fn from(_args: ()) -> Self { - Self(Vec::new()) - } -} - -impl AsRef<[T]> for PosArgs { - fn as_ref(&self) -> &[T] { - &self.0 - } -} - -impl PosArgs> { - pub fn into_tuple(self, vm: &VirtualMachine) -> PyTupleRef { - vm.ctx - .new_tuple(self.0.into_iter().map(Into::into).collect()) - } -} - -impl FromArgs for PosArgs -where - T: TryFromObject, -{ - fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result { - let mut varargs = Vec::new(); - while let Some(value) = args.take_positional() { - varargs.push(value.try_into_value(vm)?); - } - Ok(PosArgs(varargs)) - } -} - -impl IntoIterator for PosArgs { - type Item = T; - type IntoIter = std::vec::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl FromArgs for T -where - T: TryFromObject, -{ - fn arity() -> RangeInclusive { - 1..=1 - } - - fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result { - let value = args.take_positional().ok_or(ArgumentError::TooFewArgs)?; - Ok(value.try_into_value(vm)?) - } -} - -/// An argument that may or may not be provided by the caller. -/// -/// This style of argument is not possible in pure Python. -#[derive(Debug, result_like::OptionLike, is_macro::Is)] -pub enum OptionalArg { - Present(T), - Missing, -} - -impl OptionalArg { - pub fn unwrap_or_none(self, vm: &VirtualMachine) -> PyObjectRef { - self.unwrap_or_else(|| vm.ctx.none()) - } -} - -pub type OptionalOption = OptionalArg>; - -impl OptionalOption { - #[inline] - pub fn flatten(self) -> Option { - match self { - OptionalArg::Present(Some(value)) => Some(value), - _ => None, - } - } -} - -impl FromArgs for OptionalArg -where - T: TryFromObject, -{ - fn arity() -> RangeInclusive { - 0..=1 - } - - fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result { - let r = if let Some(value) = args.take_positional() { - OptionalArg::Present(value.try_into_value(vm)?) - } else { - OptionalArg::Missing - }; - Ok(r) - } -} - -// For functions that accept no arguments. Implemented explicitly instead of via -// macro below to avoid unused warnings. -impl FromArgs for () { - fn from_args(_vm: &VirtualMachine, _args: &mut FuncArgs) -> Result { - Ok(()) - } -} - -// A tuple of types that each implement `FromArgs` represents a sequence of -// arguments that can be bound and passed to a built-in function. -// -// Technically, a tuple can contain tuples, which can contain tuples, and so on, -// so this actually represents a tree of values to be bound from arguments, but -// in practice this is only used for the top-level parameters. -macro_rules! tuple_from_py_func_args { - ($($T:ident),+) => { - impl<$($T),+> FromArgs for ($($T,)+) - where - $($T: FromArgs),+ - { - fn arity() -> RangeInclusive { - let mut min = 0; - let mut max = 0; - $( - let (start, end) = $T::arity().into_inner(); - min += start; - max += end; - )+ - min..=max - } - - fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result { - Ok(($($T::from_args(vm, args)?,)+)) - } - } - }; -} - -// Implement `FromArgs` for up to 7-tuples, allowing built-in functions to bind -// up to 7 top-level parameters (note that `PosArgs`, `KwArgs`, nested tuples, etc. -// count as 1, so this should actually be more than enough). -tuple_from_py_func_args!(A); -tuple_from_py_func_args!(A, B); -tuple_from_py_func_args!(A, B, C); -tuple_from_py_func_args!(A, B, C, D); -tuple_from_py_func_args!(A, B, C, D, E); -tuple_from_py_func_args!(A, B, C, D, E, F); -tuple_from_py_func_args!(A, B, C, D, E, F, G); -tuple_from_py_func_args!(A, B, C, D, E, F, G, H); - -/// A built-in Python function. -pub type PyNativeFunc = Box PyResult)>; - -/// Implemented by types that are or can generate built-in functions. -/// -/// This trait is implemented by any function that matches the pattern: -/// -/// ```rust,ignore -/// Fn([&self,] [T where T: FromArgs, ...] [, vm: &VirtualMachine]) -/// ``` -/// -/// For example, anything from `Fn()` to `Fn(vm: &VirtualMachine) -> u32` to -/// `Fn(PyIntRef, PyIntRef) -> String` to -/// `Fn(&self, PyStrRef, FooOptions, vm: &VirtualMachine) -> PyResult` -/// is `IntoPyNativeFunc`. If you do want a really general function signature, e.g. -/// to forward the args to another function, you can define a function like -/// `Fn(FuncArgs [, &VirtualMachine]) -> ...` -/// -/// Note that the `Kind` type parameter is meaningless and should be considered -/// an implementation detail; if you need to use `IntoPyNativeFunc` as a trait bound -/// just pass an unconstrained generic type, e.g. -/// `fn foo(f: F) where F: IntoPyNativeFunc` -pub trait IntoPyNativeFunc: Sized + PyThreadingConstraint + 'static { - fn call(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult; - /// `IntoPyNativeFunc::into_func()` generates a PyNativeFunc that performs the - /// appropriate type and arity checking, any requested conversions, and then if - /// successful calls the function with the extracted parameters. - fn into_func(self) -> PyNativeFunc { - Box::new(move |vm: &VirtualMachine, args| self.call(vm, args)) - } -} - -// TODO: once higher-rank trait bounds are stabilized, remove the `Kind` type -// parameter and impl for F where F: for PyNativeFuncInternal -impl IntoPyNativeFunc<(T, R, VM)> for F -where - F: PyNativeFuncInternal, -{ - #[inline(always)] - fn call(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult { - self.call_(vm, args) - } -} - -mod sealed { - use super::*; - pub trait PyNativeFuncInternal: Sized + PyThreadingConstraint + 'static { - fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult; - } -} -use sealed::PyNativeFuncInternal; - -#[doc(hidden)] -pub struct OwnedParam(PhantomData); -#[doc(hidden)] -pub struct RefParam(PhantomData); - -// This is the "magic" that allows rust functions of varying signatures to -// generate native python functions. -// -// Note that this could be done without a macro - it is simply to avoid repetition. -macro_rules! into_py_native_func_tuple { - ($(($n:tt, $T:ident)),*) => { - impl PyNativeFuncInternal<($(OwnedParam<$T>,)*), R, VirtualMachine> for F - where - F: Fn($($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static, - $($T: FromArgs,)* - R: ToPyResult, - { - fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult { - let ($($n,)*) = args.bind::<($($T,)*)>(vm)?; - - (self)($($n,)* vm).to_pyresult(vm) - } - } - - impl PyNativeFuncInternal<(RefParam, $(OwnedParam<$T>,)*), R, VirtualMachine> for F - where - F: Fn(&S, $($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static, - S: PyValue, - $($T: FromArgs,)* - R: ToPyResult, - { - fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult { - let (zelf, $($n,)*) = args.bind::<(PyRef, $($T,)*)>(vm)?; - - (self)(&zelf, $($n,)* vm).to_pyresult(vm) - } - } - - impl PyNativeFuncInternal<($(OwnedParam<$T>,)*), R, ()> for F - where - F: Fn($($T,)*) -> R + PyThreadingConstraint + 'static, - $($T: FromArgs,)* - R: ToPyResult, - { - fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult { - let ($($n,)*) = args.bind::<($($T,)*)>(vm)?; - - (self)($($n,)*).to_pyresult(vm) - } - } - - impl PyNativeFuncInternal<(RefParam, $(OwnedParam<$T>,)*), R, ()> for F - where - F: Fn(&S, $($T,)*) -> R + PyThreadingConstraint + 'static, - S: PyValue, - $($T: FromArgs,)* - R: ToPyResult, - { - fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult { - let (zelf, $($n,)*) = args.bind::<(PyRef, $($T,)*)>(vm)?; - - (self)(&zelf, $($n,)*).to_pyresult(vm) - } - } - }; -} - -into_py_native_func_tuple!(); -into_py_native_func_tuple!((v1, T1)); -into_py_native_func_tuple!((v1, T1), (v2, T2)); -into_py_native_func_tuple!((v1, T1), (v2, T2), (v3, T3)); -into_py_native_func_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4)); -into_py_native_func_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5)); -into_py_native_func_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5), (v6, T6)); -into_py_native_func_tuple!( - (v1, T1), - (v2, T2), - (v3, T3), - (v4, T4), - (v5, T5), - (v6, T6), - (v7, T7) -); - -/// Tests that the predicate is True on a single value, or if the value is a tuple a tuple, then -/// test that any of the values contained within the tuples satisfies the predicate. Type parameter -/// T specifies the type that is expected, if the input value is not of that type or a tuple of -/// values of that type, then a TypeError is raised. -pub fn single_or_tuple_any( - obj: PyObjectRef, - predicate: &F, - message: &M, - vm: &VirtualMachine, -) -> PyResult -where - T: TryFromObject, - F: Fn(&T) -> PyResult, - M: Fn(&PyObject) -> String, -{ - match T::try_from_object(vm, obj.clone()) { - Ok(single) => (predicate)(&single), - Err(_) => { - let tuple = PyTupleRef::try_from_object(vm, obj.clone()) - .map_err(|_| vm.new_type_error((message)(&obj)))?; - for obj in tuple.as_slice().iter() { - if single_or_tuple_any(obj.clone(), predicate, message, vm)? { - return Ok(true); - } - } - Ok(false) - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_intonativefunc_noalloc() { - let check_zst = |f: PyNativeFunc| assert_eq!(std::mem::size_of_val(f.as_ref()), 0); - fn py_func(_b: bool, _vm: &crate::VirtualMachine) -> i32 { - 1 - } - check_zst(py_func.into_func()); - let empty_closure = || "foo".to_owned(); - check_zst(empty_closure.into_func()); - } -} diff --git a/vm/src/function/argument.rs b/vm/src/function/argument.rs index 59693fff4..b1ebe1155 100644 --- a/vm/src/function/argument.rs +++ b/vm/src/function/argument.rs @@ -1,175 +1,545 @@ -use super::IntoFuncArgs; use crate::{ - builtins::{iter::PySequenceIterator, PyDict, PyDictRef}, + builtins::{PyBaseExceptionRef, PyTupleRef, PyTypeRef}, convert::ToPyObject, - protocol::{PyIter, PyIterIter, PyMapping, PyMappingMethods}, - AsObject, PyObject, PyObjectRef, PyObjectWrap, PyResult, PyValue, TryFromObject, - VirtualMachine, + AsObject, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, VirtualMachine, }; -use std::{borrow::Borrow, marker::PhantomData}; +use indexmap::IndexMap; +use itertools::Itertools; +use std::ops::RangeInclusive; -#[derive(Clone, Debug)] -pub struct ArgCallable { - obj: PyObjectRef, -} - -impl ArgCallable { - pub fn invoke(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult { - vm.invoke(&self.obj, args) +pub trait IntoFuncArgs: Sized { + fn into_args(self, vm: &VirtualMachine) -> FuncArgs; + fn into_method_args(self, obj: PyObjectRef, vm: &VirtualMachine) -> FuncArgs { + let mut args = self.into_args(vm); + args.prepend_arg(obj); + args } } -impl Borrow for ArgCallable { - #[inline(always)] - fn borrow(&self) -> &PyObject { - &self.obj +impl IntoFuncArgs for T +where + T: Into, +{ + fn into_args(self, _vm: &VirtualMachine) -> FuncArgs { + self.into() } } -impl AsRef for ArgCallable { - #[inline(always)] - fn as_ref(&self) -> &PyObject { - &self.obj - } +// A tuple of values that each implement `ToPyObject` represents a sequence of +// arguments that can be bound and passed to a built-in function. +macro_rules! into_func_args_from_tuple { + ($(($n:tt, $T:ident)),*) => { + impl<$($T,)*> IntoFuncArgs for ($($T,)*) + where + $($T: ToPyObject,)* + { + #[inline] + fn into_args(self, vm: &VirtualMachine) -> FuncArgs { + let ($($n,)*) = self; + PosArgs::new(vec![$($n.to_pyobject(vm),)*]).into() + } + + #[inline] + fn into_method_args(self, obj: PyObjectRef, vm: &VirtualMachine) -> FuncArgs { + let ($($n,)*) = self; + PosArgs::new(vec![obj, $($n.to_pyobject(vm),)*]).into() + } + } + }; } -impl PyObjectWrap for ArgCallable { - fn into_object(self) -> PyObjectRef { - self.obj - } +into_func_args_from_tuple!((v1, T1)); +into_func_args_from_tuple!((v1, T1), (v2, T2)); +into_func_args_from_tuple!((v1, T1), (v2, T2), (v3, T3)); +into_func_args_from_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4)); +into_func_args_from_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5)); + +/// The `FuncArgs` struct is one of the most used structs then creating +/// a rust function that can be called from python. It holds both positional +/// arguments, as well as keyword arguments passed to the function. +#[derive(Debug, Default, Clone)] +pub struct FuncArgs { + pub args: Vec, + // sorted map, according to https://www.python.org/dev/peps/pep-0468/ + pub kwargs: IndexMap, } -impl TryFromObject for ArgCallable { - fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - if vm.is_callable(&obj) { - Ok(ArgCallable { obj }) - } else { - Err(vm.new_type_error(format!("'{}' object is not callable", obj.class().name()))) +/// Conversion from vector of python objects to function arguments. +impl From for FuncArgs +where + A: Into, +{ + fn from(args: A) -> Self { + FuncArgs { + args: args.into().into_vec(), + kwargs: IndexMap::new(), } } } -/// An iterable Python object. -/// -/// `ArgIterable` implements `FromArgs` so that a built-in function can accept -/// an object that is required to conform to the Python iterator protocol. -/// -/// ArgIterable can optionally perform type checking and conversions on iterated -/// objects using a generic type parameter that implements `TryFromObject`. -pub struct ArgIterable { - iterable: PyObjectRef, - iterfn: Option, - _item: PhantomData, -} - -impl ArgIterable { - /// Returns an iterator over this sequence of objects. - /// - /// This operation may fail if an exception is raised while invoking the - /// `__iter__` method of the iterable object. - pub fn iter<'a>(&self, vm: &'a VirtualMachine) -> PyResult> { - let iter = PyIter::new(match self.iterfn { - Some(f) => f(self.iterable.clone(), vm)?, - None => PySequenceIterator::new(self.iterable.clone(), vm)?.into_object(vm), - }); - iter.into_iter(vm) +impl From for FuncArgs { + fn from(kwargs: KwArgs) -> Self { + FuncArgs { + args: Vec::new(), + kwargs: kwargs.0, + } } } -impl TryFromObject for ArgIterable +impl FromArgs for FuncArgs { + fn from_args(_vm: &VirtualMachine, args: &mut FuncArgs) -> Result { + Ok(std::mem::take(args)) + } +} + +impl FuncArgs { + pub fn new(args: A, kwargs: K) -> Self + where + A: Into, + K: Into, + { + let PosArgs(args) = args.into(); + let KwArgs(kwargs) = kwargs.into(); + Self { args, kwargs } + } + + pub fn with_kwargs_names(mut args: A, kwarg_names: KW) -> Self + where + A: ExactSizeIterator, + KW: ExactSizeIterator, + { + // last `kwarg_names.len()` elements of args in order of appearance in the call signature + let total_argc = args.len(); + let kwargc = kwarg_names.len(); + let posargc = total_argc - kwargc; + + let posargs = args.by_ref().take(posargc).collect(); + + let kwargs = kwarg_names.zip_eq(args).collect::>(); + + FuncArgs { + args: posargs, + kwargs, + } + } + + pub fn prepend_arg(&mut self, item: PyObjectRef) { + self.args.reserve_exact(1); + self.args.insert(0, item) + } + + pub fn shift(&mut self) -> PyObjectRef { + self.args.remove(0) + } + + pub fn get_kwarg(&self, key: &str, default: PyObjectRef) -> PyObjectRef { + self.kwargs + .get(key) + .cloned() + .unwrap_or_else(|| default.clone()) + } + + pub fn get_optional_kwarg(&self, key: &str) -> Option { + self.kwargs.get(key).cloned() + } + + pub fn get_optional_kwarg_with_type( + &self, + key: &str, + ty: PyTypeRef, + vm: &VirtualMachine, + ) -> PyResult> { + match self.get_optional_kwarg(key) { + Some(kwarg) => { + if kwarg.fast_isinstance(&ty) { + Ok(Some(kwarg)) + } else { + let expected_ty_name = &ty.name(); + let kwarg_class = kwarg.class(); + let actual_ty_name = &kwarg_class.name(); + Err(vm.new_type_error(format!( + "argument of type {} is required for named parameter `{}` (got: {})", + expected_ty_name, key, actual_ty_name + ))) + } + } + None => Ok(None), + } + } + + pub fn take_positional(&mut self) -> Option { + if self.args.is_empty() { + None + } else { + Some(self.args.remove(0)) + } + } + + pub fn take_positional_keyword(&mut self, name: &str) -> Option { + self.take_positional().or_else(|| self.take_keyword(name)) + } + + pub fn take_keyword(&mut self, name: &str) -> Option { + self.kwargs.swap_remove(name) + } + + pub fn remaining_keywords(&mut self) -> impl Iterator + '_ { + self.kwargs.drain(..) + } + + /// Binds these arguments to their respective values. + /// + /// If there is an insufficient number of arguments, there are leftover + /// arguments after performing the binding, or if an argument is not of + /// the expected type, a TypeError is raised. + /// + /// If the given `FromArgs` includes any conversions, exceptions raised + /// during the conversion will halt the binding and return the error. + pub fn bind(mut self, vm: &VirtualMachine) -> PyResult { + let given_args = self.args.len(); + let bound = T::from_args(vm, &mut self) + .map_err(|e| e.into_exception(T::arity(), given_args, vm))?; + + if !self.args.is_empty() { + Err(vm.new_type_error(format!( + "Expected at most {} arguments ({} given)", + T::arity().end(), + given_args, + ))) + } else if let Some(err) = self.check_kwargs_empty(vm) { + Err(err) + } else { + Ok(bound) + } + } + + pub fn check_kwargs_empty(&self, vm: &VirtualMachine) -> Option { + self.kwargs + .keys() + .next() + .map(|k| vm.new_type_error(format!("Unexpected keyword argument {}", k))) + } +} + +/// An error encountered while binding arguments to the parameters of a Python +/// function call. +pub enum ArgumentError { + /// The call provided fewer positional arguments than the function requires. + TooFewArgs, + /// The call provided more positional arguments than the function accepts. + TooManyArgs, + /// The function doesn't accept a keyword argument with the given name. + InvalidKeywordArgument(String), + /// The function require a keyword argument with the given name, but one wasn't provided + RequiredKeywordArgument(String), + /// An exception was raised while binding arguments to the function + /// parameters. + Exception(PyBaseExceptionRef), +} + +impl From for ArgumentError { + fn from(ex: PyBaseExceptionRef) -> Self { + ArgumentError::Exception(ex) + } +} + +impl ArgumentError { + fn into_exception( + self, + arity: RangeInclusive, + num_given: usize, + vm: &VirtualMachine, + ) -> PyBaseExceptionRef { + match self { + ArgumentError::TooFewArgs => vm.new_type_error(format!( + "Expected at least {} arguments ({} given)", + arity.start(), + num_given + )), + ArgumentError::TooManyArgs => vm.new_type_error(format!( + "Expected at most {} arguments ({} given)", + arity.end(), + num_given + )), + ArgumentError::InvalidKeywordArgument(name) => { + vm.new_type_error(format!("{} is an invalid keyword argument", name)) + } + ArgumentError::RequiredKeywordArgument(name) => { + vm.new_type_error(format!("Required keyqord only argument {}", name)) + } + ArgumentError::Exception(ex) => ex, + } + } +} + +/// Implemented by any type that can be accepted as a parameter to a built-in +/// function. +/// +pub trait FromArgs: Sized { + /// The range of positional arguments permitted by the function signature. + /// + /// Returns an empty range if not applicable. + fn arity() -> RangeInclusive { + 0..=0 + } + + /// Extracts this item from the next argument(s). + fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result; +} + +pub trait FromArgOptional { + type Inner: TryFromObject; + fn from_inner(x: Self::Inner) -> Self; +} +impl FromArgOptional for OptionalArg { + type Inner = T; + fn from_inner(x: T) -> Self { + Self::Present(x) + } +} +impl FromArgOptional for T { + type Inner = Self; + fn from_inner(x: Self) -> Self { + x + } +} + +/// A map of keyword arguments to their values. +/// +/// A built-in function with a `KwArgs` parameter is analogous to a Python +/// function with `**kwargs`. All remaining keyword arguments are extracted +/// (and hence the function will permit an arbitrary number of them). +/// +/// `KwArgs` optionally accepts a generic type parameter to allow type checks +/// or conversions of each argument. +/// +/// Note: +/// +/// KwArgs is only for functions that accept arbitrary keyword arguments. For +/// functions that accept only *specific* named arguments, a rust struct with +/// an appropriate FromArgs implementation must be created. +#[derive(Clone)] +pub struct KwArgs(IndexMap); + +impl KwArgs { + pub fn new(map: IndexMap) -> Self { + KwArgs(map) + } + + pub fn pop_kwarg(&mut self, name: &str) -> Option { + self.0.remove(name) + } +} +impl FromIterator<(String, T)> for KwArgs { + fn from_iter>(iter: I) -> Self { + KwArgs(iter.into_iter().collect()) + } +} +impl Default for KwArgs { + fn default() -> Self { + KwArgs(IndexMap::new()) + } +} + +impl FromArgs for KwArgs where T: TryFromObject, { - fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - let iterfn; - { - let cls = obj.class(); - iterfn = cls.mro_find_map(|x| x.slots.iter.load()); - if iterfn.is_none() && !cls.has_attr("__getitem__") { - return Err(vm.new_type_error(format!("'{}' object is not iterable", cls.name()))); - } + fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result { + let mut kwargs = IndexMap::new(); + for (name, value) in args.remaining_keywords() { + kwargs.insert(name, value.try_into_value(vm)?); } - Ok(Self { - iterable: obj, - iterfn, - _item: PhantomData, - }) + Ok(KwArgs(kwargs)) } } +impl IntoIterator for KwArgs { + type Item = (String, T); + type IntoIter = indexmap::map::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +/// A list of positional argument values. +/// +/// A built-in function with a `PosArgs` parameter is analogous to a Python +/// function with `*args`. All remaining positional arguments are extracted +/// (and hence the function will permit an arbitrary number of them). +/// +/// `PosArgs` optionally accepts a generic type parameter to allow type checks +/// or conversions of each argument. #[derive(Clone)] -pub struct ArgMapping { - obj: PyObjectRef, - mapping_methods: PyMappingMethods, -} +pub struct PosArgs(Vec); -impl ArgMapping { - pub fn from_dict_exact(dict: PyDictRef) -> Self { - Self { - obj: dict.into(), - mapping_methods: PyDict::MAPPING_METHODS, - } +impl PosArgs { + pub fn new(args: Vec) -> Self { + Self(args) } - pub fn mapping(&self) -> PyMapping { - PyMapping::with_methods(&self.obj, self.mapping_methods) - } -} - -impl Borrow for ArgMapping { - #[inline(always)] - fn borrow(&self) -> &PyObject { - &self.obj - } -} - -impl AsRef for ArgMapping { - #[inline(always)] - fn as_ref(&self) -> &PyObject { - &self.obj - } -} - -impl PyObjectWrap for ArgMapping { - #[inline(always)] - fn into_object(self) -> PyObjectRef { - self.obj - } -} - -impl ToPyObject for ArgMapping { - #[inline(always)] - fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef { - self.obj - } -} - -impl TryFromObject for ArgMapping { - fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - let mapping = PyMapping::try_protocol(&obj, vm)?; - let mapping_methods = *mapping.methods(vm); - Ok(Self { - obj, - mapping_methods, - }) - } -} - -// this is not strictly related to PySequence protocol. -#[derive(Clone)] -pub struct ArgSequence(Vec); - -impl ArgSequence { pub fn into_vec(self) -> Vec { self.0 } - pub fn as_slice(&self) -> &[T] { + + pub fn iter(&self) -> std::slice::Iter { + self.0.iter() + } +} + +impl From> for PosArgs { + fn from(v: Vec) -> Self { + Self(v) + } +} + +impl From<()> for PosArgs { + fn from(_args: ()) -> Self { + Self(Vec::new()) + } +} + +impl AsRef<[T]> for PosArgs { + fn as_ref(&self) -> &[T] { &self.0 } } -impl TryFromObject for ArgSequence { - fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - obj.try_to_value(vm).map(Self) +impl PosArgs> { + pub fn into_tuple(self, vm: &VirtualMachine) -> PyTupleRef { + vm.ctx + .new_tuple(self.0.into_iter().map(Into::into).collect()) } } + +impl FromArgs for PosArgs +where + T: TryFromObject, +{ + fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result { + let mut varargs = Vec::new(); + while let Some(value) = args.take_positional() { + varargs.push(value.try_into_value(vm)?); + } + Ok(PosArgs(varargs)) + } +} + +impl IntoIterator for PosArgs { + type Item = T; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl FromArgs for T +where + T: TryFromObject, +{ + fn arity() -> RangeInclusive { + 1..=1 + } + + fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result { + let value = args.take_positional().ok_or(ArgumentError::TooFewArgs)?; + Ok(value.try_into_value(vm)?) + } +} + +/// An argument that may or may not be provided by the caller. +/// +/// This style of argument is not possible in pure Python. +#[derive(Debug, result_like::OptionLike, is_macro::Is)] +pub enum OptionalArg { + Present(T), + Missing, +} + +impl OptionalArg { + pub fn unwrap_or_none(self, vm: &VirtualMachine) -> PyObjectRef { + self.unwrap_or_else(|| vm.ctx.none()) + } +} + +pub type OptionalOption = OptionalArg>; + +impl OptionalOption { + #[inline] + pub fn flatten(self) -> Option { + match self { + OptionalArg::Present(Some(value)) => Some(value), + _ => None, + } + } +} + +impl FromArgs for OptionalArg +where + T: TryFromObject, +{ + fn arity() -> RangeInclusive { + 0..=1 + } + + fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result { + let r = if let Some(value) = args.take_positional() { + OptionalArg::Present(value.try_into_value(vm)?) + } else { + OptionalArg::Missing + }; + Ok(r) + } +} + +// For functions that accept no arguments. Implemented explicitly instead of via +// macro below to avoid unused warnings. +impl FromArgs for () { + fn from_args(_vm: &VirtualMachine, _args: &mut FuncArgs) -> Result { + Ok(()) + } +} + +// A tuple of types that each implement `FromArgs` represents a sequence of +// arguments that can be bound and passed to a built-in function. +// +// Technically, a tuple can contain tuples, which can contain tuples, and so on, +// so this actually represents a tree of values to be bound from arguments, but +// in practice this is only used for the top-level parameters. +macro_rules! tuple_from_py_func_args { + ($($T:ident),+) => { + impl<$($T),+> FromArgs for ($($T,)+) + where + $($T: FromArgs),+ + { + fn arity() -> RangeInclusive { + let mut min = 0; + let mut max = 0; + $( + let (start, end) = $T::arity().into_inner(); + min += start; + max += end; + )+ + min..=max + } + + fn from_args(vm: &VirtualMachine, args: &mut FuncArgs) -> Result { + Ok(($($T::from_args(vm, args)?,)+)) + } + } + }; +} + +// Implement `FromArgs` for up to 7-tuples, allowing built-in functions to bind +// up to 7 top-level parameters (note that `PosArgs`, `KwArgs`, nested tuples, etc. +// count as 1, so this should actually be more than enough). +tuple_from_py_func_args!(A); +tuple_from_py_func_args!(A, B); +tuple_from_py_func_args!(A, B, C); +tuple_from_py_func_args!(A, B, C, D); +tuple_from_py_func_args!(A, B, C, D, E); +tuple_from_py_func_args!(A, B, C, D, E, F); +tuple_from_py_func_args!(A, B, C, D, E, F, G); +tuple_from_py_func_args!(A, B, C, D, E, F, G, H); diff --git a/vm/src/function/builtin.rs b/vm/src/function/builtin.rs new file mode 100644 index 000000000..2ef026fbf --- /dev/null +++ b/vm/src/function/builtin.rs @@ -0,0 +1,157 @@ +use super::{FromArgs, FuncArgs}; +use crate::{ + convert::ToPyResult, pyobject::PyThreadingConstraint, PyRef, PyResult, PyValue, VirtualMachine, +}; +use std::marker::PhantomData; + +/// A built-in Python function. +pub type PyNativeFunc = Box PyResult)>; + +/// Implemented by types that are or can generate built-in functions. +/// +/// This trait is implemented by any function that matches the pattern: +/// +/// ```rust,ignore +/// Fn([&self,] [T where T: FromArgs, ...] [, vm: &VirtualMachine]) +/// ``` +/// +/// For example, anything from `Fn()` to `Fn(vm: &VirtualMachine) -> u32` to +/// `Fn(PyIntRef, PyIntRef) -> String` to +/// `Fn(&self, PyStrRef, FooOptions, vm: &VirtualMachine) -> PyResult` +/// is `IntoPyNativeFunc`. If you do want a really general function signature, e.g. +/// to forward the args to another function, you can define a function like +/// `Fn(FuncArgs [, &VirtualMachine]) -> ...` +/// +/// Note that the `Kind` type parameter is meaningless and should be considered +/// an implementation detail; if you need to use `IntoPyNativeFunc` as a trait bound +/// just pass an unconstrained generic type, e.g. +/// `fn foo(f: F) where F: IntoPyNativeFunc` +pub trait IntoPyNativeFunc: Sized + PyThreadingConstraint + 'static { + fn call(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult; + /// `IntoPyNativeFunc::into_func()` generates a PyNativeFunc that performs the + /// appropriate type and arity checking, any requested conversions, and then if + /// successful calls the function with the extracted parameters. + fn into_func(self) -> PyNativeFunc { + Box::new(move |vm: &VirtualMachine, args| self.call(vm, args)) + } +} + +// TODO: once higher-rank trait bounds are stabilized, remove the `Kind` type +// parameter and impl for F where F: for PyNativeFuncInternal +impl IntoPyNativeFunc<(T, R, VM)> for F +where + F: PyNativeFuncInternal, +{ + #[inline(always)] + fn call(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult { + self.call_(vm, args) + } +} + +mod sealed { + use super::*; + pub trait PyNativeFuncInternal: Sized + PyThreadingConstraint + 'static { + fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult; + } +} +use sealed::PyNativeFuncInternal; + +#[doc(hidden)] +pub struct OwnedParam(PhantomData); +#[doc(hidden)] +pub struct RefParam(PhantomData); + +// This is the "magic" that allows rust functions of varying signatures to +// generate native python functions. +// +// Note that this could be done without a macro - it is simply to avoid repetition. +macro_rules! into_py_native_func_tuple { + ($(($n:tt, $T:ident)),*) => { + impl PyNativeFuncInternal<($(OwnedParam<$T>,)*), R, VirtualMachine> for F + where + F: Fn($($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static, + $($T: FromArgs,)* + R: ToPyResult, + { + fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult { + let ($($n,)*) = args.bind::<($($T,)*)>(vm)?; + + (self)($($n,)* vm).to_pyresult(vm) + } + } + + impl PyNativeFuncInternal<(RefParam, $(OwnedParam<$T>,)*), R, VirtualMachine> for F + where + F: Fn(&S, $($T,)* &VirtualMachine) -> R + PyThreadingConstraint + 'static, + S: PyValue, + $($T: FromArgs,)* + R: ToPyResult, + { + fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult { + let (zelf, $($n,)*) = args.bind::<(PyRef, $($T,)*)>(vm)?; + + (self)(&zelf, $($n,)* vm).to_pyresult(vm) + } + } + + impl PyNativeFuncInternal<($(OwnedParam<$T>,)*), R, ()> for F + where + F: Fn($($T,)*) -> R + PyThreadingConstraint + 'static, + $($T: FromArgs,)* + R: ToPyResult, + { + fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult { + let ($($n,)*) = args.bind::<($($T,)*)>(vm)?; + + (self)($($n,)*).to_pyresult(vm) + } + } + + impl PyNativeFuncInternal<(RefParam, $(OwnedParam<$T>,)*), R, ()> for F + where + F: Fn(&S, $($T,)*) -> R + PyThreadingConstraint + 'static, + S: PyValue, + $($T: FromArgs,)* + R: ToPyResult, + { + fn call_(&self, vm: &VirtualMachine, args: FuncArgs) -> PyResult { + let (zelf, $($n,)*) = args.bind::<(PyRef, $($T,)*)>(vm)?; + + (self)(&zelf, $($n,)*).to_pyresult(vm) + } + } + }; +} + +into_py_native_func_tuple!(); +into_py_native_func_tuple!((v1, T1)); +into_py_native_func_tuple!((v1, T1), (v2, T2)); +into_py_native_func_tuple!((v1, T1), (v2, T2), (v3, T3)); +into_py_native_func_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4)); +into_py_native_func_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5)); +into_py_native_func_tuple!((v1, T1), (v2, T2), (v3, T3), (v4, T4), (v5, T5), (v6, T6)); +into_py_native_func_tuple!( + (v1, T1), + (v2, T2), + (v3, T3), + (v4, T4), + (v5, T5), + (v6, T6), + (v7, T7) +); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_intonativefunc_noalloc() { + let check_zst = |f: PyNativeFunc| assert_eq!(std::mem::size_of_val(f.as_ref()), 0); + fn py_func(_b: bool, _vm: &crate::VirtualMachine) -> i32 { + 1 + } + check_zst(py_func.into_func()); + let empty_closure = || "foo".to_owned(); + check_zst(empty_closure.into_func()); + } +} diff --git a/vm/src/function/mod.rs b/vm/src/function/mod.rs new file mode 100644 index 000000000..58e27e13d --- /dev/null +++ b/vm/src/function/mod.rs @@ -0,0 +1,50 @@ +mod argument; +mod arithmetic; +mod buffer; +mod builtin; +mod number; +mod protocol; + +pub use argument::{ + ArgumentError, FromArgOptional, FromArgs, FuncArgs, IntoFuncArgs, KwArgs, OptionalArg, + OptionalOption, PosArgs, +}; +pub use arithmetic::{PyArithmeticValue, PyComparisonValue}; +pub use buffer::{ArgAsciiBuffer, ArgBytesLike, ArgMemoryBuffer, ArgStrOrBytesLike}; +pub use builtin::{IntoPyNativeFunc, OwnedParam, PyNativeFunc, RefParam}; +pub use number::{ArgIntoBool, ArgIntoComplex, ArgIntoFloat}; +pub use protocol::{ArgCallable, ArgIterable, ArgMapping, ArgSequence}; + +use crate::{ + builtins::PyTupleRef, convert::TryFromObject, PyObject, PyObjectRef, PyResult, VirtualMachine, +}; + +/// Tests that the predicate is True on a single value, or if the value is a tuple a tuple, then +/// test that any of the values contained within the tuples satisfies the predicate. Type parameter +/// T specifies the type that is expected, if the input value is not of that type or a tuple of +/// values of that type, then a TypeError is raised. +pub fn single_or_tuple_any( + obj: PyObjectRef, + predicate: &F, + message: &M, + vm: &VirtualMachine, +) -> PyResult +where + T: TryFromObject, + F: Fn(&T) -> PyResult, + M: Fn(&PyObject) -> String, +{ + match T::try_from_object(vm, obj.clone()) { + Ok(single) => (predicate)(&single), + Err(_) => { + let tuple = PyTupleRef::try_from_object(vm, obj.clone()) + .map_err(|_| vm.new_type_error((message)(&obj)))?; + for obj in tuple.as_slice().iter() { + if single_or_tuple_any(obj.clone(), predicate, message, vm)? { + return Ok(true); + } + } + Ok(false) + } + } +} diff --git a/vm/src/function/protocol.rs b/vm/src/function/protocol.rs new file mode 100644 index 000000000..2a4b5743e --- /dev/null +++ b/vm/src/function/protocol.rs @@ -0,0 +1,180 @@ +use super::IntoFuncArgs; +use crate::{ + builtins::{iter::PySequenceIterator, PyDict, PyDictRef}, + convert::ToPyObject, + protocol::{PyIter, PyIterIter, PyMapping, PyMappingMethods}, + AsObject, PyObject, PyObjectRef, PyResult, PyValue, TryFromObject, VirtualMachine, +}; +use std::{borrow::Borrow, marker::PhantomData}; + +#[derive(Clone, Debug)] +pub struct ArgCallable { + obj: PyObjectRef, +} + +impl ArgCallable { + #[inline(always)] + pub fn invoke(&self, args: impl IntoFuncArgs, vm: &VirtualMachine) -> PyResult { + vm.invoke(&self.obj, args) + } +} + +impl Borrow for ArgCallable { + #[inline(always)] + fn borrow(&self) -> &PyObject { + &self.obj + } +} + +impl AsRef for ArgCallable { + #[inline(always)] + fn as_ref(&self) -> &PyObject { + &self.obj + } +} + +impl From for PyObjectRef { + #[inline(always)] + fn from(value: ArgCallable) -> PyObjectRef { + value.obj + } +} + +impl TryFromObject for ArgCallable { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { + if vm.is_callable(&obj) { + Ok(ArgCallable { obj }) + } else { + Err(vm.new_type_error(format!("'{}' object is not callable", obj.class().name()))) + } + } +} + +/// An iterable Python object. +/// +/// `ArgIterable` implements `FromArgs` so that a built-in function can accept +/// an object that is required to conform to the Python iterator protocol. +/// +/// ArgIterable can optionally perform type checking and conversions on iterated +/// objects using a generic type parameter that implements `TryFromObject`. +pub struct ArgIterable { + iterable: PyObjectRef, + iterfn: Option, + _item: PhantomData, +} + +impl ArgIterable { + /// Returns an iterator over this sequence of objects. + /// + /// This operation may fail if an exception is raised while invoking the + /// `__iter__` method of the iterable object. + pub fn iter<'a>(&self, vm: &'a VirtualMachine) -> PyResult> { + let iter = PyIter::new(match self.iterfn { + Some(f) => f(self.iterable.clone(), vm)?, + None => PySequenceIterator::new(self.iterable.clone(), vm)?.into_pyobject(vm), + }); + iter.into_iter(vm) + } +} + +impl TryFromObject for ArgIterable +where + T: TryFromObject, +{ + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { + let iterfn; + { + let cls = obj.class(); + iterfn = cls.mro_find_map(|x| x.slots.iter.load()); + if iterfn.is_none() && !cls.has_attr("__getitem__") { + return Err(vm.new_type_error(format!("'{}' object is not iterable", cls.name()))); + } + } + Ok(Self { + iterable: obj, + iterfn, + _item: PhantomData, + }) + } +} + +#[derive(Clone)] +pub struct ArgMapping { + obj: PyObjectRef, + mapping_methods: PyMappingMethods, +} + +impl ArgMapping { + #[inline(always)] + pub fn from_dict_exact(dict: PyDictRef) -> Self { + Self { + obj: dict.into(), + mapping_methods: PyDict::MAPPING_METHODS, + } + } + + #[inline(always)] + pub fn mapping(&self) -> PyMapping { + PyMapping::with_methods(&self.obj, self.mapping_methods) + } +} + +impl Borrow for ArgMapping { + #[inline(always)] + fn borrow(&self) -> &PyObject { + &self.obj + } +} + +impl AsRef for ArgMapping { + #[inline(always)] + fn as_ref(&self) -> &PyObject { + &self.obj + } +} + +impl From for PyObjectRef { + #[inline(always)] + fn from(value: ArgMapping) -> PyObjectRef { + value.obj + } +} + +impl ToPyObject for ArgMapping { + #[inline(always)] + fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef { + self.obj + } +} + +impl TryFromObject for ArgMapping { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { + let mapping = PyMapping::try_protocol(&obj, vm)?; + let mapping_methods = *mapping.methods(vm); + Ok(Self { + obj, + mapping_methods, + }) + } +} + +// this is not strictly related to PySequence protocol. +#[derive(Clone)] +pub struct ArgSequence(Vec); + +impl ArgSequence { + #[inline(always)] + pub fn into_vec(self) -> Vec { + self.0 + } + #[inline(always)] + pub fn as_slice(&self) -> &[T] { + &self.0 + } +} + +impl TryFromObject for ArgSequence { + fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { + obj.try_to_value(vm).map(Self) + } +} diff --git a/vm/src/lib.rs b/vm/src/lib.rs index b1918ea28..862aba2f4 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -89,7 +89,7 @@ mod pyobject { pub use self::convert::{TryFromBorrowedObject, TryFromObject}; // pyobject items pub use self::pyobject::{ - AsObject, PyContext, PyMethod, PyObjectWrap, PyRefExact, PyResult, PyStructSequence, PyValue, + AsObject, PyContext, PyMethod, PyRefExact, PyResult, PyStructSequence, PyValue, }; // pyobjectrc items pub use self::pyobject::{PyObject, PyObjectRef, PyObjectView, PyObjectWeak, PyRef, PyWeakRef}; diff --git a/vm/src/macros.rs b/vm/src/macros.rs index d1a2a8ba5..99e691481 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -72,7 +72,7 @@ macro_rules! py_namespace { /// use rustpython_vm::{PyValue}; /// /// # rustpython_vm::Interpreter::default().enter(|vm| { -/// let obj = PyInt::from(0).into_object(vm); +/// let obj = PyInt::from(0).into_pyobject(vm); /// assert_eq!( /// "int", /// match_class!(match obj { @@ -96,7 +96,7 @@ macro_rules! py_namespace { /// use rustpython_vm::{ PyValue}; /// /// # rustpython_vm::Interpreter::default().enter(|vm| { -/// let obj = PyInt::from(0).into_object(vm); +/// let obj = PyInt::from(0).into_pyobject(vm); /// /// let int_value = match_class!(match obj { /// i @ PyInt => i.as_bigint().clone(), diff --git a/vm/src/protocol/buffer.rs b/vm/src/protocol/buffer.rs index 7a871beed..5040a03e1 100644 --- a/vm/src/protocol/buffer.rs +++ b/vm/src/protocol/buffer.rs @@ -9,8 +9,8 @@ use crate::{ pyobject::PyObjectPayload, sliceable::wrap_index, types::{Constructor, Unconstructible}, - AsObject, PyObject, PyObjectRef, PyObjectView, PyObjectWrap, PyRef, PyResult, PyValue, - TryFromBorrowedObject, VirtualMachine, + AsObject, PyObject, PyObjectRef, PyObjectView, PyRef, PyResult, PyValue, TryFromBorrowedObject, + VirtualMachine, }; use itertools::Itertools; use std::{borrow::Cow, fmt::Debug, ops::Range}; @@ -65,7 +65,7 @@ impl PyBuffer { pub fn from_byte_vector(bytes: Vec, vm: &VirtualMachine) -> Self { let bytes_len = bytes.len(); PyBuffer::new( - PyValue::into_object(VecBuffer::from(bytes), vm), + PyValue::into_pyobject(VecBuffer::from(bytes), vm), BufferDescriptor::simple(bytes_len, true), &VEC_BUFFER_METHODS, ) @@ -421,14 +421,14 @@ impl PyRef { pub fn into_pybuffer(self, readonly: bool) -> PyBuffer { let len = self.data.lock().len(); PyBuffer::new( - self.into_object(), + self.into(), BufferDescriptor::simple(len, readonly), &VEC_BUFFER_METHODS, ) } pub fn into_pybuffer_with_descriptor(self, desc: BufferDescriptor) -> PyBuffer { - PyBuffer::new(self.into_object(), desc, &VEC_BUFFER_METHODS) + PyBuffer::new(self.into(), desc, &VEC_BUFFER_METHODS) } } diff --git a/vm/src/protocol/iter.rs b/vm/src/protocol/iter.rs index d9b347249..5f137aabd 100644 --- a/vm/src/protocol/iter.rs +++ b/vm/src/protocol/iter.rs @@ -1,8 +1,7 @@ use crate::{ builtins::iter::PySequenceIterator, convert::{ToPyObject, ToPyResult}, - AsObject, PyObject, PyObjectRef, PyObjectWrap, PyResult, PyValue, TryFromObject, - VirtualMachine, + AsObject, PyObject, PyObjectRef, PyResult, PyValue, TryFromObject, VirtualMachine, }; use std::borrow::Borrow; use std::ops::Deref; @@ -70,9 +69,9 @@ impl PyIter { } } -impl PyObjectWrap for PyIter { - fn into_object(self) -> PyObjectRef { - self.0 +impl From> for PyObjectRef { + fn from(value: PyIter) -> PyObjectRef { + value.0 } } @@ -134,7 +133,7 @@ impl TryFromObject for PyIter { ))) } } else if let Ok(seq_iter) = PySequenceIterator::new(iter_target.clone(), vm) { - Ok(Self(seq_iter.into_object(vm))) + Ok(Self(seq_iter.into_pyobject(vm))) } else { Err(vm.new_type_error(format!( "'{}' object is not iterable", diff --git a/vm/src/protocol/mapping.rs b/vm/src/protocol/mapping.rs index d78bda459..f8ec7437d 100644 --- a/vm/src/protocol/mapping.rs +++ b/vm/src/protocol/mapping.rs @@ -26,6 +26,7 @@ pub struct PyMapping<'a> { } impl<'a> From<&'a PyObject> for PyMapping<'a> { + #[inline(always)] fn from(obj: &'a PyObject) -> Self { Self { obj, @@ -35,12 +36,14 @@ impl<'a> From<&'a PyObject> for PyMapping<'a> { } impl AsRef for PyMapping<'_> { + #[inline(always)] fn as_ref(&self) -> &PyObject { self.obj } } impl<'a> PyMapping<'a> { + #[inline(always)] pub fn with_methods(obj: &'a PyObject, methods: PyMappingMethods) -> Self { Self { obj, @@ -60,6 +63,7 @@ impl<'a> PyMapping<'a> { impl PyMapping<'_> { // PyMapping::Check + #[inline] pub fn check(&self, vm: &VirtualMachine) -> bool { self.methods(vm).subscript.is_some() } diff --git a/vm/src/protocol/sequence.rs b/vm/src/protocol/sequence.rs index ca055d5c2..d48b86f81 100644 --- a/vm/src/protocol/sequence.rs +++ b/vm/src/protocol/sequence.rs @@ -229,7 +229,7 @@ impl PySequence<'_> { stop: stop.to_pyobject(vm), step: None, }; - mapping.subscript(&slice.into_object(vm), vm) + mapping.subscript(&slice.into_pyobject(vm), vm) } else { Err(vm.new_type_error(format!("'{}' object is unsliceable", self.obj.class()))) } @@ -249,7 +249,7 @@ impl PySequence<'_> { stop: stop.to_pyobject(vm), step: None, }; - f(&mapping, &slice.into_object(vm), value, vm) + f(&mapping, &slice.into_pyobject(vm), value, vm) } else { Err(vm.new_type_error(format!( "'{}' object doesn't support slice {}", diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index e127997ab..e3ce56451 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -526,7 +526,7 @@ where { #[inline(always)] fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef { - PyValue::into_object(self, vm) + PyValue::into_pyobject(self, vm) } } @@ -564,7 +564,7 @@ pub trait PyValue: fmt::Debug + PyThreadingConstraint + Sized + 'static { fn class(vm: &VirtualMachine) -> &PyTypeRef; #[inline] - fn into_object(self, vm: &VirtualMachine) -> PyObjectRef { + fn into_pyobject(self, vm: &VirtualMachine) -> PyObjectRef { self.into_ref(vm).into() } @@ -685,23 +685,6 @@ pub trait PyStructSequence: StaticType + PyClassImpl + Sized + 'static { } } -pub trait PyObjectWrap -where - Self: AsObject, -{ - fn into_object(self) -> PyObjectRef; -} - -impl From for PyObjectRef -where - T: PyObjectWrap, -{ - #[inline(always)] - fn from(py_ref: T) -> Self { - py_ref.into_object() - } -} - #[derive(Debug)] pub enum PyMethod { Function { diff --git a/vm/src/pyobjectrc.rs b/vm/src/pyobjectrc.rs index a66fa1ef5..fbdbc784f 100644 --- a/vm/src/pyobjectrc.rs +++ b/vm/src/pyobjectrc.rs @@ -3,7 +3,7 @@ use crate::common::linked_list::{Link, LinkedList, Pointers}; use crate::common::lock::{PyMutex, PyMutexGuard, PyRwLock}; use crate::common::refcount::RefCount; use crate::{ - _pyobject::{AsObject, PyObjectPayload, PyObjectWrap, PyResult}, + _pyobject::{AsObject, PyObjectPayload, PyResult}, builtins::{PyBaseExceptionRef, PyDictRef, PyTypeRef}, vm::VirtualMachine, }; @@ -792,10 +792,10 @@ impl Borrow for PyObjectWeak { } } -impl PyObjectWrap for PyObjectWeak { +impl From for PyRef { #[inline(always)] - fn into_object(self) -> PyObjectRef { - self.weak.into_object() + fn from(value: PyObjectWeak) -> Self { + value.weak } } @@ -999,13 +999,13 @@ where } } -impl PyObjectWrap for PyRef +impl From> for PyObjectRef where T: PyObjectPayload, { #[inline] - fn into_object(self) -> PyObjectRef { - let me = ManuallyDrop::new(self); + fn from(value: PyRef) -> Self { + let me = ManuallyDrop::new(value); PyObjectRef { ptr: me.ptr.cast() } } } diff --git a/vm/src/stdlib/builtins.rs b/vm/src/stdlib/builtins.rs index a2ec71335..c13a82990 100644 --- a/vm/src/stdlib/builtins.rs +++ b/vm/src/stdlib/builtins.rs @@ -33,8 +33,7 @@ mod builtins { stdlib::sys, types::PyComparisonOp, utils::Either, - AsObject, PyObject, PyObjectRef, PyObjectWrap, PyRef, PyResult, PyValue, TryFromObject, - VirtualMachine, + AsObject, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, VirtualMachine, }; use num_traits::{Signed, ToPrimitive, Zero}; @@ -433,8 +432,8 @@ mod builtins { } #[pyfunction] - fn locals(vm: &VirtualMachine) -> PyResult { - vm.current_locals().map(|x| x.into_object()) + fn locals(vm: &VirtualMachine) -> PyResult { + vm.current_locals() } fn min_or_max( @@ -690,7 +689,7 @@ mod builtins { })?; let len = obj.length(vm)?; let obj_iterator = PyReverseSequenceIterator::new(obj, len); - Ok(obj_iterator.into_object(vm)) + Ok(obj_iterator.into_pyobject(vm)) } } @@ -793,7 +792,7 @@ mod builtins { vm.new_type_error("vars() argument must have __dict__ attribute".to_owned()) }) } else { - Ok(vm.current_locals()?.into_object()) + Ok(vm.current_locals()?.into()) } } diff --git a/vm/src/stdlib/collections.rs b/vm/src/stdlib/collections.rs index 54912b2ce..0289d9a3d 100644 --- a/vm/src/stdlib/collections.rs +++ b/vm/src/stdlib/collections.rs @@ -571,7 +571,7 @@ mod _collections { impl Iterable for PyDeque { fn iter(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - Ok(PyDequeIterator::new(zelf).into_object(vm)) + Ok(PyDequeIterator::new(zelf).into_pyobject(vm)) } } diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index 92873bec6..1bf65dd2a 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -114,7 +114,7 @@ mod _io { types::{Constructor, Destructor, IterNext, Iterable}, utils::Either, vm::{ReprGuard, VirtualMachine}, - AsObject, PyContext, PyObject, PyObjectRef, PyObjectWrap, PyRef, PyResult, PyValue, + AsObject, PyContext, PyObject, PyObjectRef, PyRef, PyResult, PyValue, TryFromBorrowedObject, TryFromObject, }; use bstr::ByteSlice; @@ -3313,7 +3313,7 @@ mod _io { fn getbuffer(self, vm: &VirtualMachine) -> PyResult { let len = self.buffer.read().cursor.get_ref().len(); let buffer = PyBuffer::new( - self.into_object(), + self.into(), BufferDescriptor::simple(len, false), &BYTES_IO_BUFFER_METHODS, ); diff --git a/vm/src/stdlib/sre.rs b/vm/src/stdlib/sre.rs index 63f17f27d..3a9b85bfd 100644 --- a/vm/src/stdlib/sre.rs +++ b/vm/src/stdlib/sre.rs @@ -484,7 +484,7 @@ mod _sre { /* get segment following last match */ sublist.push(slice_drive(&state.string, last_pos, state.end, vm)); - let list = PyList::from(sublist).into_object(vm); + let list = PyList::from(sublist).into_pyobject(vm); let join_type: PyObjectRef = if zelf.isbytes { vm.ctx.new_bytes(vec![]).into() diff --git a/vm/src/stdlib/symtable.rs b/vm/src/stdlib/symtable.rs index 390b24ac6..e847b0f05 100644 --- a/vm/src/stdlib/symtable.rs +++ b/vm/src/stdlib/symtable.rs @@ -143,7 +143,7 @@ mod symtable { .symtable .sub_tables .iter() - .map(|t| to_py_symbol_table(t.clone()).into_object(vm)) + .map(|t| to_py_symbol_table(t.clone()).into_pyobject(vm)) .collect(); Ok(children) } @@ -231,7 +231,7 @@ mod symtable { let namespaces = self .namespaces .iter() - .map(|table| to_py_symbol_table(table.clone()).into_object(vm)) + .map(|table| to_py_symbol_table(table.clone()).into_pyobject(vm)) .collect(); Ok(namespaces) } diff --git a/vm/src/stdlib/weakref.rs b/vm/src/stdlib/weakref.rs index e5eefddef..59620625d 100644 --- a/vm/src/stdlib/weakref.rs +++ b/vm/src/stdlib/weakref.rs @@ -9,7 +9,7 @@ pub(crate) use _weakref::make_module; #[pymodule] mod _weakref { use crate::builtins::{PyDictRef, PyTypeRef, PyWeak}; - use crate::{PyObjectRef, PyObjectWrap, PyResult, VirtualMachine}; + use crate::{PyObjectRef, PyRef, PyResult, VirtualMachine}; #[pyattr(name = "ref")] fn ref_(vm: &VirtualMachine) -> PyTypeRef { @@ -40,7 +40,7 @@ mod _weakref { #[pyfunction] fn getweakrefs(obj: PyObjectRef) -> Vec { match obj.get_weak_references() { - Some(v) => v.into_iter().map(|weak| weak.into_object()).collect(), + Some(v) => v.into_iter().map(|weak| PyRef::from(weak).into()).collect(), None => vec![], } } diff --git a/vm/src/types/zoo.rs b/vm/src/types/zoo.rs index cef5e98a4..5ea301470 100644 --- a/vm/src/types/zoo.rs +++ b/vm/src/types/zoo.rs @@ -91,6 +91,7 @@ pub struct TypeZoo { } impl TypeZoo { + #[cold] pub(crate) fn init() -> Self { let (type_type, object_type, weakref_type) = crate::pyobject::init_type_hierarchy(); Self { @@ -179,6 +180,7 @@ impl TypeZoo { } /// Fill attributes of builtin types. + #[cold] pub(crate) fn extend(context: &PyContext) { pytype::init(context); object::init(context); diff --git a/vm/src/utils.rs b/vm/src/utils.rs index 8ef67dec6..ca811be2c 100644 --- a/vm/src/utils.rs +++ b/vm/src/utils.rs @@ -1,7 +1,7 @@ use crate::{ builtins::{PyFloat, PyStr}, convert::{ToPyException, ToPyObject}, - AsObject, PyObject, PyObjectRef, PyObjectWrap, PyResult, TryFromObject, VirtualMachine, + AsObject, PyObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine, }; use num_traits::ToPrimitive; use std::borrow::Borrow; @@ -31,12 +31,12 @@ impl, B: AsRef> AsRef for Either { } } -impl PyObjectWrap for Either { +impl, B: Into> From> for PyObjectRef { #[inline(always)] - fn into_object(self) -> PyObjectRef { - match self { - Either::A(a) => a.into_object(), - Either::B(b) => b.into_object(), + fn from(value: Either) -> Self { + match value { + Either::A(a) => a.into(), + Either::B(b) => b.into(), } } } diff --git a/vm/src/vm_new.rs b/vm/src/vm_new.rs index 701cd52e6..6f03565d0 100644 --- a/vm/src/vm_new.rs +++ b/vm/src/vm_new.rs @@ -10,7 +10,7 @@ use crate::{ convert::ToPyObject, scope::Scope, vm::VirtualMachine, - AsObject, PyObject, PyObjectRef, PyObjectWrap, PyRef, PyValue, + AsObject, PyObject, PyObjectRef, PyRef, PyValue, }; /// Collection of object creation helpers @@ -44,7 +44,7 @@ impl VirtualMachine { .unwrap_or_else(|| self.ctx.none()), self, ); - module.into_object() + module.into() } pub fn new_scope_with_builtins(&self) -> Scope { diff --git a/wasm/lib/src/browser_module.rs b/wasm/lib/src/browser_module.rs index 2cb35bcc4..d73031b28 100644 --- a/wasm/lib/src/browser_module.rs +++ b/wasm/lib/src/browser_module.rs @@ -112,7 +112,7 @@ mod _browser { JsFuture::from(response_format.get_response(&response)?).await }; - Ok(PyPromise::from_future(future).into_object(vm)) + Ok(PyPromise::from_future(future).into_pyobject(vm)) } #[pyfunction] @@ -253,7 +253,7 @@ mod _browser { }) }; - Ok(PyPromise::from_future(future).into_object(vm)) + Ok(PyPromise::from_future(future).into_pyobject(vm)) } } diff --git a/wasm/lib/src/js_module.rs b/wasm/lib/src/js_module.rs index 0f908af28..a81281ae9 100644 --- a/wasm/lib/src/js_module.rs +++ b/wasm/lib/src/js_module.rs @@ -14,12 +14,11 @@ mod _js { use js_sys::{Array, Object, Promise, Reflect}; use rustpython_vm::{ builtins::{PyBaseExceptionRef, PyFloat, PyStrRef, PyTypeRef}, - convert::ToPyObject, + convert::{IntoObject, ToPyObject}, function::{ArgCallable, OptionalArg, OptionalOption, PosArgs}, protocol::PyIterReturn, types::{IterNext, IterNextIterable}, - PyObjectRef, PyObjectView, PyObjectWrap, PyRef, PyResult, PyValue, TryFromObject, - VirtualMachine, + PyObjectRef, PyObjectView, PyRef, PyResult, PyValue, TryFromObject, VirtualMachine, }; use std::{cell, fmt, future}; use wasm_bindgen::{closure::Closure, prelude::*, JsCast}; @@ -322,11 +321,11 @@ mod _js { } }; stored_vm_from_wasm(&wasm_vm).interp.enter(move |vm| { - let mut pyargs = vec![PyJsValue::new(this).into_object(vm)]; + let mut pyargs = vec![PyJsValue::new(this).into_pyobject(vm)]; pyargs.extend( Vec::from(args) .into_iter() - .map(|arg| PyJsValue::new(arg).into_object(vm)), + .map(|arg| PyJsValue::new(arg).into_pyobject(vm)), ); let res = vm.invoke(&py_obj, pyargs); convert::pyresult_to_jsresult(vm, res) @@ -524,8 +523,8 @@ mod _js { vm.invoke( then, ( - on_fulfill.map(|c| c.into_object()), - on_reject.map(|c| c.into_object()), + on_fulfill.map(IntoObject::into_object), + on_reject.map(IntoObject::into_object), ), ), vm,