diff --git a/derive/src/pyclass.rs b/derive/src/pyclass.rs index 8a1c84bc9..512dacd77 100644 --- a/derive/src/pyclass.rs +++ b/derive/src/pyclass.rs @@ -271,13 +271,19 @@ where let item_meta = MethodItemMeta::from_attr(ident.clone(), &item_attr)?; let py_name = item_meta.method_name()?; - let new_func = Ident::new(&format!("new_{}", &self.method_type), args.item.span()); + let build_func = Ident::new(&format!("build_{}", &self.method_type), args.item.span()); let tokens = { - let new_func = quote_spanned!( - ident.span() => .#new_func(Self::#ident) + let doc = args.attrs.doc().map_or_else( + TokenStream::new, + |doc| quote!(.with_doc(#doc.to_owned(), ctx)), ); quote! { - class.set_str_attr(#py_name, ctx#new_func); + class.set_str_attr( + #py_name, + ctx.new_function_named(Self::#ident, #py_name.to_owned()) + #doc + .#build_func(ctx), + ); } }; diff --git a/derive/src/pymodule.rs b/derive/src/pymodule.rs index 231b8b578..aa39c2b15 100644 --- a/derive/src/pymodule.rs +++ b/derive/src/pymodule.rs @@ -257,9 +257,17 @@ impl ModuleItem for FunctionItem { let py_name = item_meta.simple_name()?; let item = { + let doc = args.attrs.doc().map_or_else( + TokenStream::new, + |doc| quote!(.with_doc(#doc.to_owned(), &vm.ctx)), + ); let module = args.module_name(); - let new_func = quote_spanned!( - ident.span() => vm.ctx.new_function_named(#ident, #module.to_owned(), #py_name.to_owned()) + let new_func = quote_spanned!(ident.span()=> + vm.ctx.new_function_named(#ident, #py_name.to_owned()) + #doc + .into_function() + .with_module(vm.ctx.new_str(#module.to_owned())) + .build(&vm.ctx) ); quote! { vm.__module_set_attr(&module, #py_name, #new_func).unwrap(); diff --git a/vm/src/lib.rs b/vm/src/lib.rs index 3354d7764..d99021e82 100644 --- a/vm/src/lib.rs +++ b/vm/src/lib.rs @@ -77,3 +77,8 @@ mod vm; pub use self::vm::{InitParameter, Interpreter, PySettings, VirtualMachine}; pub use rustpython_bytecode::*; pub use rustpython_common as common; + +#[doc(hidden)] +pub mod __exports { + pub use paste; +} diff --git a/vm/src/macros.rs b/vm/src/macros.rs index 37588ddb2..292dc674c 100644 --- a/vm/src/macros.rs +++ b/vm/src/macros.rs @@ -235,13 +235,16 @@ macro_rules! class_or_notimplemented { #[macro_export] macro_rules! named_function { ($ctx:expr, $module:ident, $func:ident) => {{ - paste::expr! { - $crate::pyobject::PyContext::new_function_named( - &$ctx, + #[allow(unused_variables)] // weird lint, something to do with paste probably + let ctx: &$crate::pyobject::PyContext = &$ctx; + $crate::__exports::paste::expr! { + ctx.new_function_named( [<$module _ $func>], stringify!($module).to_owned(), - stringify!($func).to_owned(), ) + .into_function() + .with_module(ctx.new_str(stringify!($func).to_owned())) + .build(ctx) } }}; } diff --git a/vm/src/obj/objbuiltinfunc.rs b/vm/src/obj/objbuiltinfunc.rs index be66d7193..238e086a7 100644 --- a/vm/src/obj/objbuiltinfunc.rs +++ b/vm/src/obj/objbuiltinfunc.rs @@ -1,17 +1,64 @@ use std::fmt; +use super::objclassmethod::PyClassMethod; use crate::function::{OptionalArg, PyFuncArgs, PyNativeFunc}; use crate::obj::objstr::PyStringRef; use crate::obj::objtype::PyClassRef; -use crate::pyobject::{PyClassImpl, PyContext, PyObjectRef, PyResult, PyValue, TypeProtocol}; +use crate::pyobject::{ + PyClassImpl, PyContext, PyObject, PyObjectRef, PyResult, PyValue, TypeProtocol, +}; use crate::slots::{SlotCall, SlotDescriptor}; use crate::vm::VirtualMachine; +pub struct PyFuncDef { + pub func: PyNativeFunc, + pub name: Option, + pub doc: Option, +} + +impl From for PyFuncDef { + fn from(func: PyNativeFunc) -> Self { + Self { + func, + name: None, + doc: None, + } + } +} + +impl PyFuncDef { + pub fn with_doc(mut self, doc: String, ctx: &PyContext) -> Self { + self.doc = Some(ctx.new_stringref(doc)); + self + } + + pub fn into_function(self) -> PyBuiltinFunction { + self.into() + } + pub fn build_function(self, ctx: &PyContext) -> PyObjectRef { + self.into_function().build(ctx) + } + pub fn build_method(self, ctx: &PyContext) -> PyObjectRef { + PyObject::new( + PyBuiltinMethod::from(self), + ctx.types.method_descriptor_type.clone(), + None, + ) + } + pub fn build_classmethod(self, ctx: &PyContext) -> PyObjectRef { + // TODO: classmethod_descriptor + PyObject::new( + PyClassMethod::from(self.build_method(ctx)), + ctx.types.classmethod_type.clone(), + None, + ) + } +} + #[pyclass(name = "builtin_function_or_method", module = false)] pub struct PyBuiltinFunction { - value: PyNativeFunc, - module: Option, - name: Option, + value: PyFuncDef, + module: Option, } impl PyValue for PyBuiltinFunction { @@ -28,49 +75,62 @@ impl fmt::Debug for PyBuiltinFunction { impl From for PyBuiltinFunction { fn from(value: PyNativeFunc) -> Self { + PyFuncDef::from(value).into() + } +} +impl From for PyBuiltinFunction { + fn from(value: PyFuncDef) -> Self { Self { value, module: None, - name: None, } } } impl PyBuiltinFunction { - pub fn new_with_name(value: PyNativeFunc, module: PyStringRef, name: PyStringRef) -> Self { - Self { - value, - module: Some(module), - name: Some(name), - } + pub fn with_module(mut self, module: PyObjectRef) -> Self { + self.module = Some(module); + self + } + + pub fn build(self, ctx: &PyContext) -> PyObjectRef { + PyObject::new( + self, + ctx.types.builtin_function_or_method_type.clone(), + None, + ) } pub fn as_func(&self) -> &PyNativeFunc { - &self.value + &self.value.func } } impl SlotCall for PyBuiltinFunction { fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult { - (self.value)(vm, args) + (self.value.func)(vm, args) } } #[pyimpl(with(SlotCall), flags(HAS_DICT))] impl PyBuiltinFunction { #[pyproperty(magic)] - fn module(&self) -> Option { - self.module.clone() + fn module(&self, vm: &VirtualMachine) -> PyObjectRef { + vm.unwrap_or_none(self.module.clone()) } #[pyproperty(magic)] fn name(&self) -> Option { - self.name.clone() + self.value.name.clone() + } + #[pyproperty(magic)] + fn doc(&self) -> Option { + self.value.doc.clone() } } #[pyclass(module = false, name = "method_descriptor")] pub struct PyBuiltinMethod { - function: PyBuiltinFunction, + value: PyFuncDef, } impl PyValue for PyBuiltinMethod { @@ -85,23 +145,25 @@ impl fmt::Debug for PyBuiltinMethod { } } -impl From for PyBuiltinMethod { - fn from(value: PyNativeFunc) -> Self { - Self { - function: value.into(), - } +impl From for PyBuiltinMethod { + fn from(value: PyFuncDef) -> Self { + Self { value } } } impl PyBuiltinMethod { - pub fn new_with_name(value: PyNativeFunc, module: PyStringRef, name: PyStringRef) -> Self { + pub fn new_with_name(func: PyNativeFunc, name: PyStringRef) -> Self { Self { - function: PyBuiltinFunction::new_with_name(value, module, name), + value: PyFuncDef { + func, + name: Some(name), + doc: None, + }, } } pub fn as_func(&self) -> &PyNativeFunc { - &self.function.value + &self.value.func } } @@ -126,19 +188,19 @@ impl SlotDescriptor for PyBuiltinMethod { impl SlotCall for PyBuiltinMethod { fn call(&self, args: PyFuncArgs, vm: &VirtualMachine) -> PyResult { - (self.function.value)(vm, args) + (self.value.func)(vm, args) } } #[pyimpl(with(SlotDescriptor, SlotCall))] impl PyBuiltinMethod { #[pyproperty(magic)] - fn module(&self) -> Option { - self.function.module.clone() + fn name(&self) -> Option { + self.value.name.clone() } #[pyproperty(magic)] - fn name(&self) -> Option { - self.function.name.clone() + fn doc(&self) -> Option { + self.value.doc.clone() } } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index a304f0627..378febc1a 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -12,10 +12,9 @@ use num_traits::ToPrimitive; use crate::bytecode; use crate::exceptions::{self, PyBaseExceptionRef}; use crate::function::{IntoPyNativeFunc, PyFuncArgs}; -use crate::obj::objbuiltinfunc::{PyBuiltinFunction, PyBuiltinMethod}; +use crate::obj::objbuiltinfunc::PyFuncDef; use crate::obj::objbytearray; use crate::obj::objbytes; -use crate::obj::objclassmethod::PyClassMethod; use crate::obj::objcode; use crate::obj::objcode::PyCodeRef; use crate::obj::objcomplex::PyComplex; @@ -141,7 +140,7 @@ impl PyContext { let empty_tuple = create_object(PyTuple::from(vec![]), &types.tuple_type); let tp_new_wrapper = create_object( - PyBuiltinFunction::from(objtype::tp_new_wrapper.into_func()), + PyFuncDef::from(objtype::tp_new_wrapper.into_func()).into_function(), &types.builtin_function_or_method_type, ) .into_object(); @@ -213,9 +212,9 @@ impl PyContext { pub fn new_str(&self, s: S) -> PyObjectRef where - objstr::PyString: std::convert::From, + S: Into, { - PyObject::new(objstr::PyString::from(s), self.types.str_type.clone(), None) + PyObject::new(s.into(), self.types.str_type.clone(), None) } pub fn new_bytes(&self, data: Vec) -> PyObjectRef { @@ -284,46 +283,34 @@ impl PyContext { where F: IntoPyNativeFunc, { - PyObject::new( - PyBuiltinFunction::from(f.into_func()), - self.types.builtin_function_or_method_type.clone(), - None, - ) + PyFuncDef::from(f.into_func()).build_function(self) } - pub fn new_function_named(&self, f: F, module: String, name: String) -> PyObjectRef + pub(crate) fn new_stringref(&self, s: String) -> objstr::PyStringRef { + PyRef::new_ref(objstr::PyString::from(s), self.types.str_type.clone(), None) + } + + pub fn new_function_named(&self, f: F, name: String) -> PyFuncDef where F: IntoPyNativeFunc, { - let stringref = - |s| PyRef::new_ref(objstr::PyString::from(s), self.types.str_type.clone(), None); - PyObject::new( - PyBuiltinFunction::new_with_name(f.into_func(), stringref(module), stringref(name)), - self.types.builtin_function_or_method_type.clone(), - None, - ) + let mut f = PyFuncDef::from(f.into_func()); + f.name = Some(self.new_stringref(name)); + f } pub fn new_method(&self, f: F) -> PyObjectRef where F: IntoPyNativeFunc, { - PyObject::new( - PyBuiltinMethod::from(f.into_func()), - self.types.method_descriptor_type.clone(), - None, - ) + PyFuncDef::from(f.into_func()).build_method(self) } pub fn new_classmethod(&self, f: F) -> PyObjectRef where F: IntoPyNativeFunc, { - PyObject::new( - PyClassMethod::from(self.new_method(f)), - self.types.classmethod_type.clone(), - None, - ) + PyFuncDef::from(f.into_func()).build_classmethod(self) } pub fn new_staticmethod(&self, f: F) -> PyObjectRef where