add class doc string

This commit is contained in:
Nikolay Kim 2017-06-12 17:15:26 -07:00
parent 4d68f7f2a3
commit d7c3d34198
6 changed files with 61 additions and 12 deletions

View File

@ -6,9 +6,12 @@ use std::collections::HashMap;
use syn;
use quote::Tokens;
use utils;
pub fn build_py_class(ast: &mut syn::DeriveInput, attr: String) -> Tokens {
let params = parse_attribute(attr);
let doc = utils::get_doc(&ast.attrs);
let base = syn::Ident::from("_pyo3::PyObject");
let mut token: Option<syn::Ident> = None;
@ -26,7 +29,7 @@ pub fn build_py_class(ast: &mut syn::DeriveInput, attr: String) -> Tokens {
}
let dummy_const = syn::Ident::new(format!("_IMPL_PYO3_CLS_{}", ast.ident));
let tokens = impl_class(&ast.ident, &base, token, params);
let tokens = impl_class(&ast.ident, &base, token, doc, params);
quote! {
#[feature(specialization)]
@ -42,7 +45,8 @@ pub fn build_py_class(ast: &mut syn::DeriveInput, attr: String) -> Tokens {
}
fn impl_class(cls: &syn::Ident, base: &syn::Ident,
token: Option<syn::Ident>, params: HashMap<&'static str, syn::Ident>) -> Tokens {
token: Option<syn::Ident>, doc: syn::Lit,
params: HashMap<&'static str, syn::Ident>) -> Tokens {
let cls_name = match params.get("name") {
Some(name) => quote! { #name }.as_str().to_string(),
None => quote! { #cls }.as_str().to_string()
@ -133,9 +137,12 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
((bs + align - 1) / align * align) as isize
}
#[inline]
fn type_name() -> &'static str { #cls_name }
fn type_description() -> &'static str {
#doc
}
#[inline]
fn type_object() -> &'static mut _pyo3::ffi::PyTypeObject {
static mut TYPE_OBJECT: _pyo3::ffi::PyTypeObject = _pyo3::ffi::PyTypeObject_INIT;
@ -153,11 +160,11 @@ fn impl_class(cls: &syn::Ident, base: &syn::Ident,
if (ty.tp_flags & _pyo3::ffi::Py_TPFLAGS_READY) == 0 {
// automatically initialize the class on-demand
let to = _pyo3::typeob::initialize_type::<#cls>(
py, None, <#cls as _pyo3::typeob::PyTypeInfo>::type_name(), ty)
.expect(
format!("An error occurred while initializing class {}",
<#cls as _pyo3::typeob::PyTypeInfo>::type_name())
.as_ref());
py, None, <#cls as _pyo3::typeob::PyTypeInfo>::type_name(),
<#cls as _pyo3::typeob::PyTypeInfo>::type_description(), ty).expect(
format!("An error occurred while initializing class {}",
<#cls as _pyo3::typeob::PyTypeInfo>::type_name())
.as_ref());
py.release(to);
}
});

View File

@ -67,7 +67,9 @@ pub fn build_py_proto(ast: &mut syn::Item) -> Tokens {
}
}
fn impl_proto_impl(ty: &Box<syn::Ty>, impls: &mut Vec<syn::ImplItem>, proto: &defs::Proto) -> Tokens {
fn impl_proto_impl(ty: &Box<syn::Ty>,
impls: &mut Vec<syn::ImplItem>, proto: &defs::Proto) -> Tokens
{
let mut tokens = Tokens::new();
let mut py_methods = Vec::new();

View File

@ -1,3 +1,4 @@
use syn;
use quote::{Tokens, ToTokens};
@ -11,3 +12,21 @@ pub fn for_err_msg(i: &ToTokens) -> String {
i.to_tokens(&mut tokens);
tokens.as_str().to_string()
}
pub fn get_doc(attrs: &Vec<syn::Attribute>) -> syn::Lit {
let mut doc = Vec::new();
for attr in attrs.iter() {
match attr.value {
syn::MetaItem::NameValue(ref ident, ref lit) => {
if ident.as_ref() == "doc" {
let s = quote!{ #lit }.to_string();
doc.push(s[1..s.len()-1].to_owned())
}
}
_ => (),
}
}
let doc = doc.join("\n");
syn::Lit::Str(format!("{}\0", doc), syn::StrStyle::Cooked)
}

View File

@ -112,7 +112,10 @@ impl<'p> PyModule {
} else {
// automatically initialize the class
let name = self.name(py)?;
let to = ::typeob::initialize_type::<T>(py, Some(name), type_name, ty)
let type_description = <T as ::typeob::PyTypeInfo>::type_description();
let to = ::typeob::initialize_type::<T>(
py, Some(name), type_name, type_description, ty)
.expect(format!("An error occurred while initializing class {}",
<T as ::typeob::PyTypeInfo>::type_name()).as_ref());
py.release(to);

View File

@ -29,6 +29,9 @@ pub trait PyTypeInfo {
/// Type name
fn type_name() -> &'static str;
/// Type description
fn type_description() -> &'static str { "\0" }
/// PyTypeObject instance for this type
fn type_object() -> &'static mut ffi::PyTypeObject;
@ -131,7 +134,8 @@ impl<T> PyTypeObject for T where T: PyObjectAlloc<T> + PyTypeInfo {
if (ty.tp_flags & ffi::Py_TPFLAGS_READY) == 0 {
// automatically initialize the class on-demand
let to = initialize_type::<T>(
py, None, <T as PyTypeInfo>::type_name(), ty).expect(
py, None, <T as PyTypeInfo>::type_name(),
<T as PyTypeInfo>::type_description(), ty).expect(
format!("An error occurred while initializing class {}",
<T as PyTypeInfo>::type_name()).as_ref());
py.release(to);
@ -148,7 +152,8 @@ impl<T> PyTypeObject for T where T: PyObjectAlloc<T> + PyTypeInfo {
pub fn initialize_type<T>(py: Python, module_name: Option<&str>, type_name: &str,
type_object: &mut ffi::PyTypeObject) -> PyResult<PyType>
type_description: &'static str, type_object: &mut ffi::PyTypeObject)
-> PyResult<PyType>
where T: PyObjectAlloc<T> + PyTypeInfo
{
// type name
@ -160,6 +165,7 @@ pub fn initialize_type<T>(py: Python, module_name: Option<&str>, type_name: &str
"Module name/type name must not contain NUL byte").into_raw();
type_object.tp_name = name;
type_object.tp_doc = type_description.as_ptr() as *const _;
// dealloc
type_object.tp_dealloc = Some(tp_dealloc_callback::<T>);

View File

@ -51,6 +51,18 @@ fn empty_class() {
py_assert!(py, typeobj, "typeobj.__name__ == 'EmptyClass'");
}
#[py::class]
/// Line1
/// Line2
struct ClassWithDocs { }
#[test]
fn class_with_docstr() {
let gil = Python::acquire_gil();
let py = gil.python();
let typeobj = py.get_type::<ClassWithDocs>();
py_run!(py, typeobj, "assert typeobj.__doc__ == '/// Line1\\n/// Line2'");
}
#[py::class(name=CustomName)]
struct EmptyClass2 { }