From 96a949189d75ea0d4297db33927e50cc1243d2c8 Mon Sep 17 00:00:00 2001 From: Charles Hubain Date: Tue, 4 Apr 2023 23:42:57 +0900 Subject: [PATCH] Implemented tp_repr slots for more types --- stdlib/src/array.rs | 48 +++++++------ stdlib/src/contextvars.rs | 44 ++++++------ stdlib/src/socket.rs | 32 +++++---- vm/src/builtins/code.rs | 26 ++++--- vm/src/builtins/function.rs | 41 ++++++----- vm/src/builtins/slice.rs | 1 + vm/src/exceptions.rs | 23 ++++--- vm/src/stdlib/collections.rs | 55 ++++++++------- vm/src/stdlib/io.rs | 116 +++++++++++++++++++++---------- vm/src/stdlib/itertools.rs | 50 +++++++------- vm/src/stdlib/operator.rs | 128 ++++++++++++++++++----------------- vm/src/stdlib/os.rs | 34 +++++----- vm/src/stdlib/posix.rs | 26 +++---- vm/src/stdlib/sre.rs | 128 ++++++++++++++++++----------------- vm/src/stdlib/thread.rs | 28 ++++---- wasm/lib/src/js_module.rs | 12 ++-- 16 files changed, 451 insertions(+), 341 deletions(-) diff --git a/stdlib/src/array.rs b/stdlib/src/array.rs index bed79c200..810a5f9ce 100644 --- a/stdlib/src/array.rs +++ b/stdlib/src/array.rs @@ -62,7 +62,7 @@ mod array { }, types::{ AsBuffer, AsMapping, AsSequence, Comparable, Constructor, IterNext, - IterNextIterable, Iterable, PyComparisonOp, + IterNextIterable, Iterable, PyComparisonOp, Representable, }, AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }, @@ -721,7 +721,15 @@ mod array { #[pyclass( flags(BASETYPE), - with(Comparable, AsBuffer, AsMapping, AsSequence, Iterable, Constructor) + with( + Comparable, + AsBuffer, + AsMapping, + AsSequence, + Iterable, + Constructor, + Representable + ) )] impl PyArray { fn read(&self) -> PyRwLockReadGuard<'_, ArrayContentType> { @@ -1113,23 +1121,6 @@ mod array { Ok(zelf) } - #[pymethod(magic)] - fn repr(zelf: &Py, vm: &VirtualMachine) -> PyResult { - let class = zelf.class(); - let class_name = class.name(); - if zelf.read().typecode() == 'u' { - if zelf.len() == 0 { - return Ok(format!("{class_name}('u')")); - } - return Ok(format!( - "{}('u', {})", - class_name, - crate::common::str::repr(&zelf.tounicode(vm)?) - )); - } - zelf.read().repr(&class_name, vm) - } - #[pymethod(magic)] pub(crate) fn len(&self) -> usize { self.read().len() @@ -1292,6 +1283,25 @@ mod array { } } + impl Representable for PyArray { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + let class = zelf.class(); + let class_name = class.name(); + if zelf.read().typecode() == 'u' { + if zelf.len() == 0 { + return Ok(format!("{class_name}('u')")); + } + return Ok(format!( + "{}('u', {})", + class_name, + crate::common::str::repr(&zelf.tounicode(vm)?) + )); + } + zelf.read().repr(&class_name, vm) + } + } + static BUFFER_METHODS: BufferMethods = BufferMethods { obj_bytes: |buffer| buffer.obj_as::().get_bytes().into(), obj_bytes_mut: |buffer| buffer.obj_as::().get_bytes_mut().into(), diff --git a/stdlib/src/contextvars.rs b/stdlib/src/contextvars.rs index d21c9bfb1..b182e0fb6 100644 --- a/stdlib/src/contextvars.rs +++ b/stdlib/src/contextvars.rs @@ -5,8 +5,8 @@ mod _contextvars { use crate::vm::{ builtins::{PyFunction, PyStrRef, PyTypeRef}, function::{ArgCallable, FuncArgs, OptionalArg}, - types::Initializer, - PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, + types::{Initializer, Representable}, + Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; #[pyattr] @@ -99,7 +99,7 @@ mod _contextvars { default: OptionalArg, } - #[pyclass(with(Initializer))] + #[pyclass(with(Initializer, Representable))] impl ContextVar { #[pygetset] fn name(&self) -> String { @@ -133,17 +133,6 @@ mod _contextvars { fn class_getitem(_cls: PyTypeRef, _key: PyStrRef, _vm: &VirtualMachine) -> PyResult<()> { unimplemented!("ContextVar.__class_getitem__() is currently under construction") } - - #[pymethod(magic)] - fn repr(_zelf: PyRef, _vm: &VirtualMachine) -> String { - unimplemented!("", - // zelf.name.as_str(), - // zelf.default.map_or("", |x| PyStr::from(*x).as_str()), - // zelf.get_id() - // ) - } } impl Initializer for ContextVar { @@ -154,6 +143,19 @@ mod _contextvars { } } + impl Representable for ContextVar { + #[inline] + fn repr_str(_zelf: &Py, _vm: &VirtualMachine) -> PyResult { + unimplemented!("", + // zelf.name.as_str(), + // zelf.default.map_or("", |x| PyStr::from(*x).as_str()), + // zelf.get_id() + // ) + } + } + #[pyattr] #[pyclass(name = "Token")] #[derive(Debug, PyPayload)] @@ -172,7 +174,7 @@ mod _contextvars { old_value: PyObjectRef, } - #[pyclass(with(Initializer))] + #[pyclass(with(Initializer, Representable))] impl ContextToken { #[pygetset] fn var(&self, _vm: &VirtualMachine) -> PyObjectRef { @@ -183,11 +185,6 @@ mod _contextvars { fn old_value(&self, _vm: &VirtualMachine) -> PyObjectRef { unimplemented!("Token.old_value() is currently under construction") } - - #[pymethod(magic)] - fn repr(_zelf: PyRef, _vm: &VirtualMachine) -> String { - unimplemented!("") - } } impl Initializer for ContextToken { @@ -198,6 +195,13 @@ mod _contextvars { } } + impl Representable for ContextToken { + #[inline] + fn repr_str(_zelf: &Py, _vm: &VirtualMachine) -> PyResult { + unimplemented!("") + } + } + #[pyfunction] fn copy_context() {} } diff --git a/stdlib/src/socket.rs b/stdlib/src/socket.rs index 08ca8496f..25d610a51 100644 --- a/stdlib/src/socket.rs +++ b/stdlib/src/socket.rs @@ -15,9 +15,9 @@ mod _socket { builtins::{PyBaseExceptionRef, PyListRef, PyStrRef, PyTupleRef, PyTypeRef}, convert::{IntoPyException, ToPyObject, TryFromBorrowedObject, TryFromObject}, function::{ArgBytesLike, ArgMemoryBuffer, Either, FsPath, OptionalArg, OptionalOption}, - types::{DefaultConstructor, Initializer}, + types::{DefaultConstructor, Initializer, Representable}, utils::ToCString, - AsObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, + AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; use crossbeam_utils::atomic::AtomicCell; use num_traits::ToPrimitive; @@ -1015,7 +1015,21 @@ mod _socket { } } - #[pyclass(with(DefaultConstructor, Initializer), flags(BASETYPE))] + impl Representable for PySocket { + #[inline] + fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { + Ok(format!( + "", + // cast because INVALID_SOCKET is unsigned, so would show usize::MAX instead of -1 + zelf.fileno() as i64, + zelf.family.load(), + zelf.kind.load(), + zelf.proto.load(), + )) + } + } + + #[pyclass(with(DefaultConstructor, Initializer, Representable), flags(BASETYPE))] impl PySocket { fn _init( zelf: PyRef, @@ -1463,18 +1477,6 @@ mod _socket { fn proto(&self) -> i32 { self.proto.load() } - - #[pymethod(magic)] - fn repr(&self) -> String { - format!( - "", - // cast because INVALID_SOCKET is unsigned, so would show usize::MAX instead of -1 - self.fileno() as i64, - self.family.load(), - self.kind.load(), - self.proto.load(), - ) - } } struct Address { diff --git a/vm/src/builtins/code.rs b/vm/src/builtins/code.rs index 52ed1c107..797825dfe 100644 --- a/vm/src/builtins/code.rs +++ b/vm/src/builtins/code.rs @@ -230,7 +230,7 @@ impl Representable for PyCode { } } -#[pyclass(with(Py))] +#[pyclass(with(Py, Representable))] impl PyCode { #[pyslot] fn slot_new(_cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult { @@ -418,18 +418,26 @@ impl PyCode { } } -#[pyclass] -impl Py { - #[pymethod(magic)] - fn repr(&self) -> String { - let code = &self.code; - format!( +#[pyclass(with(Representable))] +impl Py {} + +impl PyPayload for Py { + fn class(ctx: &Context) -> &'static Py { + ctx.types.code_type + } +} + +impl Representable for Py { + #[inline] + fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { + let code = &zelf.code; + Ok(format!( "", code.obj_name, - self.get_id(), + zelf.get_id(), code.source_path.as_str(), code.first_line_number - ) + )) } } diff --git a/vm/src/builtins/function.rs b/vm/src/builtins/function.rs index bf8fb46d2..ba60be936 100644 --- a/vm/src/builtins/function.rs +++ b/vm/src/builtins/function.rs @@ -549,25 +549,11 @@ impl PyBoundMethod { } } -#[pyclass(with(Callable, Comparable, GetAttr, Constructor), flags(HAS_DICT))] +#[pyclass( + with(Callable, Comparable, GetAttr, Constructor, Representable), + flags(HAS_DICT) +)] impl PyBoundMethod { - #[pymethod(magic)] - fn repr(&self, vm: &VirtualMachine) -> PyResult { - #[allow(clippy::needless_match)] // False positive on nightly - let funcname = - if let Some(qname) = vm.get_attribute_opt(self.function.clone(), "__qualname__")? { - Some(qname) - } else { - vm.get_attribute_opt(self.function.clone(), "__name__")? - }; - let funcname: Option = funcname.and_then(|o| o.downcast().ok()); - Ok(format!( - "", - funcname.as_ref().map_or("?", |s| s.as_str()), - &self.object.repr(vm)?.as_str(), - )) - } - #[pymethod(magic)] fn reduce( &self, @@ -628,6 +614,25 @@ impl PyPayload for PyBoundMethod { } } +impl Representable for PyBoundMethod { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + #[allow(clippy::needless_match)] // False positive on nightly + let funcname = + if let Some(qname) = vm.get_attribute_opt(zelf.function.clone(), "__qualname__")? { + Some(qname) + } else { + vm.get_attribute_opt(zelf.function.clone(), "__name__")? + }; + let funcname: Option = funcname.and_then(|o| o.downcast().ok()); + Ok(format!( + "", + funcname.as_ref().map_or("?", |s| s.as_str()), + &zelf.object.repr(vm)?.as_str(), + )) + } +} + #[pyclass(module = false, name = "cell")] #[derive(Debug, Default)] pub(crate) struct PyCell { diff --git a/vm/src/builtins/slice.rs b/vm/src/builtins/slice.rs index b4e0df750..e87c3adbb 100644 --- a/vm/src/builtins/slice.rs +++ b/vm/src/builtins/slice.rs @@ -244,6 +244,7 @@ impl Comparable for PySlice { Ok(PyComparisonValue::Implemented(ret)) } } + impl Representable for PySlice { #[inline] fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index d095a9569..9df03290d 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -11,7 +11,7 @@ use crate::{ py_io::{self, Write}, stdlib::sys, suggestion::offer_suggestions, - types::{Callable, Constructor, Initializer}, + types::{Callable, Constructor, Initializer, Representable}, AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, }; use crossbeam_utils::atomic::AtomicCell; @@ -430,7 +430,10 @@ impl PyBaseException { } } -#[pyclass(with(Constructor, Initializer), flags(BASETYPE, HAS_DICT))] +#[pyclass( + with(Constructor, Initializer, Representable), + flags(BASETYPE, HAS_DICT) +)] impl PyBaseException { #[pygetset] pub fn args(&self) -> PyTupleRef { @@ -502,13 +505,6 @@ impl PyBaseException { } } - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> String { - let repr_args = vm.exception_args_as_string(zelf.args(), false); - let cls = zelf.class(); - format!("{}({})", cls.name(), repr_args.iter().format(", ")) - } - #[pymethod(magic)] fn reduce(zelf: PyRef, vm: &VirtualMachine) -> PyTupleRef { if let Some(dict) = zelf.as_object().dict().filter(|x| !x.is_empty()) { @@ -538,6 +534,15 @@ impl Initializer for PyBaseException { } } +impl Representable for PyBaseException { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + let repr_args = vm.exception_args_as_string(zelf.args(), false); + let cls = zelf.class(); + Ok(format!("{}({})", cls.name(), repr_args.iter().format(", "))) + } +} + impl ExceptionZoo { pub(crate) fn init() -> Self { use self::types::*; diff --git a/vm/src/stdlib/collections.rs b/vm/src/stdlib/collections.rs index 5c26f1590..5935b124f 100644 --- a/vm/src/stdlib/collections.rs +++ b/vm/src/stdlib/collections.rs @@ -17,7 +17,7 @@ mod _collections { sliceable::SequenceIndexOp, types::{ AsSequence, Comparable, Constructor, Initializer, IterNext, IterNextIterable, Iterable, - PyComparisonOp, + PyComparisonOp, Representable, }, utils::collection_repr, AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, @@ -57,7 +57,14 @@ mod _collections { #[pyclass( flags(BASETYPE), - with(Constructor, Initializer, AsSequence, Comparable, Iterable) + with( + Constructor, + Initializer, + AsSequence, + Comparable, + Iterable, + Representable + ) )] impl PyDeque { #[pymethod] @@ -295,27 +302,6 @@ mod _collections { .ok_or_else(|| vm.new_index_error("deque index out of range".to_owned())) } - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let deque = zelf.borrow_deque().clone(); - let class = zelf.class(); - let class_name = class.name(); - let closing_part = zelf - .maxlen - .map(|maxlen| format!("], maxlen={maxlen}")) - .unwrap_or_else(|| "]".to_owned()); - - let s = if zelf.len() == 0 { - format!("{class_name}([{closing_part})") - } else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { - collection_repr(Some(&class_name), "[", &closing_part, deque.iter(), vm)? - } else { - "[...]".to_owned() - }; - - Ok(s) - } - #[pymethod(magic)] fn contains(&self, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult { self._contains(&needle, vm) @@ -580,6 +566,29 @@ mod _collections { } } + impl Representable for PyDeque { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + let deque = zelf.borrow_deque().clone(); + let class = zelf.class(); + let class_name = class.name(); + let closing_part = zelf + .maxlen + .map(|maxlen| format!("], maxlen={maxlen}")) + .unwrap_or_else(|| "]".to_owned()); + + let s = if zelf.len() == 0 { + format!("{class_name}([{closing_part})") + } else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { + collection_repr(Some(&class_name), "[", &closing_part, deque.iter(), vm)? + } else { + "[...]".to_owned() + }; + + Ok(s) + } + } + #[pyattr] #[pyclass(name = "_deque_iterator")] #[derive(Debug, PyPayload)] diff --git a/vm/src/stdlib/io.rs b/vm/src/stdlib/io.rs index ba7d06a72..07e39f194 100644 --- a/vm/src/stdlib/io.rs +++ b/vm/src/stdlib/io.rs @@ -12,6 +12,7 @@ cfg_if::cfg_if! { use crate::{ builtins::PyBaseExceptionRef, convert::{IntoPyException, ToPyException, ToPyObject}, + types::Representable, PyObjectRef, PyResult, TryFromObject, VirtualMachine, }; pub use _io::io_open as open; @@ -1525,19 +1526,6 @@ mod _io { vm.call_method(self.lock(vm)?.check_init(vm)?, "isatty", ()) } - #[pymethod(magic)] - fn repr(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let name_repr = repr_fileobj_name(&zelf, vm)?; - let cls = zelf.class(); - let slot_name = cls.slot_name(); - let repr = if let Some(name_repr) = name_repr { - format!("<{slot_name} name={name_repr}>") - } else { - format!("<{slot_name}>") - }; - Ok(repr) - } - fn close_strict(&self, vm: &VirtualMachine) -> PyResult { let mut data = self.lock(vm)?; let raw = data.check_init(vm)?; @@ -1684,13 +1672,28 @@ mod _io { } #[pyclass( - with(DefaultConstructor, BufferedMixin, BufferedReadable), + with(DefaultConstructor, BufferedMixin, BufferedReadable, Representable), flags(BASETYPE, HAS_DICT) )] impl BufferedReader {} impl DefaultConstructor for BufferedReader {} + impl Representable for BufferedReader { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + let name_repr = repr_fileobj_name(zelf.as_object(), vm)?; + let cls = zelf.class(); + let slot_name = cls.slot_name(); + let repr = if let Some(name_repr) = name_repr { + format!("<{slot_name} name={name_repr}>") + } else { + format!("<{slot_name}>") + }; + Ok(repr) + } + } + #[pyclass] trait BufferedWritable: PyPayload { type Writer: BufferedMixin; @@ -1734,13 +1737,28 @@ mod _io { } #[pyclass( - with(DefaultConstructor, BufferedMixin, BufferedWritable), + with(DefaultConstructor, BufferedMixin, BufferedWritable, Representable), flags(BASETYPE, HAS_DICT) )] impl BufferedWriter {} impl DefaultConstructor for BufferedWriter {} + impl Representable for BufferedWriter { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + let name_repr = repr_fileobj_name(zelf.as_object(), vm)?; + let cls = zelf.class(); + let slot_name = cls.slot_name(); + let repr = if let Some(name_repr) = name_repr { + format!("<{slot_name} name={name_repr}>") + } else { + format!("<{slot_name}>") + }; + Ok(repr) + } + } + #[pyattr] #[pyclass(name = "BufferedRandom", base = "_BufferedIOBase")] #[derive(Debug, Default, PyPayload)] @@ -1770,13 +1788,34 @@ mod _io { } #[pyclass( - with(DefaultConstructor, BufferedMixin, BufferedReadable, BufferedWritable), + with( + DefaultConstructor, + BufferedMixin, + BufferedReadable, + BufferedWritable, + Representable + ), flags(BASETYPE, HAS_DICT) )] impl BufferedRandom {} impl DefaultConstructor for BufferedRandom {} + impl Representable for BufferedRandom { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + let name_repr = repr_fileobj_name(zelf.as_object(), vm)?; + let cls = zelf.class(); + let slot_name = cls.slot_name(); + let repr = if let Some(name_repr) = name_repr { + format!("<{slot_name} name={name_repr}>") + } else { + format!("<{slot_name}>") + }; + Ok(repr) + } + } + #[pyattr] #[pyclass(name = "BufferedRWPair", base = "_BufferedIOBase")] #[derive(Debug, Default, PyPayload)] @@ -3709,8 +3748,8 @@ mod fileio { convert::ToPyException, function::{ArgBytesLike, ArgMemoryBuffer, OptionalArg, OptionalOption}, stdlib::os, - types::{DefaultConstructor, Initializer}, - AsObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, + types::{DefaultConstructor, Initializer, Representable}, + AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, }; use crossbeam_utils::atomic::AtomicCell; use std::io::{Read, Write}; @@ -3896,7 +3935,29 @@ mod fileio { } } - #[pyclass(with(DefaultConstructor, Initializer), flags(BASETYPE, HAS_DICT))] + impl Representable for FileIO { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + let fd = zelf.fd.load(); + if fd < 0 { + return Ok("<_io.FileIO [closed]>".to_owned()); + } + let name_repr = repr_fileobj_name(zelf.as_object(), vm)?; + let mode = zelf.mode(); + let closefd = if zelf.closefd.load() { "True" } else { "False" }; + let repr = if let Some(name_repr) = name_repr { + format!("<_io.FileIO name={name_repr} mode='{mode}' closefd={closefd}>") + } else { + format!("<_io.FileIO fd={fd} mode='{mode}' closefd={closefd}>") + }; + Ok(repr) + } + } + + #[pyclass( + with(DefaultConstructor, Initializer, Representable), + flags(BASETYPE, HAS_DICT) + )] impl FileIO { #[pygetset] fn closed(&self) -> bool { @@ -3956,23 +4017,6 @@ mod fileio { } } - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let fd = zelf.fd.load(); - if fd < 0 { - return Ok("<_io.FileIO [closed]>".to_owned()); - } - let name_repr = repr_fileobj_name(zelf.as_object(), vm)?; - let mode = zelf.mode(); - let closefd = if zelf.closefd.load() { "True" } else { "False" }; - let repr = if let Some(name_repr) = name_repr { - format!("<_io.FileIO name={name_repr} mode='{mode}' closefd={closefd}>") - } else { - format!("<_io.FileIO fd={fd} mode='{mode}' closefd={closefd}>") - }; - Ok(repr) - } - #[pymethod] fn read(&self, read_byte: OptionalSize, vm: &VirtualMachine) -> PyResult> { if !self.mode.load().contains(Mode::READABLE) { diff --git a/vm/src/stdlib/itertools.rs b/vm/src/stdlib/itertools.rs index 09bcd2696..66b4eb50d 100644 --- a/vm/src/stdlib/itertools.rs +++ b/vm/src/stdlib/itertools.rs @@ -16,7 +16,7 @@ mod decl { identifier, protocol::{PyIter, PyIterReturn, PyNumber}, stdlib::sys, - types::{Constructor, IterNext, IterNextIterable}, + types::{Constructor, IterNext, IterNextIterable, Representable}, AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, PyWeakRef, TryFromObject, VirtualMachine, }; @@ -258,7 +258,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor))] + #[pyclass(with(IterNext, Constructor, Representable))] impl PyItertoolsCount { // TODO: Implement this // if (lz->cnt == PY_SSIZE_T_MAX) @@ -267,16 +267,6 @@ mod decl { fn reduce(zelf: PyRef) -> (PyTypeRef, (PyObjectRef,)) { (zelf.class().to_owned(), (zelf.cur.read().clone(),)) } - - #[pymethod(magic)] - fn repr(&self, vm: &VirtualMachine) -> PyResult { - let cur = format!("{}", self.cur.read().clone().repr(vm)?); - let step = &self.step; - if vm.bool_eq(step, vm.ctx.new_int(1).as_object())? { - return Ok(format!("count({cur})")); - } - Ok(format!("count({}, {})", cur, step.repr(vm)?)) - } } impl IterNextIterable for PyItertoolsCount {} impl IterNext for PyItertoolsCount { @@ -289,6 +279,18 @@ mod decl { } } + impl Representable for PyItertoolsCount { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + let cur = format!("{}", zelf.cur.read().clone().repr(vm)?); + let step = &zelf.step; + if vm.bool_eq(step, vm.ctx.new_int(1).as_object())? { + return Ok(format!("count({cur})")); + } + Ok(format!("count({}, {})", cur, step.repr(vm)?)) + } + } + #[pyattr] #[pyclass(name = "cycle")] #[derive(Debug, PyPayload)] @@ -376,7 +378,7 @@ mod decl { } } - #[pyclass(with(IterNext, Constructor), flags(BASETYPE))] + #[pyclass(with(IterNext, Constructor, Representable), flags(BASETYPE))] impl PyItertoolsRepeat { #[pymethod(magic)] fn length_hint(&self, vm: &VirtualMachine) -> PyResult { @@ -396,16 +398,6 @@ mod decl { None => vm.new_tuple((cls, (zelf.object.clone(),))), }) } - - #[pymethod(magic)] - fn repr(&self, vm: &VirtualMachine) -> PyResult { - let mut fmt = format!("{}", &self.object.repr(vm)?); - if let Some(ref times) = self.times { - fmt.push_str(", "); - fmt.push_str(×.read().to_string()); - } - Ok(format!("repeat({fmt})")) - } } impl IterNextIterable for PyItertoolsRepeat {} @@ -422,6 +414,18 @@ mod decl { } } + impl Representable for PyItertoolsRepeat { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + let mut fmt = format!("{}", &zelf.object.repr(vm)?); + if let Some(ref times) = zelf.times { + fmt.push_str(", "); + fmt.push_str(×.read().to_string()); + } + Ok(format!("repeat({fmt})")) + } + } + #[pyattr] #[pyclass(name = "starmap")] #[derive(Debug, PyPayload)] diff --git a/vm/src/stdlib/operator.rs b/vm/src/stdlib/operator.rs index 32131d795..a48dc7064 100644 --- a/vm/src/stdlib/operator.rs +++ b/vm/src/stdlib/operator.rs @@ -10,7 +10,7 @@ mod _operator { identifier, protocol::PyIter, recursion::ReprGuard, - types::{Callable, Constructor, PyComparisonOp}, + types::{Callable, Constructor, PyComparisonOp, Representable}, AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, }; @@ -356,22 +356,8 @@ mod _operator { attrs: Vec, } - #[pyclass(with(Callable, Constructor))] + #[pyclass(with(Callable, Constructor, Representable))] impl PyAttrGetter { - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let fmt = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { - let mut parts = Vec::with_capacity(zelf.attrs.len()); - for part in &zelf.attrs { - parts.push(part.as_object().repr(vm)?.as_str().to_owned()); - } - parts.join(", ") - } else { - "...".to_owned() - }; - Ok(format!("operator.attrgetter({fmt})")) - } - #[pymethod(magic)] fn reduce(zelf: PyRef, vm: &VirtualMachine) -> PyResult<(PyTypeRef, PyTupleRef)> { let attrs = vm @@ -441,6 +427,22 @@ mod _operator { } } + impl Representable for PyAttrGetter { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + let fmt = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { + let mut parts = Vec::with_capacity(zelf.attrs.len()); + for part in &zelf.attrs { + parts.push(part.as_object().repr(vm)?.as_str().to_owned()); + } + parts.join(", ") + } else { + "...".to_owned() + }; + Ok(format!("operator.attrgetter({fmt})")) + } + } + /// itemgetter(item, ...) --> itemgetter object /// /// Return a callable object that fetches the given item(s) from its operand. @@ -453,22 +455,8 @@ mod _operator { items: Vec, } - #[pyclass(with(Callable, Constructor))] + #[pyclass(with(Callable, Constructor, Representable))] impl PyItemGetter { - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let fmt = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { - let mut items = Vec::with_capacity(zelf.items.len()); - for item in &zelf.items { - items.push(item.repr(vm)?.as_str().to_owned()); - } - items.join(", ") - } else { - "...".to_owned() - }; - Ok(format!("operator.itemgetter({fmt})")) - } - #[pymethod(magic)] fn reduce(zelf: PyRef, vm: &VirtualMachine) -> PyObjectRef { let items = vm.ctx.new_tuple(zelf.items.to_vec()); @@ -508,6 +496,22 @@ mod _operator { } } + impl Representable for PyItemGetter { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + let fmt = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { + let mut items = Vec::with_capacity(zelf.items.len()); + for item in &zelf.items { + items.push(item.repr(vm)?.as_str().to_owned()); + } + items.join(", ") + } else { + "...".to_owned() + }; + Ok(format!("operator.itemgetter({fmt})")) + } + } + /// methodcaller(name, ...) --> methodcaller object /// /// Return a callable object that calls the given method on its operand. @@ -522,37 +526,8 @@ mod _operator { args: FuncArgs, } - #[pyclass(with(Callable, Constructor))] + #[pyclass(with(Callable, Constructor, Representable))] impl PyMethodCaller { - #[pymethod(magic)] - fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { - let fmt = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { - let args = &zelf.args.args; - let kwargs = &zelf.args.kwargs; - let mut fmt = vec![zelf.name.as_object().repr(vm)?.as_str().to_owned()]; - if !args.is_empty() { - let mut parts = Vec::with_capacity(args.len()); - for v in args { - parts.push(v.repr(vm)?.as_str().to_owned()); - } - fmt.push(parts.join(", ")); - } - // build name=value pairs from KwArgs. - if !kwargs.is_empty() { - let mut parts = Vec::with_capacity(kwargs.len()); - for (key, value) in kwargs { - let value_repr = value.repr(vm)?; - parts.push(format!("{key}={value_repr}")); - } - fmt.push(parts.join(", ")); - } - fmt.join(", ") - } else { - "...".to_owned() - }; - Ok(format!("operator.methodcaller({fmt})")) - } - #[pymethod(magic)] fn reduce(zelf: PyRef, vm: &VirtualMachine) -> PyResult { // With no kwargs, return (type(obj), (name, *args)) tuple. @@ -596,4 +571,35 @@ mod _operator { vm.call_method(&obj, zelf.name.as_str(), zelf.args.clone()) } } + + impl Representable for PyMethodCaller { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + let fmt = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { + let args = &zelf.args.args; + let kwargs = &zelf.args.kwargs; + let mut fmt = vec![zelf.name.as_object().repr(vm)?.as_str().to_owned()]; + if !args.is_empty() { + let mut parts = Vec::with_capacity(args.len()); + for v in args { + parts.push(v.repr(vm)?.as_str().to_owned()); + } + fmt.push(parts.join(", ")); + } + // build name=value pairs from KwArgs. + if !kwargs.is_empty() { + let mut parts = Vec::with_capacity(kwargs.len()); + for (key, value) in kwargs { + let value_repr = value.repr(vm)?; + parts.push(format!("{key}={value_repr}")); + } + fmt.push(parts.join(", ")); + } + fmt.join(", ") + } else { + "...".to_owned() + }; + Ok(format!("operator.methodcaller({fmt})")) + } + } } diff --git a/vm/src/stdlib/os.rs b/vm/src/stdlib/os.rs index 6c822822f..0c6fcfd8e 100644 --- a/vm/src/stdlib/os.rs +++ b/vm/src/stdlib/os.rs @@ -302,22 +302,20 @@ pub(super) mod _os { errno_err, DirFd, FollowSymlinks, IOErrorBuilder, OsPath, OsPathOrFd, OutputMode, SupportFunc, }; - use crate::common::lock::{OnceCell, PyRwLock}; use crate::{ builtins::{ PyBytesRef, PyGenericAlias, PyIntRef, PyStrRef, PyTuple, PyTupleRef, PyTypeRef, }, - common::{ - crt_fd::{Fd, Offset}, - suppress_iph, - }, + common::crt_fd::{Fd, Offset}, + common::lock::{OnceCell, PyRwLock}, + common::suppress_iph, convert::{IntoPyException, ToPyObject}, function::{ArgBytesLike, Either, FsPath, FuncArgs, OptionalArg}, protocol::PyIterReturn, recursion::ReprGuard, - types::{IterNext, IterNextIterable, PyStructSequence}, + types::{IterNext, IterNextIterable, PyStructSequence, Representable}, vm::VirtualMachine, - AsObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, + AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, }; use crossbeam_utils::atomic::AtomicCell; use itertools::Itertools; @@ -619,7 +617,7 @@ pub(super) mod _os { ino: AtomicCell>, } - #[pyclass] + #[pyclass(with(Representable))] impl DirEntry { #[pygetset] fn name(&self, vm: &VirtualMachine) -> PyResult { @@ -747,9 +745,16 @@ pub(super) mod _os { self.path(vm) } - #[pymethod(magic)] - fn repr(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let name = match zelf.get_attr("name", vm) { + #[pyclassmethod(magic)] + fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { + PyGenericAlias::new(cls, args, vm) + } + } + + impl Representable for DirEntry { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + let name = match zelf.as_object().get_attr("name", vm) { Ok(name) => Some(name), Err(e) if e.fast_isinstance(vm.ctx.exceptions.attribute_error) @@ -760,7 +765,7 @@ pub(super) mod _os { Err(e) => return Err(e), }; if let Some(name) = name { - if let Some(_guard) = ReprGuard::enter(vm, &zelf) { + if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { let repr = name.repr(vm)?; Ok(format!("<{} {}>", zelf.class(), repr)) } else { @@ -773,11 +778,6 @@ pub(super) mod _os { Ok(format!("<{}>", zelf.class())) } } - - #[pyclassmethod(magic)] - fn class_getitem(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { - PyGenericAlias::new(cls, args, vm) - } } #[pyattr] diff --git a/vm/src/stdlib/posix.rs b/vm/src/stdlib/posix.rs index a4270136e..2366cb622 100644 --- a/vm/src/stdlib/posix.rs +++ b/vm/src/stdlib/posix.rs @@ -28,9 +28,9 @@ pub mod module { errno_err, DirFd, FollowSymlinks, OsPath, OsPathOrFd, SupportFunc, TargetIsDirectory, _os, fs_metadata, IOErrorBuilder, }, - types::Constructor, + types::{Constructor, Representable}, utils::ToCString, - AsObject, PyObjectRef, PyPayload, PyResult, VirtualMachine, + AsObject, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine, }; use bitflags::bitflags; use nix::{ @@ -540,22 +540,13 @@ pub mod module { } } - #[pyclass(with(Constructor))] + #[pyclass(with(Constructor, Representable))] impl SchedParam { #[pygetset] fn sched_priority(&self, vm: &VirtualMachine) -> PyObjectRef { self.sched_priority.clone().to_pyobject(vm) } - #[pymethod(magic)] - fn repr(&self, vm: &VirtualMachine) -> PyResult { - let sched_priority_repr = self.sched_priority.repr(vm)?; - Ok(format!( - "posix.sched_param(sched_priority = {})", - sched_priority_repr.as_str() - )) - } - #[cfg(any( target_os = "linux", target_os = "netbsd", @@ -591,6 +582,17 @@ pub mod module { } } + impl Representable for SchedParam { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + let sched_priority_repr = zelf.sched_priority.repr(vm)?; + Ok(format!( + "posix.sched_param(sched_priority = {})", + sched_priority_repr.as_str() + )) + } + } + #[cfg(any( target_os = "linux", target_os = "netbsd", diff --git a/vm/src/stdlib/sre.rs b/vm/src/stdlib/sre.rs index 3ae0b5c34..93ecd7c24 100644 --- a/vm/src/stdlib/sre.rs +++ b/vm/src/stdlib/sre.rs @@ -13,9 +13,9 @@ mod _sre { function::{ArgCallable, OptionalArg, PosArgs, PyComparisonValue}, protocol::{PyBuffer, PyMappingMethods}, stdlib::sys, - types::{AsMapping, Comparable, Hashable}, - PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject, TryFromObject, - VirtualMachine, + types::{AsMapping, Comparable, Hashable, Representable}, + Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject, + TryFromObject, VirtualMachine, }; use core::str; use crossbeam_utils::atomic::AtomicCell; @@ -151,7 +151,7 @@ mod _sre { }; } - #[pyclass(with(Hashable, Comparable))] + #[pyclass(with(Hashable, Comparable, Representable))] impl Pattern { fn with_str(string: &PyObject, vm: &VirtualMachine, f: F) -> PyResult where @@ -336,51 +336,6 @@ mod _sre { }) } - #[pymethod(magic)] - fn repr(&self, vm: &VirtualMachine) -> PyResult { - let flag_names = [ - ("re.TEMPLATE", SreFlag::TEMPLATE), - ("re.IGNORECASE", SreFlag::IGNORECASE), - ("re.LOCALE", SreFlag::LOCALE), - ("re.MULTILINE", SreFlag::MULTILINE), - ("re.DOTALL", SreFlag::DOTALL), - ("re.UNICODE", SreFlag::UNICODE), - ("re.VERBOSE", SreFlag::VERBOSE), - ("re.DEBUG", SreFlag::DEBUG), - ("re.ASCII", SreFlag::ASCII), - ]; - - /* Omit re.UNICODE for valid string patterns. */ - let mut flags = self.flags; - if !self.isbytes - && (flags & (SreFlag::LOCALE | SreFlag::UNICODE | SreFlag::ASCII)) - == SreFlag::UNICODE - { - flags &= !SreFlag::UNICODE; - } - - let flags = flag_names - .iter() - .filter(|(_, flag)| flags.contains(*flag)) - .map(|(name, _)| name) - .join("|"); - - let pattern = self.pattern.repr(vm)?; - let truncated: String; - let s = if pattern.char_len() > 200 { - truncated = pattern.as_str().chars().take(200).collect(); - &truncated - } else { - pattern.as_str() - }; - - if flags.is_empty() { - Ok(format!("re.compile({s})")) - } else { - Ok(format!("re.compile({s}, {flags})")) - } - } - #[pygetset] fn flags(&self) -> u16 { self.flags.bits() @@ -512,6 +467,53 @@ mod _sre { } } + impl Representable for Pattern { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + let flag_names = [ + ("re.TEMPLATE", SreFlag::TEMPLATE), + ("re.IGNORECASE", SreFlag::IGNORECASE), + ("re.LOCALE", SreFlag::LOCALE), + ("re.MULTILINE", SreFlag::MULTILINE), + ("re.DOTALL", SreFlag::DOTALL), + ("re.UNICODE", SreFlag::UNICODE), + ("re.VERBOSE", SreFlag::VERBOSE), + ("re.DEBUG", SreFlag::DEBUG), + ("re.ASCII", SreFlag::ASCII), + ]; + + /* Omit re.UNICODE for valid string patterns. */ + let mut flags = zelf.flags; + if !zelf.isbytes + && (flags & (SreFlag::LOCALE | SreFlag::UNICODE | SreFlag::ASCII)) + == SreFlag::UNICODE + { + flags &= !SreFlag::UNICODE; + } + + let flags = flag_names + .iter() + .filter(|(_, flag)| flags.contains(*flag)) + .map(|(name, _)| name) + .join("|"); + + let pattern = zelf.pattern.repr(vm)?; + let truncated: String; + let s = if pattern.char_len() > 200 { + truncated = pattern.as_str().chars().take(200).collect(); + &truncated + } else { + pattern.as_str() + }; + + if flags.is_empty() { + Ok(format!("re.compile({s})")) + } else { + Ok(format!("re.compile({s}, {flags})")) + } + } + } + #[pyattr] #[pyclass(name = "Match")] #[derive(Debug, PyPayload)] @@ -524,7 +526,7 @@ mod _sre { regs: Vec<(isize, isize)>, } - #[pyclass(with(AsMapping))] + #[pyclass(with(AsMapping, Representable))] impl Match { pub(crate) fn new( state: &State, @@ -703,18 +705,6 @@ mod _sre { }) } - #[pymethod(magic)] - fn repr(&self, vm: &VirtualMachine) -> PyResult { - with_sre_str!(self.pattern, &self.string, vm, |str_drive| { - Ok(format!( - "", - self.regs[0].0, - self.regs[0].1, - self.get_slice(0, str_drive, vm).unwrap().repr(vm)? - )) - }) - } - fn get_index(&self, group: PyObjectRef, vm: &VirtualMachine) -> Option { let i = if let Ok(i) = group.try_index(vm) { i @@ -768,6 +758,20 @@ mod _sre { } } + impl Representable for Match { + #[inline] + fn repr_str(zelf: &Py, vm: &VirtualMachine) -> PyResult { + with_sre_str!(zelf.pattern, &zelf.string, vm, |str_drive| { + Ok(format!( + "", + zelf.regs[0].0, + zelf.regs[0].1, + zelf.get_slice(0, str_drive, vm).unwrap().repr(vm)? + )) + }) + } + } + #[pyattr] #[pyclass(name = "SRE_Scanner")] #[derive(Debug, PyPayload)] diff --git a/vm/src/stdlib/thread.rs b/vm/src/stdlib/thread.rs index b9699e140..4d3d04c8c 100644 --- a/vm/src/stdlib/thread.rs +++ b/vm/src/stdlib/thread.rs @@ -8,7 +8,7 @@ pub(crate) mod _thread { builtins::{PyDictRef, PyStr, PyTupleRef, PyTypeRef}, convert::ToPyException, function::{ArgCallable, Either, FuncArgs, KwArgs, OptionalArg, PySetterValue}, - types::{Constructor, GetAttr, SetAttr}, + types::{Constructor, GetAttr, Representable, SetAttr}, AsObject, Py, PyPayload, PyRef, PyResult, VirtualMachine, }; use parking_lot::{ @@ -97,12 +97,12 @@ pub(crate) mod _thread { } else { "unlocked" }; - format!( + Ok(format!( "<{} {} object at {:#x}>", status, $zelf.class().name(), $zelf.get_id() - ) + )) }}; } @@ -119,7 +119,7 @@ pub(crate) mod _thread { } } - #[pyclass(with(Constructor))] + #[pyclass(with(Constructor, Representable))] impl Lock { #[pymethod] #[pymethod(name = "acquire_lock")] @@ -146,11 +146,6 @@ pub(crate) mod _thread { fn locked(&self) -> bool { self.mu.is_locked() } - - #[pymethod(magic)] - fn repr(zelf: PyRef) -> String { - repr_lock_impl!(zelf) - } } impl Constructor for Lock { @@ -160,6 +155,13 @@ pub(crate) mod _thread { } } + impl Representable for Lock { + #[inline] + fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { + repr_lock_impl!(zelf) + } + } + pub type RawRMutex = RawReentrantMutex; #[pyattr] #[pyclass(module = "thread", name = "RLock")] @@ -174,7 +176,7 @@ pub(crate) mod _thread { } } - #[pyclass] + #[pyclass(with(Representable))] impl RLock { #[pyslot] fn slot_new(cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult { @@ -210,9 +212,11 @@ pub(crate) mod _thread { fn exit(&self, _args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> { self.release(vm) } + } - #[pymethod(magic)] - fn repr(zelf: PyRef) -> String { + impl Representable for RLock { + #[inline] + fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { repr_lock_impl!(zelf) } } diff --git a/wasm/lib/src/js_module.rs b/wasm/lib/src/js_module.rs index e689b6037..907f65936 100644 --- a/wasm/lib/src/js_module.rs +++ b/wasm/lib/src/js_module.rs @@ -14,7 +14,7 @@ mod _js { convert::{IntoObject, ToPyObject}, function::{ArgCallable, OptionalArg, OptionalOption, PosArgs}, protocol::PyIterReturn, - types::{IterNext, IterNextIterable}, + types::{IterNext, IterNextIterable, Representable}, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine, }; use std::{cell, fmt, future}; @@ -90,7 +90,7 @@ mod _js { } } - #[pyclass] + #[pyclass(with(Representable))] impl PyJsValue { #[inline] pub fn new(value: impl Into) -> PyJsValue { @@ -265,10 +265,12 @@ mod _js { fn instanceof(&self, rhs: PyJsValueRef, vm: &VirtualMachine) -> PyResult { instance_of(&self.value, &rhs.value).map_err(|err| new_js_error(vm, err)) } + } - #[pymethod(magic)] - fn repr(&self) -> String { - format!("{:?}", self.value) + impl Representable for PyJsValue { + #[inline] + fn repr_str(zelf: &Py, _vm: &VirtualMachine) -> PyResult { + Ok(format!("{:?}", zelf.value)) } }