diff --git a/vm/src/builtins/asyncgenerator.rs b/vm/src/builtins/asyncgenerator.rs index 1cc2cbbc1..6db3dee04 100644 --- a/vm/src/builtins/asyncgenerator.rs +++ b/vm/src/builtins/asyncgenerator.rs @@ -1,4 +1,4 @@ -use super::{PyCode, PyGenericAlias, PyStrRef, PyType, PyTypeRef}; +use super::{PyCode, PyGenericAlias, PyStr, PyStrRef, PyType, PyTypeRef}; use crate::{ builtins::PyBaseExceptionRef, class::PyClassImpl, @@ -6,7 +6,7 @@ use crate::{ frame::FrameRef, function::OptionalArg, protocol::PyIterReturn, - types::{Constructor, IterNext, IterNextIterable, Unconstructible}, + types::{Constructor, IterNext, IterNextIterable, Representable, Unconstructible}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -26,7 +26,7 @@ impl PyPayload for PyAsyncGen { } } -#[pyclass(with(Constructor))] +#[pyclass(with(Constructor, Representable))] impl PyAsyncGen { pub fn as_coro(&self) -> &Coro { &self.inner @@ -49,11 +49,6 @@ impl PyAsyncGen { self.inner.set_name(name) } - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> String { - zelf.inner.repr(zelf.as_object(), zelf.get_id(), vm) - } - #[pymethod(magic)] fn aiter(zelf: PyRef, _vm: &VirtualMachine) -> PyRef { zelf @@ -129,6 +124,14 @@ impl PyAsyncGen { PyGenericAlias::new(cls, args, vm) } } + +impl Representable for PyAsyncGen { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + Ok(PyStr::from(zelf.inner.repr(zelf.as_object(), zelf.get_id(), vm)).into_ref(vm)) + } +} + impl Unconstructible for PyAsyncGen {} #[pyclass(module = false, name = "async_generator_wrapped_value")] diff --git a/vm/src/builtins/bool.rs b/vm/src/builtins/bool.rs index 417cc0e8d..756eca49c 100644 --- a/vm/src/builtins/bool.rs +++ b/vm/src/builtins/bool.rs @@ -5,7 +5,7 @@ use crate::{ function::OptionalArg, identifier, protocol::PyNumberMethods, - types::{AsNumber, Constructor}, + types::{AsNumber, Constructor, Representable}, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, TryFromBorrowedObject, VirtualMachine, }; @@ -107,18 +107,8 @@ impl Constructor for PyBool { } } -#[pyclass(with(Constructor, AsNumber))] +#[pyclass(with(Constructor, AsNumber, Representable))] impl PyBool { - #[pymethod(magic)] - fn repr(zelf: bool, vm: &VirtualMachine) -> PyStrRef { - if zelf { - vm.ctx.names.True - } else { - vm.ctx.names.False - } - .to_owned() - } - #[pymethod(magic)] fn format(obj: PyObjectRef, format_spec: PyStrRef, vm: &VirtualMachine) -> PyResult { if format_spec.is_empty() { @@ -189,6 +179,31 @@ impl AsNumber for PyBool { } } +impl Representable for PyBool { + #[inline] + fn slot_repr(zelf: &PyObject, vm: &VirtualMachine) -> PyResult { + if get_value(zelf) { + Ok(vm.ctx.names.True.to_owned()) + } else { + Ok(vm.ctx.names.False.to_owned()) + } + } + + #[inline] + fn __repr__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { + Self::slot_repr(&zelf, vm) + } + + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + if get_value(zelf.as_object()) { + Ok(vm.ctx.names.True.to_owned()) + } else { + Ok(vm.ctx.names.False.to_owned()) + } + } +} + pub(crate) fn init(context: &Context) { PyBool::extend_class(context, context.types.bool_type); } diff --git a/vm/src/builtins/builtin_func.rs b/vm/src/builtins/builtin_func.rs index 8d694abaa..d2bf00db1 100644 --- a/vm/src/builtins/builtin_func.rs +++ b/vm/src/builtins/builtin_func.rs @@ -3,7 +3,7 @@ use crate::{ builtins::PyBoundMethod, class::PyClassImpl, function::{FuncArgs, IntoPyNativeFunc, PyNativeFunc}, - types::{Callable, Constructor, GetDescriptor, Unconstructible}, + types::{Callable, Constructor, GetDescriptor, Representable, Unconstructible}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; use std::fmt; @@ -145,10 +145,6 @@ impl PyBuiltinFunction { fn reduce_ex(&self, _ver: PyObjectRef) -> PyStrRef { self.name() } - #[pymethod(magic)] - fn repr(&self) -> String { - format!("", self.value.name) - } #[pygetset(magic)] fn text_signature(&self) -> Option { self.value.doc.as_ref().and_then(|doc| { @@ -157,6 +153,14 @@ impl PyBuiltinFunction { }) } } + +impl Representable for PyBuiltinFunction { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + Ok(PyStr::from(format!("", zelf.value.name)).into_ref(vm)) + } +} + impl Unconstructible for PyBuiltinFunction {} // `PyBuiltinMethod` is similar to both `PyMethodDescrObject` in @@ -225,7 +229,10 @@ impl PyBuiltinMethod { } } -#[pyclass(with(GetDescriptor, Callable, Constructor), flags(METHOD_DESCR))] +#[pyclass( + with(GetDescriptor, Callable, Constructor, Representable), + flags(METHOD_DESCR) +)] impl PyBuiltinMethod { #[pygetset(magic)] fn name(&self) -> PyStrRef { @@ -247,14 +254,6 @@ impl PyBuiltinMethod { }) } #[pymethod(magic)] - fn repr(&self) -> String { - format!( - "", - &self.value.name, - self.class.name() - ) - } - #[pymethod(magic)] fn reduce( &self, vm: &VirtualMachine, @@ -264,6 +263,19 @@ impl PyBuiltinMethod { (builtins_getattr, (classname, self.value.name.clone())) } } + +impl Representable for PyBuiltinMethod { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + Ok(PyStr::from(format!( + "", + &zelf.value.name, + zelf.class.name() + )) + .into_ref(vm)) + } +} + impl Unconstructible for PyBuiltinMethod {} pub fn init(context: &Context) { diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs index c3498947e..8290b5f33 100644 --- a/vm/src/builtins/bytearray.rs +++ b/vm/src/builtins/bytearray.rs @@ -1,7 +1,7 @@ //! Implementation of the python bytearray object. use super::{ - PositionIterInternal, PyBytes, PyBytesRef, PyDictRef, PyIntRef, PyStrRef, PyTuple, PyTupleRef, - PyType, PyTypeRef, + PositionIterInternal, PyBytes, PyBytesRef, PyDictRef, PyIntRef, PyStr, PyStrRef, PyTuple, + PyTupleRef, PyType, PyTypeRef, }; use crate::{ anystr::{self, AnyStr}, @@ -31,7 +31,7 @@ use crate::{ sliceable::{SequenceIndex, SliceableSequenceMutOp, SliceableSequenceOp}, types::{ AsBuffer, AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Initializer, - IterNext, IterNextIterable, Iterable, PyComparisonOp, Unconstructible, + IterNext, IterNextIterable, Iterable, PyComparisonOp, Representable, Unconstructible, }, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, @@ -107,7 +107,8 @@ pub(crate) fn init(context: &Context) { AsMapping, AsSequence, AsNumber, - Iterable + Iterable, + Representable ) )] impl PyByteArray { @@ -126,13 +127,6 @@ impl PyByteArray { self.inner.write() } - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let class = zelf.class(); - let class_name = class.name(); - zelf.inner().repr(Some(&class_name), vm) - } - #[pymethod(magic)] fn alloc(&self) -> usize { self.inner().capacity() @@ -886,6 +880,17 @@ impl Iterable for PyByteArray { } } +impl Representable for PyByteArray { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let class = zelf.class(); + let class_name = class.name(); + zelf.inner() + .repr(Some(&class_name), vm) + .map(|s| PyStr::from(s).into_ref(vm)) + } +} + // fn set_value(obj: &PyObject, value: Vec) { // obj.borrow_mut().kind = PyObjectPayload::Bytes { value }; // } @@ -922,6 +927,7 @@ impl PyByteArrayIterator { .set_state(state, |obj, pos| pos.min(obj.len()), vm) } } + impl Unconstructible for PyByteArrayIterator {} impl IterNextIterable for PyByteArrayIterator {} diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index d91e0ef63..6fd1a8891 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -1,5 +1,6 @@ use super::{ - PositionIterInternal, PyDictRef, PyIntRef, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef, + PositionIterInternal, PyDictRef, PyIntRef, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType, + PyTypeRef, }; use crate::{ anystr::{self, AnyStr}, @@ -21,7 +22,7 @@ use crate::{ sliceable::{SequenceIndex, SliceableSequenceOp}, types::{ AsBuffer, AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Hashable, - IterNext, IterNextIterable, Iterable, PyComparisonOp, Unconstructible, + IterNext, IterNextIterable, Iterable, PyComparisonOp, Representable, Unconstructible, }, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject, TryFromObject, VirtualMachine, @@ -125,15 +126,11 @@ impl PyBytes { AsBuffer, Iterable, Constructor, - AsNumber + AsNumber, + Representable, ) )] impl PyBytes { - #[pymethod(magic)] - pub(crate) fn repr(&self, vm: &VirtualMachine) -> PyResult { - self.inner.repr(None, vm) - } - #[pymethod(magic)] #[inline] pub fn len(&self) -> usize { @@ -689,6 +686,15 @@ impl Iterable for PyBytes { } } +impl Representable for PyBytes { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + zelf.inner + .repr(None, vm) + .map(|s| PyStr::from(s).into_ref(vm)) + } +} + #[pyclass(module = false, name = "bytes_iterator")] #[derive(Debug)] pub struct PyBytesIterator { diff --git a/vm/src/builtins/classmethod.rs b/vm/src/builtins/classmethod.rs index 6398108ad..6f7d0ffe0 100644 --- a/vm/src/builtins/classmethod.rs +++ b/vm/src/builtins/classmethod.rs @@ -1,8 +1,8 @@ -use super::{PyBoundMethod, PyStr, PyType, PyTypeRef}; +use super::{PyBoundMethod, PyStr, PyStrRef, PyType, PyTypeRef}; use crate::{ class::PyClassImpl, common::lock::PyMutex, - types::{Constructor, GetDescriptor, Initializer}, + types::{Constructor, GetDescriptor, Initializer, Representable}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -104,7 +104,10 @@ impl PyClassMethod { } } -#[pyclass(with(GetDescriptor, Constructor), flags(BASETYPE, HAS_DICT))] +#[pyclass( + with(GetDescriptor, Constructor, Representable), + flags(BASETYPE, HAS_DICT) +)] impl PyClassMethod { #[pygetset(magic)] fn func(&self) -> PyObjectRef { @@ -136,26 +139,6 @@ impl PyClassMethod { self.callable.lock().get_attr("__annotations__", vm) } - #[pymethod(magic)] - fn repr(&self, vm: &VirtualMachine) -> Option { - let callable = self.callable.lock().repr(vm).unwrap(); - let class = Self::class(vm); - - match ( - class - .qualname(vm) - .downcast_ref::() - .map(|n| n.as_str()), - class.module(vm).downcast_ref::().map(|m| m.as_str()), - ) { - (None, _) => None, - (Some(qualname), Some(module)) if module != "builtins" => { - Some(format!("<{module}.{qualname}({callable})>")) - } - _ => Some(format!("<{}({})>", class.slot_name(), callable)), - } - } - #[pygetset(magic)] fn isabstractmethod(&self, vm: &VirtualMachine) -> PyObjectRef { match vm.get_attribute_opt(self.callable.lock().clone(), "__isabstractmethod__") { @@ -173,6 +156,28 @@ impl PyClassMethod { } } +impl Representable for PyClassMethod { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let callable = zelf.callable.lock().repr(vm).unwrap(); + let class = Self::class(vm); + + match ( + class + .qualname(vm) + .downcast_ref::() + .map(|n| n.as_str()), + class.module(vm).downcast_ref::().map(|m| m.as_str()), + ) { + (None, _) => Err(vm.new_type_error("Unknown qualified name".into())), + (Some(qualname), Some(module)) if module != "builtins" => { + Ok(PyStr::from(format!("<{module}.{qualname}({callable})>")).into_ref(vm)) + } + _ => Ok(PyStr::from(format!("<{}({})>", class.slot_name(), callable)).into_ref(vm)), + } + } +} + pub(crate) fn init(context: &Context) { PyClassMethod::extend_class(context, context.types.classmethod_type); } diff --git a/vm/src/builtins/code.rs b/vm/src/builtins/code.rs index 14022f93c..60b782fd2 100644 --- a/vm/src/builtins/code.rs +++ b/vm/src/builtins/code.rs @@ -2,13 +2,14 @@ */ -use super::{PyStrRef, PyTupleRef, PyType, PyTypeRef}; +use super::{PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef}; use crate::{ builtins::PyStrInterned, bytecode::{self, AsBag, BorrowedConstant, CodeFlags, Constant, ConstantBag}, class::{PyClassImpl, StaticType}, convert::ToPyObject, function::{FuncArgs, OptionalArg}, + types::Representable, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; use num_traits::Zero; @@ -215,9 +216,24 @@ impl PyPayload for PyCode { } } -#[pyclass(with(PyRef))] +#[pyclass(with(PyRef, Representable))] impl PyCode {} +impl Representable for PyCode { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let code = &zelf.code; + Ok(PyStr::from(format!( + "", + code.obj_name, + zelf.get_id(), + code.source_path.as_str(), + code.first_line_number + )) + .into_ref(vm)) + } +} + #[pyclass] impl PyRef { #[pyslot] @@ -225,18 +241,6 @@ impl PyRef { Err(vm.new_type_error("Cannot directly create code object".to_owned())) } - #[pymethod(magic)] - fn repr(self) -> String { - let code = &self.code; - format!( - "", - code.obj_name, - self.get_id(), - code.source_path.as_str(), - code.first_line_number - ) - } - #[pygetset] fn co_posonlyargcount(self) -> usize { self.code.posonlyarg_count as usize diff --git a/vm/src/builtins/complex.rs b/vm/src/builtins/complex.rs index 4146b29b6..0835d51ab 100644 --- a/vm/src/builtins/complex.rs +++ b/vm/src/builtins/complex.rs @@ -1,4 +1,4 @@ -use super::{float, PyStr, PyType, PyTypeRef}; +use super::{float, PyStr, PyStrRef, PyType, PyTypeRef}; use crate::{ class::PyClassImpl, convert::{ToPyObject, ToPyResult}, @@ -9,7 +9,7 @@ use crate::{ }, identifier, protocol::{PyNumber, PyNumberMethods}, - types::{AsNumber, Comparable, Constructor, Hashable, PyComparisonOp}, + types::{AsNumber, Comparable, Constructor, Hashable, PyComparisonOp, Representable}, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; use num_complex::Complex64; @@ -211,7 +211,10 @@ impl PyComplex { } } -#[pyclass(flags(BASETYPE), with(Comparable, Hashable, Constructor, AsNumber))] +#[pyclass( + flags(BASETYPE), + with(Comparable, Hashable, Constructor, AsNumber, Representable) +)] impl PyComplex { #[pymethod(magic)] fn complex(zelf: PyRef, vm: &VirtualMachine) -> PyRef { @@ -331,44 +334,6 @@ impl PyComplex { -self.value } - #[pymethod(magic)] - fn repr(&self) -> String { - // TODO: when you fix this, move it to rustpython_common::complex::repr and update - // ast/src/unparse.rs + impl Display for Constant in ast/src/constant.rs - let Complex64 { re, im } = self.value; - // integer => drop ., fractional => float_ops - let mut im_part = if im.fract() == 0.0 { - im.to_string() - } else { - float_ops::to_string(im) - }; - im_part.push('j'); - - // positive empty => return im_part, integer => drop ., fractional => float_ops - let re_part = if re == 0.0 { - if re.is_sign_positive() { - return im_part; - } else { - re.to_string() - } - } else if re.fract() == 0.0 { - re.to_string() - } else { - float_ops::to_string(re) - }; - let mut result = String::with_capacity( - re_part.len() + im_part.len() + 2 + im.is_sign_positive() as usize, - ); - result.push('('); - result.push_str(&re_part); - if im.is_sign_positive() || im.is_nan() { - result.push('+'); - } - result.push_str(&im_part); - result.push(')'); - result - } - #[pymethod(magic)] fn pow( &self, @@ -488,6 +453,46 @@ impl AsNumber for PyComplex { } } +impl Representable for PyComplex { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + // TODO: when you fix this, move it to rustpython_common::complex::repr and update + // ast/src/unparse.rs + impl Display for Constant in ast/src/constant.rs + let Complex64 { re, im } = zelf.value; + // integer => drop ., fractional => float_ops + let mut im_part = if im.fract() == 0.0 { + im.to_string() + } else { + float_ops::to_string(im) + }; + im_part.push('j'); + + // positive empty => return im_part, integer => drop ., fractional => float_ops + let re_part = if re == 0.0 { + if re.is_sign_positive() { + return Ok(PyStr::from(im_part).into_ref(vm)); + } else { + re.to_string() + } + } else if re.fract() == 0.0 { + re.to_string() + } else { + float_ops::to_string(re) + }; + let mut result = String::with_capacity( + re_part.len() + im_part.len() + 2 + im.is_sign_positive() as usize, + ); + result.push('('); + result.push_str(&re_part); + if im.is_sign_positive() || im.is_nan() { + result.push('+'); + } + result.push_str(&im_part); + result.push(')'); + Ok(PyStr::from(result).into_ref(vm)) + } +} + impl PyComplex { fn number_op(number: PyNumber, other: &PyObject, op: F, vm: &VirtualMachine) -> PyResult where diff --git a/vm/src/builtins/coroutine.rs b/vm/src/builtins/coroutine.rs index f64cb1dec..e1d2d7c5f 100644 --- a/vm/src/builtins/coroutine.rs +++ b/vm/src/builtins/coroutine.rs @@ -1,11 +1,11 @@ -use super::{PyCode, PyStrRef, PyType}; +use super::{PyCode, PyStr, PyStrRef, PyType}; use crate::{ class::PyClassImpl, coroutine::Coro, frame::FrameRef, function::OptionalArg, protocol::PyIterReturn, - types::{Constructor, IterNext, IterNextIterable, Unconstructible}, + types::{Constructor, IterNext, IterNextIterable, Representable, Unconstructible}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -22,7 +22,7 @@ impl PyPayload for PyCoroutine { } } -#[pyclass(with(Constructor, IterNext))] +#[pyclass(with(Constructor, IterNext, Representable))] impl PyCoroutine { pub fn as_coro(&self) -> &Coro { &self.inner @@ -44,11 +44,6 @@ impl PyCoroutine { self.inner.set_name(name) } - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> String { - zelf.inner.repr(zelf.as_object(), zelf.get_id(), vm) - } - #[pymethod] fn send(zelf: PyRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult { zelf.inner.send(zelf.as_object(), value, vm) @@ -106,6 +101,13 @@ impl PyCoroutine { } impl Unconstructible for PyCoroutine {} +impl Representable for PyCoroutine { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + Ok(PyStr::from(zelf.inner.repr(zelf.as_object(), zelf.get_id(), vm)).into_ref(vm)) + } +} + impl IterNextIterable for PyCoroutine {} impl IterNext for PyCoroutine { fn next(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/builtins/descriptor.rs b/vm/src/builtins/descriptor.rs index ba2eca5a1..ad9f1b83d 100644 --- a/vm/src/builtins/descriptor.rs +++ b/vm/src/builtins/descriptor.rs @@ -1,8 +1,8 @@ -use super::{PyStr, PyType, PyTypeRef}; +use super::{PyStr, PyStrRef, PyType, PyTypeRef}; use crate::{ class::PyClassImpl, function::PySetterValue, - types::{Constructor, GetDescriptor, Unconstructible}, + types::{Constructor, GetDescriptor, Representable, Unconstructible}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; use rustpython_common::lock::PyRwLock; @@ -100,17 +100,8 @@ fn calculate_qualname(descr: &DescrObject, vm: &VirtualMachine) -> PyResult) -> String { - format!( - "", - zelf.common.name, - zelf.common.typ.name(), - ) - } - #[pygetset(magic)] fn doc(zelf: PyRef) -> Option { zelf.member.doc.to_owned() @@ -197,6 +188,18 @@ fn set_slot_at_object( impl Unconstructible for MemberDescrObject {} +impl Representable for MemberDescrObject { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + Ok(PyStr::from(format!( + "", + zelf.common.name, + zelf.common.typ.name(), + )) + .into_ref(vm)) + } +} + impl GetDescriptor for MemberDescrObject { fn descr_get( zelf: PyObjectRef, diff --git a/vm/src/builtins/dict.rs b/vm/src/builtins/dict.rs index 514dd54f5..0789c3104 100644 --- a/vm/src/builtins/dict.rs +++ b/vm/src/builtins/dict.rs @@ -1,6 +1,6 @@ use super::{ set::PySetInner, IterStatus, PositionIterInternal, PyBaseExceptionRef, PyGenericAlias, - PyMappingProxy, PySet, PyStr, PyTupleRef, PyType, PyTypeRef, + PyMappingProxy, PySet, PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef, }; use crate::{ atomic_func, @@ -20,7 +20,7 @@ use crate::{ recursion::ReprGuard, types::{ AsMapping, AsNumber, AsSequence, Callable, Comparable, Constructor, Initializer, IterNext, - IterNextIterable, Iterable, PyComparisonOp, Unconstructible, + IterNextIterable, Iterable, PyComparisonOp, Representable, Unconstructible, }, vm::VirtualMachine, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult, @@ -209,7 +209,8 @@ impl PyDict { Comparable, Iterable, AsSequence, - AsNumber + AsNumber, + Representable ), flags(BASETYPE) )] @@ -254,23 +255,6 @@ impl PyDict { std::mem::size_of::() + self.entries.sizeof() } - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let s = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { - let mut str_parts = Vec::with_capacity(zelf.len()); - for (key, value) in zelf { - let key_repr = &key.repr(vm)?; - let value_repr = value.repr(vm)?; - str_parts.push(format!("{key_repr}: {value_repr}")); - } - - format!("{{{}}}", str_parts.join(", ")) - } else { - "{...}".to_owned() - }; - Ok(s) - } - #[pymethod(magic)] fn contains(&self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult { self.entries.contains(vm, &*key) @@ -520,6 +504,25 @@ impl Iterable for PyDict { } } +impl Representable for PyDict { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let s = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { + let mut str_parts = Vec::with_capacity(zelf.len()); + for (key, value) in zelf { + let key_repr = &key.repr(vm)?; + let value_repr = value.repr(vm)?; + str_parts.push(format!("{key_repr}: {value_repr}")); + } + + format!("{{{}}}", str_parts.join(", ")) + } else { + "{...}".to_owned() + }; + Ok(PyStr::from(s).into_ref(vm)) + } +} + impl Py { #[inline] fn exact_dict(&self, vm: &VirtualMachine) -> bool { @@ -727,7 +730,7 @@ impl ExactSizeIterator for DictIter<'_> { } #[pyclass] -trait DictView: PyPayload + PyClassDef + Iterable +trait DictView: PyPayload + PyClassDef + Iterable + Representable where Self::ReverseIter: PyPayload, { @@ -741,21 +744,6 @@ where self.dict().len() } - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let s = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { - let mut str_parts = Vec::with_capacity(zelf.len()); - for (key, value) in zelf.dict().clone() { - let s = &Self::item(vm, key, value).repr(vm)?; - str_parts.push(s.as_str().to_owned()); - } - format!("{}([{}])", Self::NAME, str_parts.join(", ")) - } else { - "{...}".to_owned() - }; - Ok(s) - } - #[pymethod(magic)] fn reversed(&self) -> Self::ReverseIter; } @@ -802,6 +790,23 @@ macro_rules! dict_view { } } + impl Representable for $name { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let s = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { + let mut str_parts = Vec::with_capacity(zelf.len()); + for (key, value) in zelf.dict().clone() { + let s = &Self::item(vm, key, value).repr(vm)?; + str_parts.push(s.as_str().to_owned()); + } + format!("{}([{}])", Self::NAME, str_parts.join(", ")) + } else { + "{...}".to_owned() + }; + Ok(PyStr::from(s).into_ref(vm)) + } + } + #[pyclass(module = false, name = $iter_class_name)] #[derive(Debug)] pub(crate) struct $iter_name { @@ -1099,7 +1104,8 @@ impl ViewSetOps for PyDictKeys {} Iterable, ViewSetOps, AsSequence, - AsNumber + AsNumber, + Representable ))] impl PyDictKeys { #[pymethod(magic)] @@ -1203,7 +1209,8 @@ impl ViewSetOps for PyDictItems {} Iterable, ViewSetOps, AsSequence, - AsNumber + AsNumber, + Representable ))] impl PyDictItems { #[pymethod(magic)] @@ -1313,7 +1320,7 @@ impl AsNumber for PyDictItems { } } -#[pyclass(with(DictView, Constructor, Iterable, AsSequence))] +#[pyclass(with(DictView, Constructor, Iterable, AsSequence, Representable))] impl PyDictValues { #[pygetset] fn mapping(zelf: PyRef) -> PyMappingProxy { diff --git a/vm/src/builtins/float.rs b/vm/src/builtins/float.rs index 6f0b5fda6..e40f78a87 100644 --- a/vm/src/builtins/float.rs +++ b/vm/src/builtins/float.rs @@ -14,7 +14,7 @@ use crate::{ PyComparisonValue, }, protocol::{PyNumber, PyNumberMethods}, - types::{AsNumber, Callable, Comparable, Constructor, Hashable, PyComparisonOp}, + types::{AsNumber, Callable, Comparable, Constructor, Hashable, PyComparisonOp, Representable}, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject, TryFromObject, VirtualMachine, }; @@ -185,7 +185,10 @@ fn float_from_string(val: PyObjectRef, vm: &VirtualMachine) -> PyResult { }) } -#[pyclass(flags(BASETYPE), with(Comparable, Hashable, Constructor, AsNumber))] +#[pyclass( + flags(BASETYPE), + with(Comparable, Hashable, Constructor, AsNumber, Representable) +)] impl PyFloat { #[pymethod(magic)] fn format(&self, spec: PyStrRef, vm: &VirtualMachine) -> PyResult { @@ -356,11 +359,6 @@ impl PyFloat { self.simple_op(other, |a, b| Ok(b - a), vm) } - #[pymethod(magic)] - fn repr(&self) -> String { - float_ops::to_string(self.value) - } - #[pymethod(magic)] fn truediv(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult> { self.simple_op(other, |a, b| inner_div(a, b, vm), vm) @@ -580,6 +578,13 @@ impl AsNumber for PyFloat { } } +impl Representable for PyFloat { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + Ok(PyStr::from(float_ops::to_string(zelf.value)).into_ref(vm)) + } +} + impl PyFloat { fn number_op(number: PyNumber, other: &PyObject, op: F, vm: &VirtualMachine) -> PyResult where diff --git a/vm/src/builtins/frame.rs b/vm/src/builtins/frame.rs index 52d577ace..43654efb5 100644 --- a/vm/src/builtins/frame.rs +++ b/vm/src/builtins/frame.rs @@ -2,12 +2,13 @@ */ -use super::{PyCode, PyDictRef, PyIntRef}; +use super::{PyCode, PyDictRef, PyIntRef, PyStr, PyStrRef}; use crate::{ class::PyClassImpl, frame::{Frame, FrameRef}, function::PySetterValue, - types::{Constructor, Unconstructible}, + object::PyPayload, + types::{Constructor, Representable, Unconstructible}, AsObject, Context, PyObjectRef, PyRef, PyResult, VirtualMachine, }; use num_traits::Zero; @@ -16,17 +17,20 @@ pub fn init(context: &Context) { FrameRef::extend_class(context, context.types.frame_type); } -#[pyclass(with(Constructor, PyRef))] +#[pyclass(with(Constructor, PyRef, Representable))] impl Frame {} + impl Unconstructible for Frame {} +impl Representable for Frame { + #[inline] + fn repr(_zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + Ok(PyStr::from("").into_ref(vm)) + } +} + #[pyclass] impl FrameRef { - #[pymethod(magic)] - fn repr(self) -> String { - "".to_owned() - } - #[pymethod] fn clear(self) { // TODO diff --git a/vm/src/builtins/function.rs b/vm/src/builtins/function.rs index d6d85fead..705d2ce62 100644 --- a/vm/src/builtins/function.rs +++ b/vm/src/builtins/function.rs @@ -16,7 +16,9 @@ use crate::{ frame::Frame, function::{FuncArgs, OptionalArg, PyComparisonValue, PySetterValue}, scope::Scope, - types::{Callable, Comparable, Constructor, GetAttr, GetDescriptor, PyComparisonOp}, + types::{ + Callable, Comparable, Constructor, GetAttr, GetDescriptor, PyComparisonOp, Representable, + }, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; use itertools::Itertools; @@ -337,7 +339,10 @@ impl PyPayload for PyFunction { } } -#[pyclass(with(GetDescriptor, Callable), flags(HAS_DICT, METHOD_DESCR))] +#[pyclass( + with(GetDescriptor, Callable, Representable), + flags(HAS_DICT, METHOD_DESCR) +)] impl PyFunction { #[pygetset(magic)] fn code(&self) -> PyRef { @@ -414,11 +419,6 @@ impl PyFunction { Ok(()) } - #[pymethod(magic)] - fn repr(zelf: PyRef) -> String { - format!("", zelf.qualname(), zelf.get_id()) - } - #[cfg(feature = "jit")] #[pymethod(magic)] fn jit(zelf: PyRef, vm: &VirtualMachine) -> PyResult<()> { @@ -457,6 +457,18 @@ impl Callable for PyFunction { } } +impl Representable for PyFunction { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + Ok(PyStr::from(format!( + "", + zelf.qualname(), + zelf.get_id() + )) + .into_ref(vm)) + } +} + #[pyclass(module = false, name = "method")] #[derive(Debug)] pub struct PyBoundMethod { diff --git a/vm/src/builtins/generator.rs b/vm/src/builtins/generator.rs index 3eeff40aa..543e33bf2 100644 --- a/vm/src/builtins/generator.rs +++ b/vm/src/builtins/generator.rs @@ -2,14 +2,14 @@ * The mythical generator. */ -use super::{PyCode, PyStrRef, PyType}; +use super::{PyCode, PyStr, PyStrRef, PyType}; use crate::{ class::PyClassImpl, coroutine::Coro, frame::FrameRef, function::OptionalArg, protocol::PyIterReturn, - types::{Constructor, IterNext, IterNextIterable, Unconstructible}, + types::{Constructor, IterNext, IterNextIterable, Representable, Unconstructible}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -47,11 +47,6 @@ impl PyGenerator { self.inner.set_name(name) } - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> String { - zelf.inner.repr(zelf.as_object(), zelf.get_id(), vm) - } - #[pymethod] fn send(zelf: PyRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult { zelf.inner.send(zelf.as_object(), value, vm) @@ -96,8 +91,16 @@ impl PyGenerator { self.inner.frame().yield_from_target() } } + impl Unconstructible for PyGenerator {} +impl Representable for PyGenerator { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + Ok(PyStr::from(zelf.inner.repr(zelf.as_object(), zelf.get_id(), vm)).into_ref(vm)) + } +} + impl IterNextIterable for PyGenerator {} impl IterNext for PyGenerator { fn next(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/builtins/genericalias.rs b/vm/src/builtins/genericalias.rs index eff834132..fbd35ec41 100644 --- a/vm/src/builtins/genericalias.rs +++ b/vm/src/builtins/genericalias.rs @@ -9,7 +9,10 @@ use crate::{ convert::ToPyObject, function::{FuncArgs, PyComparisonValue}, protocol::PyMappingMethods, - types::{AsMapping, Callable, Comparable, Constructor, GetAttr, Hashable, PyComparisonOp}, + types::{ + AsMapping, Callable, Comparable, Constructor, GetAttr, Hashable, PyComparisonOp, + Representable, + }, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, }; @@ -60,7 +63,15 @@ impl Constructor for PyGenericAlias { } #[pyclass( - with(AsMapping, Callable, Comparable, Constructor, GetAttr, Hashable), + with( + AsMapping, + Callable, + Comparable, + Constructor, + GetAttr, + Hashable, + Representable + ), flags(BASETYPE) )] impl PyGenericAlias { @@ -79,7 +90,6 @@ impl PyGenericAlias { } } - #[pymethod(magic)] fn repr(&self, vm: &VirtualMachine) -> PyResult { fn repr_item(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { if obj.is(&vm.ctx.ellipsis) { @@ -390,6 +400,13 @@ impl GetAttr for PyGenericAlias { } } +impl Representable for PyGenericAlias { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + zelf.repr(vm).map(|s| PyStr::from(s).into_ref(vm)) + } +} + pub fn init(context: &Context) { let generic_alias_type = &context.types.generic_alias_type; PyGenericAlias::extend_class(context, generic_alias_type); diff --git a/vm/src/builtins/int.rs b/vm/src/builtins/int.rs index 4c49aba68..ae174193f 100644 --- a/vm/src/builtins/int.rs +++ b/vm/src/builtins/int.rs @@ -10,7 +10,7 @@ use crate::{ PyComparisonValue, }, protocol::{PyNumber, PyNumberMethods}, - types::{AsNumber, Comparable, Constructor, Hashable, PyComparisonOp}, + types::{AsNumber, Comparable, Constructor, Hashable, PyComparisonOp, Representable}, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject, VirtualMachine, }; @@ -316,7 +316,10 @@ impl PyInt { } } -#[pyclass(flags(BASETYPE), with(Comparable, Hashable, Constructor, AsNumber))] +#[pyclass( + flags(BASETYPE), + with(Comparable, Hashable, Constructor, AsNumber, Representable) +)] impl PyInt { #[pymethod(name = "__radd__")] #[pymethod(magic)] @@ -555,11 +558,6 @@ impl PyInt { !(&self.value) } - #[pymethod(magic)] - pub(crate) fn repr(&self) -> String { - self.value.to_string() - } - #[pymethod(magic)] fn format(&self, spec: PyStrRef, vm: &VirtualMachine) -> PyResult { FormatSpec::parse(spec.as_str()) @@ -714,6 +712,13 @@ impl Comparable for PyInt { } } +impl Representable for PyInt { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + Ok(PyStr::from(zelf.value.to_string()).into_ref(vm)) + } +} + impl Hashable for PyInt { #[inline] fn hash(zelf: &crate::Py, _vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/builtins/list.rs b/vm/src/builtins/list.rs index 1e976d7cd..94184c8d9 100644 --- a/vm/src/builtins/list.rs +++ b/vm/src/builtins/list.rs @@ -1,4 +1,4 @@ -use super::{PositionIterInternal, PyGenericAlias, PyTupleRef, PyType, PyTypeRef}; +use super::{PositionIterInternal, PyGenericAlias, PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef}; use crate::atomic_func; use crate::common::lock::{ PyMappedRwLockReadGuard, PyMutex, PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard, @@ -14,7 +14,7 @@ use crate::{ sliceable::{SequenceIndex, SliceableSequenceMutOp, SliceableSequenceOp}, types::{ AsMapping, AsSequence, Comparable, Constructor, Initializer, IterNext, IterNextIterable, - Iterable, PyComparisonOp, Unconstructible, + Iterable, PyComparisonOp, Representable, Unconstructible, }, utils::collection_repr, vm::VirtualMachine, @@ -97,7 +97,15 @@ pub(crate) struct SortOptions { pub type PyListRef = PyRef; #[pyclass( - with(Constructor, Initializer, AsMapping, Iterable, Comparable, AsSequence), + with( + Constructor, + Initializer, + AsMapping, + Iterable, + Comparable, + AsSequence, + Representable + ), flags(BASETYPE) )] impl PyList { @@ -229,18 +237,6 @@ impl PyList { self._setitem(&needle, value, vm) } - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let s = if zelf.len() == 0 { - "[]".to_owned() - } else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { - collection_repr(None, "[", "]", zelf.borrow_vec().iter(), vm)? - } else { - "[...]".to_owned() - }; - Ok(s) - } - #[pymethod(magic)] #[pymethod(name = "__rmul__")] fn mul(&self, n: ArgSize, vm: &VirtualMachine) -> PyResult> { @@ -493,6 +489,20 @@ impl Comparable for PyList { } } +impl Representable for PyList { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let s = if zelf.len() == 0 { + "[]".to_owned() + } else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { + collection_repr(None, "[", "]", zelf.borrow_vec().iter(), vm)? + } else { + "[...]".to_owned() + }; + Ok(PyStr::from(s).into_ref(vm)) + } +} + fn do_sort( vm: &VirtualMachine, values: &mut Vec, diff --git a/vm/src/builtins/mappingproxy.rs b/vm/src/builtins/mappingproxy.rs index 25ddcb03b..81a5f553e 100644 --- a/vm/src/builtins/mappingproxy.rs +++ b/vm/src/builtins/mappingproxy.rs @@ -1,11 +1,16 @@ -use super::{PyDict, PyDictRef, PyGenericAlias, PyList, PyTuple, PyType, PyTypeRef}; +use super::{ + PyDict, PyDictRef, PyGenericAlias, PyList, PyStr, PyStrRef, PyTuple, PyType, PyTypeRef, +}; use crate::{ atomic_func, class::PyClassImpl, convert::ToPyObject, function::{ArgMapping, OptionalArg, PyComparisonValue}, protocol::{PyMapping, PyMappingMethods, PyNumberMethods, PySequenceMethods}, - types::{AsMapping, AsNumber, AsSequence, Comparable, Constructor, Iterable, PyComparisonOp}, + types::{ + AsMapping, AsNumber, AsSequence, Comparable, Constructor, Iterable, PyComparisonOp, + Representable, + }, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; use once_cell::sync::Lazy; @@ -69,7 +74,15 @@ impl Constructor for PyMappingProxy { } } -#[pyclass(with(AsMapping, Iterable, Constructor, AsSequence, Comparable, AsNumber))] +#[pyclass(with( + AsMapping, + Iterable, + Constructor, + AsSequence, + Comparable, + AsNumber, + Representable +))] impl PyMappingProxy { fn get_inner(&self, key: PyObjectRef, vm: &VirtualMachine) -> PyResult> { let opt = match &self.mapping { @@ -149,11 +162,6 @@ impl PyMappingProxy { } } } - #[pymethod(magic)] - fn repr(&self, vm: &VirtualMachine) -> PyResult { - let obj = self.to_object(vm)?; - Ok(format!("mappingproxy({})", obj.repr(vm)?)) - } #[pyclassmethod(magic)] fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { @@ -260,6 +268,14 @@ impl Iterable for PyMappingProxy { } } +impl Representable for PyMappingProxy { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let obj = zelf.to_object(vm)?; + Ok(PyStr::from(format!("mappingproxy({})", obj.repr(vm)?)).into_ref(vm)) + } +} + pub fn init(context: &Context) { PyMappingProxy::extend_class(context, context.types.mappingproxy_type) } diff --git a/vm/src/builtins/memory.rs b/vm/src/builtins/memory.rs index 039a48d4f..866d11f3a 100644 --- a/vm/src/builtins/memory.rs +++ b/vm/src/builtins/memory.rs @@ -22,7 +22,7 @@ use crate::{ sliceable::SequenceIndexOp, types::{ AsBuffer, AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, - IterNextIterable, Iterable, PyComparisonOp, Unconstructible, + IterNextIterable, Iterable, PyComparisonOp, Representable, Unconstructible, }, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject, TryFromObject, VirtualMachine, @@ -75,7 +75,8 @@ impl Constructor for PyMemoryView { AsMapping, AsSequence, Constructor, - Iterable + Iterable, + Representable ))] impl PyMemoryView { fn parse_format(format: &str, vm: &VirtualMachine) -> PyResult { @@ -593,15 +594,6 @@ impl PyMemoryView { Ok(other.into_ref(vm)) } - #[pymethod(magic)] - fn repr(zelf: PyRef) -> String { - if zelf.released.load() { - format!("", zelf.get_id()) - } else { - format!("", zelf.get_id()) - } - } - #[pymethod] fn hex( &self, @@ -1058,6 +1050,17 @@ impl PyPayload for PyMemoryView { } } +impl Representable for PyMemoryView { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + if zelf.released.load() { + Ok(PyStr::from(format!("", zelf.get_id())).into_ref(vm)) + } else { + Ok(PyStr::from(format!("", zelf.get_id())).into_ref(vm)) + } + } +} + pub(crate) fn init(ctx: &Context) { PyMemoryView::extend_class(ctx, ctx.types.memoryview_type); PyMemoryViewIterator::extend_class(ctx, ctx.types.memoryviewiterator_type); diff --git a/vm/src/builtins/module.rs b/vm/src/builtins/module.rs index d73c02540..5651319c7 100644 --- a/vm/src/builtins/module.rs +++ b/vm/src/builtins/module.rs @@ -5,7 +5,7 @@ use crate::{ class::PyClassImpl, convert::ToPyObject, function::FuncArgs, - types::{GetAttr, Initializer}, + types::{GetAttr, Initializer, Representable}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -26,7 +26,7 @@ pub struct ModuleInitArgs { doc: Option, } -#[pyclass(with(GetAttr, Initializer), flags(BASETYPE, HAS_DICT))] +#[pyclass(with(GetAttr, Initializer, Representable), flags(BASETYPE, HAS_DICT))] impl PyModule { // pub(crate) fn new(d: PyDictRef) -> Self { // PyModule { dict: d.into() } @@ -67,13 +67,6 @@ impl PyModule { .and_then(|obj| obj.downcast::().ok()) } - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let importlib = vm.import("_frozen_importlib", None, 0)?; - let module_repr = importlib.get_attr("_module_repr", vm)?; - module_repr.call((zelf,), vm) - } - #[pymethod(magic)] fn dir(zelf: PyRef, vm: &VirtualMachine) -> PyResult> { let dict = zelf @@ -147,6 +140,18 @@ impl GetAttr for PyModule { } } +impl Representable for PyModule { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let importlib = vm.import("_frozen_importlib", None, 0)?; + let module_repr = importlib.get_attr("_module_repr", vm)?; + module_repr.call((zelf.to_owned(),), vm).and_then(|obj| { + obj.downcast() + .map_err(|_| vm.new_type_error("_module_repr did not return a string".into())) + }) + } +} + pub(crate) fn init(context: &Context) { PyModule::extend_class(context, context.types.module_type); } diff --git a/vm/src/builtins/namespace.rs b/vm/src/builtins/namespace.rs index aec75659a..7d3f73d05 100644 --- a/vm/src/builtins/namespace.rs +++ b/vm/src/builtins/namespace.rs @@ -1,10 +1,10 @@ -use super::{PyType, PyTypeRef}; +use super::{PyStr, PyStrRef, PyType, PyTypeRef}; use crate::{ builtins::PyDict, class::PyClassImpl, function::{FuncArgs, PyComparisonValue}, recursion::ReprGuard, - types::{Comparable, Constructor, Initializer, PyComparisonOp}, + types::{Comparable, Constructor, Initializer, PyComparisonOp, Representable}, AsObject, Context, Py, PyObject, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -39,33 +39,11 @@ impl PyNamespace { } } -#[pyclass(flags(BASETYPE, HAS_DICT), with(Constructor, Initializer, Comparable))] -impl PyNamespace { - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let o = zelf.as_object(); - let name = if o.class().is(vm.ctx.types.namespace_type) { - "namespace".to_owned() - } else { - o.class().slot_name() - }; - - let repr = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { - let dict = zelf.as_object().dict().unwrap(); - let mut parts = Vec::with_capacity(dict.len()); - for (key, value) in dict { - let k = &key.repr(vm)?; - let key_str = k.as_str(); - let value_repr = value.repr(vm)?; - parts.push(format!("{}={}", &key_str[1..key_str.len() - 1], value_repr)); - } - format!("{}({})", name, parts.join(", ")) - } else { - format!("{name}(...)") - }; - Ok(repr) - } -} +#[pyclass( + flags(BASETYPE, HAS_DICT), + with(Constructor, Initializer, Comparable, Representable) +)] +impl PyNamespace {} impl Initializer for PyNamespace { type Args = FuncArgs; @@ -97,6 +75,33 @@ impl Comparable for PyNamespace { } } +impl Representable for PyNamespace { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let o = zelf.as_object(); + let name = if o.class().is(vm.ctx.types.namespace_type) { + "namespace".to_owned() + } else { + o.class().slot_name() + }; + + let repr = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { + let dict = zelf.as_object().dict().unwrap(); + let mut parts = Vec::with_capacity(dict.len()); + for (key, value) in dict { + let k = &key.repr(vm)?; + let key_str = k.as_str(); + let value_repr = value.repr(vm)?; + parts.push(format!("{}={}", &key_str[1..key_str.len() - 1], value_repr)); + } + format!("{}({})", name, parts.join(", ")) + } else { + format!("{name}(...)") + }; + Ok(PyStr::from(repr).into_ref(vm)) + } +} + pub fn init(context: &Context) { PyNamespace::extend_class(context, context.types.namespace_type); } diff --git a/vm/src/builtins/object.rs b/vm/src/builtins/object.rs index 6beef5eaa..2d6aad0c7 100644 --- a/vm/src/builtins/object.rs +++ b/vm/src/builtins/object.rs @@ -187,11 +187,9 @@ impl PyBaseObject { zelf.repr(vm) } - /// Return repr(self). - #[pymethod(magic)] - fn repr(zelf: PyObjectRef, vm: &VirtualMachine) -> Option { + #[pyslot] + fn slot_repr(zelf: &PyObject, vm: &VirtualMachine) -> PyResult { let class = zelf.class(); - match ( class .qualname(vm) @@ -199,21 +197,29 @@ impl PyBaseObject { .map(|n| n.as_str()), class.module(vm).downcast_ref::().map(|m| m.as_str()), ) { - (None, _) => None, - (Some(qualname), Some(module)) if module != "builtins" => Some(format!( + (None, _) => Err(vm.new_type_error("Unknown qualified name".into())), + (Some(qualname), Some(module)) if module != "builtins" => Ok(PyStr::from(format!( "<{}.{} object at {:#x}>", module, qualname, zelf.get_id() - )), - _ => Some(format!( + )) + .into_ref(vm)), + _ => Ok(PyStr::from(format!( "<{} object at {:#x}>", class.slot_name(), zelf.get_id() - )), + )) + .into_ref(vm)), } } + /// Return repr(self). + #[pymethod(magic)] + fn repr(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { + Self::slot_repr(&zelf, vm) + } + #[pyclassmethod(magic)] fn subclasshook(_args: FuncArgs, vm: &VirtualMachine) -> PyObjectRef { vm.ctx.not_implemented() diff --git a/vm/src/builtins/range.rs b/vm/src/builtins/range.rs index 2d214174f..2616d8782 100644 --- a/vm/src/builtins/range.rs +++ b/vm/src/builtins/range.rs @@ -1,5 +1,6 @@ use super::{ - builtins_iter, tuple::tuple_hash, PyInt, PyIntRef, PySlice, PyTupleRef, PyType, PyTypeRef, + builtins_iter, tuple::tuple_hash, PyInt, PyIntRef, PySlice, PyStr, PyStrRef, PyTupleRef, + PyType, PyTypeRef, }; use crate::{ atomic_func, @@ -9,7 +10,7 @@ use crate::{ protocol::{PyIterReturn, PyMappingMethods, PySequenceMethods}, types::{ AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, IterNextIterable, - Iterable, PyComparisonOp, Unconstructible, + Iterable, PyComparisonOp, Representable, Unconstructible, }, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, @@ -174,7 +175,7 @@ pub fn init(context: &Context) { PyRangeIterator::extend_class(context, context.types.range_iterator_type); } -#[pyclass(with(AsMapping, AsSequence, Hashable, Comparable, Iterable))] +#[pyclass(with(AsMapping, AsSequence, Hashable, Comparable, Iterable, Representable))] impl PyRange { fn new(cls: PyTypeRef, stop: ArgIndex, vm: &VirtualMachine) -> PyResult> { PyRange { @@ -260,15 +261,6 @@ impl PyRange { self.compute_length() } - #[pymethod(magic)] - fn repr(&self) -> String { - if self.step.as_bigint().is_one() { - format!("range({}, {})", self.start, self.stop) - } else { - format!("range({}, {}, {})", self.start, self.stop, self.step) - } - } - #[pymethod(magic)] fn bool(&self) -> bool { !self.is_empty() @@ -511,6 +503,21 @@ impl Iterable for PyRange { } } +impl Representable for PyRange { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + if zelf.step.as_bigint().is_one() { + Ok(PyStr::from(format!("range({}, {})", zelf.start, zelf.stop)).into_ref(vm)) + } else { + Ok(PyStr::from(format!( + "range({}, {}, {})", + zelf.start, zelf.stop, zelf.step + )) + .into_ref(vm)) + } + } +} + // Semantically, this is the same as the previous representation. // // Unfortunately, since AtomicCell requires a Copy type, no BigInt implementations can diff --git a/vm/src/builtins/set.rs b/vm/src/builtins/set.rs index 7a53093c7..f7ff1a8ab 100644 --- a/vm/src/builtins/set.rs +++ b/vm/src/builtins/set.rs @@ -2,8 +2,8 @@ * Builtin set type with a sequence of unique items. */ use super::{ - builtins_iter, IterStatus, PositionIterInternal, PyDict, PyDictRef, PyGenericAlias, PyTupleRef, - PyType, PyTypeRef, + builtins_iter, IterStatus, PositionIterInternal, PyDict, PyDictRef, PyGenericAlias, PyStr, + PyStrRef, PyTupleRef, PyType, PyTypeRef, }; use crate::{ atomic_func, @@ -17,7 +17,7 @@ use crate::{ types::AsNumber, types::{ AsSequence, Comparable, Constructor, Hashable, Initializer, IterNext, IterNextIterable, - Iterable, PyComparisonOp, Unconstructible, + Iterable, PyComparisonOp, Representable, Unconstructible, }, utils::collection_repr, vm::VirtualMachine, @@ -489,7 +489,15 @@ fn reduce_set( } #[pyclass( - with(Constructor, Initializer, AsSequence, Comparable, Iterable, AsNumber), + with( + Constructor, + Initializer, + AsSequence, + Comparable, + Iterable, + AsNumber, + Representable + ), flags(BASETYPE) )] impl PySet { @@ -626,26 +634,6 @@ impl PySet { } } - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let class = zelf.class(); - let borrowed_name = class.name(); - let class_name = borrowed_name.deref(); - let s = if zelf.inner.len() == 0 { - format!("{class_name}()") - } else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { - let name = if class_name != "set" { - Some(class_name) - } else { - None - }; - zelf.inner.repr(name, vm)? - } else { - format!("{class_name}(...)") - }; - Ok(s) - } - #[pymethod] pub fn add(&self, item: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { self.inner.add(item, vm)?; @@ -888,6 +876,28 @@ impl AsNumber for PySet { } } +impl Representable for PySet { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let class = zelf.class(); + let borrowed_name = class.name(); + let class_name = borrowed_name.deref(); + let s = if zelf.inner.len() == 0 { + format!("{class_name}()") + } else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { + let name = if class_name != "set" { + Some(class_name) + } else { + None + }; + zelf.inner.repr(name, vm)? + } else { + format!("{class_name}(...)") + }; + Ok(PyStr::from(s).into_ref(vm)) + } +} + impl Constructor for PyFrozenSet { type Args = OptionalArg; @@ -918,7 +928,15 @@ impl Constructor for PyFrozenSet { #[pyclass( flags(BASETYPE), - with(Constructor, AsSequence, Hashable, Comparable, Iterable, AsNumber) + with( + Constructor, + AsSequence, + Hashable, + Comparable, + Iterable, + AsNumber, + Representable + ) )] impl PyFrozenSet { #[pymethod(magic)] @@ -1059,21 +1077,6 @@ impl PyFrozenSet { } } - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let inner = &zelf.inner; - let class = zelf.class(); - let class_name = class.name(); - let s = if inner.len() == 0 { - format!("{class_name}()") - } else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { - inner.repr(Some(&class_name), vm)? - } else { - format!("{class_name}(...)") - }; - Ok(s) - } - #[pymethod(magic)] fn reduce( zelf: PyRef, @@ -1164,6 +1167,23 @@ impl AsNumber for PyFrozenSet { } } +impl Representable for PyFrozenSet { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let inner = &zelf.inner; + let class = zelf.class(); + let class_name = class.name(); + let s = if inner.len() == 0 { + format!("{class_name}()") + } else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { + inner.repr(Some(&class_name), vm)? + } else { + format!("{class_name}(...)") + }; + Ok(PyStr::from(s).into_ref(vm)) + } +} + struct AnySet { object: PyObjectRef, } diff --git a/vm/src/builtins/singletons.rs b/vm/src/builtins/singletons.rs index 28a142e10..910945682 100644 --- a/vm/src/builtins/singletons.rs +++ b/vm/src/builtins/singletons.rs @@ -1,9 +1,9 @@ -use super::{PyType, PyTypeRef}; +use super::{PyStr, PyStrRef, PyType, PyTypeRef}; use crate::{ class::PyClassImpl, convert::ToPyObject, protocol::PyNumberMethods, - types::{AsNumber, Constructor}, + types::{AsNumber, Constructor, Representable}, Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; @@ -42,19 +42,21 @@ impl Constructor for PyNone { } } -#[pyclass(with(Constructor, AsNumber))] +#[pyclass(with(Constructor, AsNumber, Representable))] impl PyNone { - #[pymethod(magic)] - fn repr(&self) -> String { - "None".to_owned() - } - #[pymethod(magic)] fn bool(&self) -> bool { false } } +impl Representable for PyNone { + #[inline] + fn repr(_zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + Ok(PyStr::from("None").into_ref(vm)) + } +} + impl AsNumber for PyNone { fn as_number() -> &'static PyNumberMethods { static AS_NUMBER: PyNumberMethods = PyNumberMethods { @@ -93,17 +95,19 @@ impl PyNotImplemented { true } - #[pymethod(magic)] - fn repr(&self) -> String { - "NotImplemented".to_owned() - } - #[pymethod(magic)] fn reduce(&self) -> String { "NotImplemented".to_owned() } } +impl Representable for PyNotImplemented { + #[inline] + fn repr(_zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + Ok(PyStr::from("NotImplemented").into_ref(vm)) + } +} + pub fn init(context: &Context) { PyNone::extend_class(context, context.types.none_type); PyNotImplemented::extend_class(context, context.types.not_implemented_type); diff --git a/vm/src/builtins/slice.rs b/vm/src/builtins/slice.rs index 5177a0bb4..e7c4e6706 100644 --- a/vm/src/builtins/slice.rs +++ b/vm/src/builtins/slice.rs @@ -1,12 +1,12 @@ // sliceobject.{h,c} in CPython // spell-checker:ignore sliceobject -use super::{PyTupleRef, PyType, PyTypeRef}; +use super::{PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef}; use crate::{ class::PyClassImpl, convert::ToPyObject, function::{ArgIndex, FuncArgs, OptionalArg, PyComparisonValue}, sliceable::SaturatedSlice, - types::{Comparable, Constructor, PyComparisonOp}, + types::{Comparable, Constructor, PyComparisonOp, Representable}, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; use num_bigint::{BigInt, ToBigInt}; @@ -26,7 +26,7 @@ impl PyPayload for PySlice { } } -#[pyclass(with(Comparable))] +#[pyclass(with(Comparable, Representable))] impl PySlice { #[pygetset] fn start(&self, vm: &VirtualMachine) -> PyObjectRef { @@ -57,20 +57,6 @@ impl PySlice { } } - #[pymethod(magic)] - fn repr(&self, vm: &VirtualMachine) -> PyResult { - let start_repr = self.start_ref(vm).repr(vm)?; - let stop_repr = &self.stop.repr(vm)?; - let step_repr = self.step_ref(vm).repr(vm)?; - - Ok(format!( - "slice({}, {}, {})", - start_repr.as_str(), - stop_repr.as_str(), - step_repr.as_str() - )) - } - pub fn to_saturated(&self, vm: &VirtualMachine) -> PyResult { SaturatedSlice::with_slice(self, vm) } @@ -258,6 +244,22 @@ impl Comparable for PySlice { Ok(PyComparisonValue::Implemented(ret)) } } +impl Representable for PySlice { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let start_repr = zelf.start_ref(vm).repr(vm)?; + let stop_repr = &zelf.stop.repr(vm)?; + let step_repr = zelf.step_ref(vm).repr(vm)?; + + Ok(PyStr::from(format!( + "slice({}, {}, {})", + start_repr.as_str(), + stop_repr.as_str(), + step_repr.as_str() + )) + .into_ref(vm)) + } +} #[pyclass(module = false, name = "EllipsisType")] #[derive(Debug)] @@ -277,19 +279,21 @@ impl Constructor for PyEllipsis { } } -#[pyclass(with(Constructor))] +#[pyclass(with(Constructor, Representable))] impl PyEllipsis { - #[pymethod(magic)] - fn repr(&self) -> String { - "Ellipsis".to_owned() - } - #[pymethod(magic)] fn reduce(&self) -> String { "Ellipsis".to_owned() } } +impl Representable for PyEllipsis { + #[inline] + fn repr(_zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + Ok(PyStr::from("Ellipsis").into_ref(vm)) + } +} + pub fn init(ctx: &Context) { PySlice::extend_class(ctx, ctx.types.slice_type); PyEllipsis::extend_class(ctx, ctx.types.ellipsis_type); diff --git a/vm/src/builtins/staticmethod.rs b/vm/src/builtins/staticmethod.rs index 76144ce63..877f333ac 100644 --- a/vm/src/builtins/staticmethod.rs +++ b/vm/src/builtins/staticmethod.rs @@ -1,10 +1,10 @@ -use super::{PyStr, PyType, PyTypeRef}; +use super::{PyStr, PyStrRef, PyType, PyTypeRef}; use crate::{ builtins::builtin_func::PyBuiltinMethod, class::PyClassImpl, common::lock::PyMutex, function::{FuncArgs, IntoPyNativeFunc}, - types::{Callable, Constructor, GetDescriptor, Initializer}, + types::{Callable, Constructor, GetDescriptor, Initializer, Representable}, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -104,7 +104,7 @@ impl Initializer for PyStaticMethod { } #[pyclass( - with(Callable, GetDescriptor, Constructor, Initializer), + with(Callable, GetDescriptor, Constructor, Initializer, Representable), flags(BASETYPE, HAS_DICT) )] impl PyStaticMethod { @@ -138,26 +138,6 @@ impl PyStaticMethod { self.callable.lock().get_attr("__annotations__", vm) } - #[pymethod(magic)] - fn repr(&self, vm: &VirtualMachine) -> Option { - let callable = self.callable.lock().repr(vm).unwrap(); - let class = Self::class(vm); - - match ( - class - .qualname(vm) - .downcast_ref::() - .map(|n| n.as_str()), - class.module(vm).downcast_ref::().map(|m| m.as_str()), - ) { - (None, _) => None, - (Some(qualname), Some(module)) if module != "builtins" => { - Some(format!("<{module}.{qualname}({callable})>")) - } - _ => Some(format!("<{}({})>", class.slot_name(), callable)), - } - } - #[pygetset(magic)] fn isabstractmethod(&self, vm: &VirtualMachine) -> PyObjectRef { match vm.get_attribute_opt(self.callable.lock().clone(), "__isabstractmethod__") { @@ -184,6 +164,28 @@ impl Callable for PyStaticMethod { } } +impl Representable for PyStaticMethod { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let callable = zelf.callable.lock().repr(vm).unwrap(); + let class = Self::class(vm); + + match ( + class + .qualname(vm) + .downcast_ref::() + .map(|n| n.as_str()), + class.module(vm).downcast_ref::().map(|m| m.as_str()), + ) { + (None, _) => Err(vm.new_type_error("Unknown qualified name".into())), + (Some(qualname), Some(module)) if module != "builtins" => { + Ok(PyStr::from(format!("<{module}.{qualname}({callable})>")).into_ref(vm)) + } + _ => Ok(PyStr::from(format!("<{}({})>", class.slot_name(), callable)).into_ref(vm)), + } + } +} + pub fn init(context: &Context) { PyStaticMethod::extend_class(context, context.types.staticmethod_type); } diff --git a/vm/src/builtins/str.rs b/vm/src/builtins/str.rs index a2f85479e..ce317bf67 100644 --- a/vm/src/builtins/str.rs +++ b/vm/src/builtins/str.rs @@ -20,7 +20,7 @@ use crate::{ sliceable::{SequenceIndex, SliceableSequenceOp}, types::{ AsMapping, AsNumber, AsSequence, Comparable, Constructor, Hashable, IterNext, - IterNextIterable, Iterable, PyComparisonOp, Unconstructible, + IterNextIterable, Iterable, PyComparisonOp, Representable, Unconstructible, }, AsObject, Context, Py, PyExact, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult, TryFromBorrowedObject, VirtualMachine, @@ -377,6 +377,7 @@ impl PyStr { AsMapping, AsNumber, AsSequence, + Representable, Hashable, Comparable, Iterable, @@ -495,11 +496,12 @@ impl PyStr { zelf } - #[pymethod(magic)] - pub(crate) fn repr(&self, vm: &VirtualMachine) -> PyResult { + #[inline] + pub(crate) fn repr(&self, vm: &VirtualMachine) -> PyResult { rustpython_common::str::repr(self.as_str()) .to_string_checked() .map_err(|err| vm.new_overflow_error(err.to_string())) + .map(|s| PyStr::from(s).into_ref(vm)) } #[pymethod] @@ -1259,6 +1261,13 @@ impl PyStrRef { } } +impl Representable for PyStr { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + zelf.repr(vm) + } +} + impl Hashable for PyStr { #[inline] fn hash(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/builtins/super.rs b/vm/src/builtins/super.rs index e5ca71eba..ea8e0772e 100644 --- a/vm/src/builtins/super.rs +++ b/vm/src/builtins/super.rs @@ -3,11 +3,11 @@ See also [CPython source code.](https://github.com/python/cpython/blob/50b48572d9a90c5bb36e2bef6179548ea927a35a/Objects/typeobject.c#L7663) */ -use super::{PyStrRef, PyType, PyTypeRef}; +use super::{PyStr, PyStrRef, PyType, PyTypeRef}; use crate::{ class::PyClassImpl, function::{IntoFuncArgs, OptionalArg}, - types::{Callable, Constructor, GetAttr, GetDescriptor}, + types::{Callable, Constructor, GetAttr, GetDescriptor, Representable}, AsObject, Context, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; @@ -97,7 +97,7 @@ impl Constructor for PySuper { } } -#[pyclass(with(GetAttr, GetDescriptor, Constructor))] +#[pyclass(with(GetAttr, GetDescriptor, Constructor, Representable))] impl PySuper { fn new(typ: PyTypeRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { let obj = if vm.is_none(&obj) { @@ -123,15 +123,6 @@ impl PySuper { fn __self__(&self) -> Option { Some(self.obj.as_ref()?.0.clone()) } - - #[pymethod(magic)] - fn repr(&self) -> String { - let name = &self.typ.name(); - match self.obj { - Some((_, ref ty)) => format!(", <{} object>>", name, ty.name()), - None => format!(", NULL>"), - } - } } impl GetAttr for PySuper { @@ -193,6 +184,22 @@ impl GetDescriptor for PySuper { } } +impl Representable for PySuper { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let typname = &zelf.typ.name(); + match zelf.obj { + Some((_, ref ty)) => Ok(PyStr::from(format!( + ", <{} object>>", + typname, + ty.name() + )) + .into_ref(vm)), + None => Ok(PyStr::from(format!(", NULL>")).into_ref(vm)), + } + } +} + fn supercheck(ty: PyTypeRef, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { if let Ok(cls) = obj.clone().downcast::() { if cls.fast_issubclass(&ty) { diff --git a/vm/src/builtins/tuple.rs b/vm/src/builtins/tuple.rs index 6ae9377e0..7cccba62c 100644 --- a/vm/src/builtins/tuple.rs +++ b/vm/src/builtins/tuple.rs @@ -1,6 +1,6 @@ use once_cell::sync::Lazy; -use super::{PositionIterInternal, PyGenericAlias, PyType, PyTypeRef}; +use super::{PositionIterInternal, PyGenericAlias, PyStr, PyStrRef, PyType, PyTypeRef}; use crate::common::{hash::PyHash, lock::PyMutex}; use crate::{ atomic_func, @@ -14,7 +14,7 @@ use crate::{ sliceable::{SequenceIndex, SliceableSequenceOp}, types::{ AsMapping, AsSequence, Comparable, Constructor, Hashable, IterNext, IterNextIterable, - Iterable, PyComparisonOp, Unconstructible, + Iterable, PyComparisonOp, Representable, Unconstructible, }, utils::collection_repr, vm::VirtualMachine, @@ -189,7 +189,15 @@ impl PyTuple { #[pyclass( flags(BASETYPE), - with(AsMapping, AsSequence, Hashable, Comparable, Iterable, Constructor) + with( + AsMapping, + AsSequence, + Hashable, + Comparable, + Iterable, + Constructor, + Representable + ) )] impl PyTuple { #[pymethod(magic)] @@ -242,22 +250,6 @@ impl PyTuple { self.elements.is_empty() } - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let s = if zelf.len() == 0 { - "()".to_owned() - } else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { - if zelf.len() == 1 { - format!("({},)", zelf.elements[0].repr(vm)?) - } else { - collection_repr(None, "(", ")", zelf.elements.iter(), vm)? - } - } else { - "(...)".to_owned() - }; - Ok(s) - } - #[pymethod(name = "__rmul__")] #[pymethod(magic)] fn mul(zelf: PyRef, value: ArgSize, vm: &VirtualMachine) -> PyResult> { @@ -406,6 +398,24 @@ impl Iterable for PyTuple { } } +impl Representable for PyTuple { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let s = if zelf.len() == 0 { + "()".to_owned() + } else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { + if zelf.len() == 1 { + format!("({},)", zelf.elements[0].repr(vm)?) + } else { + collection_repr(None, "(", ")", zelf.elements.iter(), vm)? + } + } else { + "(...)".to_owned() + }; + Ok(PyStr::from(s).into_ref(vm)) + } +} + #[pyclass(module = false, name = "tuple_iterator")] #[derive(Debug)] pub(crate) struct PyTupleIterator { diff --git a/vm/src/builtins/type.rs b/vm/src/builtins/type.rs index f26dcfef1..e5930065c 100644 --- a/vm/src/builtins/type.rs +++ b/vm/src/builtins/type.rs @@ -21,8 +21,7 @@ use crate::{ function::{FuncArgs, KwArgs, OptionalArg, PySetterValue}, identifier, protocol::{PyIterReturn, PyMappingMethods, PyNumberMethods, PySequenceMethods}, - types::AsNumber, - types::{Callable, GetAttr, PyTypeFlags, PyTypeSlots, SetAttr}, + types::{AsNumber, Callable, GetAttr, PyTypeFlags, PyTypeSlots, Representable, SetAttr}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, }; use indexmap::{map::Entry, IndexMap}; @@ -375,7 +374,10 @@ impl Py { } } -#[pyclass(with(GetAttr, SetAttr, Callable, AsNumber), flags(BASETYPE))] +#[pyclass( + with(GetAttr, SetAttr, Callable, AsNumber, Representable), + flags(BASETYPE) +)] impl PyType { // bound method for every type pub(crate) fn __new__(zelf: PyRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult { @@ -459,27 +461,6 @@ impl PyType { .into() } - #[pymethod(magic)] - fn repr(&self, vm: &VirtualMachine) -> String { - let module = self.module(vm); - let module = module.downcast_ref::().map(|m| m.as_str()); - - match module { - Some(module) if module != "builtins" => { - let name = self.name(); - format!( - "", - module, - self.qualname(vm) - .downcast_ref::() - .map(|n| n.as_str()) - .unwrap_or_else(|| &name) - ) - } - _ => format!("", self.slot_name()), - } - } - #[pygetset(magic)] pub fn qualname(&self, vm: &VirtualMachine) -> PyObjectRef { self.attributes @@ -1072,6 +1053,30 @@ impl AsNumber for PyType { } } +impl Representable for PyType { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let module = zelf.module(vm); + let module = module.downcast_ref::().map(|m| m.as_str()); + + match module { + Some(module) if module != "builtins" => { + let name = zelf.name(); + Ok(PyStr::from(format!( + "", + module, + zelf.qualname(vm) + .downcast_ref::() + .map(|n| n.as_str()) + .unwrap_or_else(|| &name) + )) + .into_ref(vm)) + } + _ => Ok(PyStr::from(format!("", zelf.slot_name())).into_ref(vm)), + } + } +} + fn find_base_dict_descr(cls: &Py, vm: &VirtualMachine) -> Option { cls.iter_base_chain().skip(1).find_map(|cls| { // TODO: should actually be some translation of: diff --git a/vm/src/builtins/union.rs b/vm/src/builtins/union.rs index 09785a712..4901e744e 100644 --- a/vm/src/builtins/union.rs +++ b/vm/src/builtins/union.rs @@ -7,7 +7,7 @@ use crate::{ convert::{ToPyObject, ToPyResult}, function::PyComparisonValue, protocol::{PyMappingMethods, PyNumberMethods}, - types::{AsMapping, AsNumber, Comparable, GetAttr, Hashable, PyComparisonOp}, + types::{AsMapping, AsNumber, Comparable, GetAttr, Hashable, PyComparisonOp, Representable}, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, }; @@ -34,14 +34,16 @@ impl PyPayload for PyUnion { } } -#[pyclass(flags(BASETYPE), with(Hashable, Comparable, AsMapping, AsNumber))] +#[pyclass( + flags(BASETYPE), + with(Hashable, Comparable, AsMapping, AsNumber, Representable) +)] impl PyUnion { pub fn new(args: PyTupleRef, vm: &VirtualMachine) -> Self { let parameters = make_parameters(&args, vm); Self { args, parameters } } - #[pymethod(magic)] fn repr(&self, vm: &VirtualMachine) -> PyResult { fn repr_item(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult { if obj.is(vm.ctx.types.none_type) { @@ -307,6 +309,13 @@ impl GetAttr for PyUnion { } } +impl Representable for PyUnion { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + zelf.repr(vm).map(|s| PyStr::from(s).into_ref(vm)) + } +} + pub fn init(context: &Context) { let union_type = &context.types.union_type; PyUnion::extend_class(context, union_type); diff --git a/vm/src/builtins/weakproxy.rs b/vm/src/builtins/weakproxy.rs index 3db19199b..4c8296ef9 100644 --- a/vm/src/builtins/weakproxy.rs +++ b/vm/src/builtins/weakproxy.rs @@ -6,7 +6,10 @@ use crate::{ class::PyClassImpl, function::{OptionalArg, PyComparisonValue, PySetterValue}, protocol::{PyMappingMethods, PySequenceMethods}, - types::{AsMapping, AsSequence, Comparable, Constructor, GetAttr, PyComparisonOp, SetAttr}, + types::{ + AsMapping, AsSequence, Comparable, Constructor, GetAttr, PyComparisonOp, Representable, + SetAttr, + }, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -61,7 +64,15 @@ crate::common::static_cell! { static WEAK_SUBCLASS: PyTypeRef; } -#[pyclass(with(GetAttr, SetAttr, Constructor, Comparable, AsSequence, AsMapping))] +#[pyclass(with( + GetAttr, + SetAttr, + Constructor, + Comparable, + AsSequence, + AsMapping, + Representable +))] impl PyWeakProxy { fn try_upgrade(&self, vm: &VirtualMachine) -> PyResult { self.weak.upgrade().ok_or_else(|| new_reference_error(vm)) @@ -86,11 +97,6 @@ impl PyWeakProxy { self.try_upgrade(vm)?.bytes(vm) } - #[pymethod(magic)] - fn repr(&self, vm: &VirtualMachine) -> PyResult { - self.try_upgrade(vm)?.repr(vm) - } - #[pymethod(magic)] fn contains(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { self.try_upgrade(vm)?.to_sequence(vm).contains(&needle, vm) @@ -191,6 +197,13 @@ impl AsMapping for PyWeakProxy { } } +impl Representable for PyWeakProxy { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + zelf.try_upgrade(vm)?.repr(vm) + } +} + pub fn init(context: &Context) { PyWeakProxy::extend_class(context, context.types.weakproxy_type); } diff --git a/vm/src/builtins/weakref.rs b/vm/src/builtins/weakref.rs index 8258280b8..170299376 100644 --- a/vm/src/builtins/weakref.rs +++ b/vm/src/builtins/weakref.rs @@ -1,4 +1,4 @@ -use super::{PyGenericAlias, PyType, PyTypeRef}; +use super::{PyGenericAlias, PyStr, PyStrRef, PyType, PyTypeRef}; use crate::common::{ atomic::{Ordering, Radium}, hash::{self, PyHash}, @@ -6,8 +6,8 @@ use crate::common::{ use crate::{ class::PyClassImpl, function::OptionalArg, - types::{Callable, Comparable, Constructor, Hashable, PyComparisonOp}, - AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, + types::{Callable, Comparable, Constructor, Hashable, PyComparisonOp, Representable}, + AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; pub use crate::object::PyWeak; @@ -47,23 +47,11 @@ impl Constructor for PyWeak { } } -#[pyclass(with(Callable, Hashable, Comparable, Constructor), flags(BASETYPE))] +#[pyclass( + with(Callable, Hashable, Comparable, Constructor, Representable), + flags(BASETYPE) +)] impl PyWeak { - #[pymethod(magic)] - fn repr(zelf: PyRef) -> String { - let id = zelf.get_id(); - if let Some(o) = zelf.upgrade() { - format!( - "", - id, - o.class().name(), - o.get_id(), - ) - } else { - format!("") - } - } - #[pyclassmethod(magic)] fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { PyGenericAlias::new(cls, args, vm) @@ -114,6 +102,24 @@ impl Comparable for PyWeak { } } +impl Representable for PyWeak { + #[inline] + fn repr(zelf: &crate::Py, vm: &VirtualMachine) -> PyResult { + let id = zelf.get_id(); + if let Some(o) = zelf.upgrade() { + Ok(PyStr::from(format!( + "", + id, + o.class().name(), + o.get_id(), + )) + .into_ref(vm)) + } else { + Ok(PyStr::from(format!("")).into_ref(vm)) + } + } +} + pub fn init(context: &Context) { PyWeak::extend_class(context, context.types.weakref_type); } diff --git a/vm/src/protocol/object.rs b/vm/src/protocol/object.rs index 8aa90734c..c41270f43 100644 --- a/vm/src/protocol/object.rs +++ b/vm/src/protocol/object.rs @@ -317,8 +317,12 @@ impl PyObject { pub fn repr(&self, vm: &VirtualMachine) -> PyResult { vm.with_recursion("while getting the repr of an object", || { - let repr = vm.call_special_method(self.to_owned(), identifier!(vm, __repr__), ())?; - repr.try_into_value(vm) + match self.class().slots.repr.load() { + Some(slot) => slot(self, vm), + None => vm + .call_special_method(self.to_owned(), identifier!(vm, __repr__), ())? + .try_into_value(vm), + } }) } diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index 32975c91a..5f80554c2 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -1,5 +1,7 @@ use crate::{ - builtins::{type_::PointerSlot, PyFloat, PyInt, PyStrInterned, PyStrRef, PyType, PyTypeRef}, + builtins::{ + type_::PointerSlot, PyFloat, PyInt, PyStr, PyStrInterned, PyStrRef, PyType, PyTypeRef, + }, bytecode::ComparisonOperator, common::{hash::PyHash, lock::PyRwLock}, convert::{ToPyObject, ToPyResult}, @@ -43,7 +45,7 @@ pub struct PyTypeSlots { // More standard operations (here for binary compatibility) pub hash: AtomicCell>, pub call: AtomicCell>, - // tp_str + pub repr: AtomicCell>, pub getattro: AtomicCell>, pub setattro: AtomicCell>, @@ -273,6 +275,7 @@ impl Default for PyTypeFlags { pub(crate) type GenericMethod = fn(&PyObject, FuncArgs, &VirtualMachine) -> PyResult; pub(crate) type HashFunc = fn(&PyObject, &VirtualMachine) -> PyResult; // CallFunc = GenericMethod +pub(crate) type ReprFunc = fn(&PyObject, &VirtualMachine) -> PyResult; pub(crate) type GetattroFunc = fn(&PyObject, PyStrRef, &VirtualMachine) -> PyResult; pub(crate) type SetattroFunc = fn(&PyObject, PyStrRef, PySetterValue, &VirtualMachine) -> PyResult<()>; @@ -369,6 +372,16 @@ fn setitem_wrapper( .map(drop) } +fn repr_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult { + let ret = vm.call_special_method(zelf.to_owned(), identifier!(vm, __repr__), ())?; + ret.downcast::().map_err(|obj| { + vm.new_type_error(format!( + "__repr__ returned non-string (type {})", + obj.class() + )) + }) +} + fn hash_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult { let hash_obj = vm.call_special_method(zelf.to_owned(), identifier!(vm, __hash__), ())?; match hash_obj.payload_if_subclass::(vm) { @@ -561,6 +574,9 @@ impl PyType { }); update_pointer_slot!(as_mapping, mapping_methods); } + _ if name == identifier!(ctx, __repr__) => { + update_slot!(repr, repr_wrapper); + } _ if name == identifier!(ctx, __hash__) => { let is_unhashable = self .attributes @@ -993,6 +1009,27 @@ pub trait Hashable: PyPayload { fn hash(zelf: &Py, vm: &VirtualMachine) -> PyResult; } +#[pyclass] +pub trait Representable: PyPayload { + #[inline] + #[pyslot] + fn slot_repr(zelf: &PyObject, vm: &VirtualMachine) -> PyResult { + if let Some(zelf) = zelf.downcast_ref() { + Self::repr(zelf, vm) + } else { + Err(vm.new_type_error("unexpected payload for __repr__".to_owned())) + } + } + + #[inline] + #[pymethod] + fn __repr__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { + Self::slot_repr(&zelf, vm) + } + + fn repr(zelf: &Py, vm: &VirtualMachine) -> PyResult; +} + #[pyclass] pub trait Comparable: PyPayload { #[inline]