diff --git a/vm/src/builtins/builtinfunc.rs b/vm/src/builtins/builtinfunc.rs index 62b934f8c6b..aa8c954d0f9 100644 --- a/vm/src/builtins/builtinfunc.rs +++ b/vm/src/builtins/builtinfunc.rs @@ -1,6 +1,7 @@ use std::fmt; use super::classmethod::PyClassMethod; +use super::pytype; use crate::builtins::pystr::PyStrRef; use crate::builtins::pytype::PyTypeRef; use crate::function::{FuncArgs, PyNativeFunc}; @@ -142,6 +143,13 @@ impl PyBuiltinFunction { fn repr(&self) -> String { format!("", self.value.name) } + #[pyproperty(magic)] + fn text_signature(&self) -> Option { + self.value.doc.as_ref().and_then(|doc| { + pytype::get_text_signature_from_internal_doc(self.value.name.as_str(), doc.as_str()) + .map(|signature| signature.to_string()) + }) + } } // `PyBuiltinMethod` is similar to both `PyMethodDescrObject` in @@ -207,6 +215,13 @@ impl PyBuiltinMethod { fn doc(&self) -> Option { self.value.doc.clone() } + #[pyproperty(magic)] + fn text_signature(&self) -> Option { + self.value.doc.as_ref().and_then(|doc| { + pytype::get_text_signature_from_internal_doc(self.value.name.as_str(), doc.as_str()) + .map(|signature| signature.to_string()) + }) + } #[pymethod(magic)] fn repr(&self) -> String { format!( diff --git a/vm/src/builtins/object.rs b/vm/src/builtins/object.rs index fa8a88682a0..6f47968ddd0 100644 --- a/vm/src/builtins/object.rs +++ b/vm/src/builtins/object.rs @@ -14,7 +14,13 @@ use crate::{ PyContext, PyObject, PyObjectRef, PyResult, PyValue, TryFromObject, TypeProtocol, }; -/// The most base type +/// object() +/// -- +/// +/// The base class of the class hierarchy. +/// +/// When called, it accepts no arguments and returns a new featureless +/// instance that has no instance attributes and cannot be given any. #[pyclass(module = false, name = "object")] #[derive(Debug)] pub struct PyBaseObject; @@ -27,6 +33,10 @@ impl PyValue for PyBaseObject { #[pyimpl(flags(BASETYPE))] impl PyBaseObject { + /// __new__($type, *args, **kwargs) + /// -- + /// + /// Create and return a new object. See help(type) for accurate signature. #[pyslot] fn tp_new(mut args: FuncArgs, vm: &VirtualMachine) -> PyResult { // more or less __new__ operator @@ -82,6 +92,10 @@ impl PyBaseObject { Ok(res) } + /// __eq__($self, value, /) + /// -- + /// + /// Return self==value. #[pymethod(magic)] fn eq( zelf: PyObjectRef, @@ -90,6 +104,11 @@ impl PyBaseObject { ) -> PyResult { Self::cmp(&zelf, &other, PyComparisonOp::Eq, vm) } + + /// __ne__($self, value, /) + /// -- + /// + /// Return self!=value. #[pymethod(magic)] fn ne( zelf: PyObjectRef, @@ -98,6 +117,11 @@ impl PyBaseObject { ) -> PyResult { Self::cmp(&zelf, &other, PyComparisonOp::Ne, vm) } + + /// __lt__($self, value, /) + /// -- + /// + /// Return self PyResult { Self::cmp(&zelf, &other, PyComparisonOp::Lt, vm) } + + /// __le__($self, value, /) + /// -- + /// + /// Return self<=value. #[pymethod(magic)] fn le( zelf: PyObjectRef, @@ -114,6 +143,11 @@ impl PyBaseObject { ) -> PyResult { Self::cmp(&zelf, &other, PyComparisonOp::Le, vm) } + + /// __ge__($self, value, /) + /// -- + /// + /// Return self>=value. #[pymethod(magic)] fn ge( zelf: PyObjectRef, @@ -122,6 +156,11 @@ impl PyBaseObject { ) -> PyResult { Self::cmp(&zelf, &other, PyComparisonOp::Ge, vm) } + + /// __gt__($self, value, /) + /// -- + /// + /// Return self>value. #[pymethod(magic)] fn gt( zelf: PyObjectRef, @@ -131,6 +170,10 @@ impl PyBaseObject { Self::cmp(&zelf, &other, PyComparisonOp::Gt, vm) } + /// __setattr__($self, name, value /) + /// -- + /// + /// Implement setattr(self, name, value). #[pymethod] fn __setattr__( obj: PyObjectRef, @@ -141,6 +184,10 @@ impl PyBaseObject { setattr(&obj, attr_name, Some(value), vm) } + /// __delattr__($self, name, /) + /// -- + /// + /// Implement delattr(self, name). #[pymethod] fn __delattr__(obj: PyObjectRef, attr_name: PyStrRef, vm: &VirtualMachine) -> PyResult<()> { setattr(&obj, attr_name, None, vm) @@ -156,11 +203,19 @@ impl PyBaseObject { setattr(obj, attr_name, value, vm) } + /// __str__($self, /) + /// -- + /// + /// Return str(self). #[pymethod(magic)] fn str(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { vm.to_repr(&zelf) } + /// __repr__($self, /) + /// -- + /// + /// Return repr(self). #[pymethod(magic)] fn repr(zelf: PyObjectRef) -> String { format!("<{} object at {:#x}>", zelf.class().name, zelf.get_id()) @@ -233,6 +288,10 @@ impl PyBaseObject { } } + /// __getattribute__($self, name, /) + /// -- + /// + /// Return getattr(self, name). #[pymethod(name = "__getattribute__")] #[pyslot] pub(crate) fn getattro(obj: PyObjectRef, name: PyStrRef, vm: &VirtualMachine) -> PyResult { @@ -262,6 +321,10 @@ impl PyBaseObject { Ok(zelf.get_id() as _) } + /// __hash__($self, /) + /// -- + /// + /// Return hash(self). #[pymethod(magic)] fn hash(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult { Self::tp_hash(&zelf, vm) diff --git a/vm/src/builtins/pytype.rs b/vm/src/builtins/pytype.rs index 77e1e195c5c..4800576cf18 100644 --- a/vm/src/builtins/pytype.rs +++ b/vm/src/builtins/pytype.rs @@ -517,6 +517,37 @@ impl PyType { "Setting __dict__ attribute on a type isn't yet implemented".to_owned(), )) } + + #[pyproperty(magic)] + fn text_signature(&self) -> Option { + self.slots + .doc + .and_then(|doc| get_text_signature_from_internal_doc(self.name().as_str(), doc)) + .map(|signature| signature.to_string()) + } +} + +const SIGNATURE_END_MARKER: &str = ")\n--\n\n"; +fn get_signature(doc: &str) -> Option<&str> { + doc.find(SIGNATURE_END_MARKER) + .map(|index| &doc[..index + 1]) +} + +fn find_signature<'a>(name: &str, doc: &'a str) -> Option<&'a str> { + let name = name.rsplit('.').next().unwrap(); + let doc = doc.strip_prefix(name)?; + if !doc.starts_with('(') { + None + } else { + Some(doc) + } +} + +pub(crate) fn get_text_signature_from_internal_doc<'a>( + name: &str, + internal_doc: &'a str, +) -> Option<&'a str> { + find_signature(name, internal_doc).and_then(get_signature) } impl SlotGetattro for PyType { diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index ffcb70831de..016ce988191 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -1126,6 +1126,7 @@ pub trait PyClassImpl: PyClassDef { let mut slots = PyTypeSlots { flags: Self::TP_FLAGS, name: PyRwLock::new(Some(Self::TP_NAME.to_owned())), + doc: Self::DOC, ..Default::default() }; Self::extend_slots(&mut slots); diff --git a/vm/src/slots.rs b/vm/src/slots.rs index 515a6be1cbd..5baf6f97119 100644 --- a/vm/src/slots.rs +++ b/vm/src/slots.rs @@ -96,7 +96,9 @@ pub struct PyTypeSlots { // Flags to define presence of optional/expanded features pub flags: PyTpFlags, + // tp_doc + pub doc: Option<&'static str>, // Strong reference on a heap type, borrowed reference on a static type // tp_base