Merge pull request #1169 from PyO3/doc-null-check
Add null-check for function's documents
This commit is contained in:
commit
cb90c5171d
|
@ -6,10 +6,16 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
## Added
|
||||
### Added
|
||||
- Add support for keyword-only arguments without default values in `#[pyfunction]`. [#1209](https://github.com/PyO3/pyo3/pull/1209)
|
||||
- Add a wrapper for `PyErr_CheckSignals()` as `Python::check_signals()`. [#1214](https://github.com/PyO3/pyo3/pull/1214)
|
||||
|
||||
### Changed
|
||||
- Fields of `PyMethodDef`, `PyGetterDef`, `PySetterDef`, and `PyClassAttributeDef` are now private. [#1169](https://github.com/PyO3/pyo3/pull/1169)
|
||||
|
||||
### Fixed
|
||||
- Fix invalid document for protocol methods. [#1169](https://github.com/PyO3/pyo3/pull/1169)
|
||||
|
||||
## [0.12.1] - 2020-09-16
|
||||
### Fixed
|
||||
- Fix building for a 32-bit Python on 64-bit Windows with a 64-bit Rust toolchain. [#1179](https://github.com/PyO3/pyo3/pull/1179)
|
||||
|
|
|
@ -754,14 +754,14 @@ impl pyo3::IntoPy<PyObject> for MyClass {
|
|||
}
|
||||
|
||||
pub struct Pyo3MethodsInventoryForMyClass {
|
||||
methods: &'static [pyo3::class::PyMethodDefType],
|
||||
methods: Vec<pyo3::class::PyMethodDefType>,
|
||||
}
|
||||
impl pyo3::class::methods::PyMethodsInventory for Pyo3MethodsInventoryForMyClass {
|
||||
fn new(methods: &'static [pyo3::class::PyMethodDefType]) -> Self {
|
||||
fn new(methods: Vec<pyo3::class::PyMethodDefType>) -> Self {
|
||||
Self { methods }
|
||||
}
|
||||
fn get(&self) -> &'static [pyo3::class::PyMethodDefType] {
|
||||
self.methods
|
||||
fn get(&'static self) -> &'static [pyo3::class::PyMethodDefType] {
|
||||
&self.methods
|
||||
}
|
||||
}
|
||||
impl pyo3::class::methods::HasMethodsInventory for MyClass {
|
||||
|
|
|
@ -133,7 +133,9 @@ impl<'a> Container<'a> {
|
|||
"Cannot derive FromPyObject for empty structs and variants.",
|
||||
));
|
||||
}
|
||||
let transparent = attrs.iter().any(ContainerAttribute::transparent);
|
||||
let transparent = attrs
|
||||
.iter()
|
||||
.any(|attr| *attr == ContainerAttribute::Transparent);
|
||||
if transparent {
|
||||
Self::check_transparent_len(fields)?;
|
||||
}
|
||||
|
@ -182,7 +184,6 @@ impl<'a> Container<'a> {
|
|||
let err_name = attrs
|
||||
.iter()
|
||||
.find_map(|a| a.annotation())
|
||||
.cloned()
|
||||
.unwrap_or_else(|| path.segments.last().unwrap().ident.to_string());
|
||||
|
||||
let v = Container {
|
||||
|
@ -306,18 +307,10 @@ enum ContainerAttribute {
|
|||
}
|
||||
|
||||
impl ContainerAttribute {
|
||||
/// Return whether this attribute is `Transparent`
|
||||
fn transparent(&self) -> bool {
|
||||
match self {
|
||||
ContainerAttribute::Transparent => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience method to access `ErrorAnnotation`.
|
||||
fn annotation(&self) -> Option<&String> {
|
||||
fn annotation(&self) -> Option<String> {
|
||||
match self {
|
||||
ContainerAttribute::ErrorAnnotation(s) => Some(s),
|
||||
ContainerAttribute::ErrorAnnotation(s) => Some(s.to_string()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -200,7 +200,7 @@ pub fn add_fn_to_module(
|
|||
doc,
|
||||
};
|
||||
|
||||
let doc = &spec.doc;
|
||||
let doc = syn::LitByteStr::new(spec.doc.value().as_bytes(), spec.doc.span());
|
||||
|
||||
let python_name = &spec.python_name;
|
||||
|
||||
|
@ -212,7 +212,16 @@ pub fn add_fn_to_module(
|
|||
fn #function_wrapper_ident<'a>(
|
||||
args: impl Into<pyo3::derive_utils::PyFunctionArguments<'a>>
|
||||
) -> pyo3::PyResult<&'a pyo3::types::PyCFunction> {
|
||||
pyo3::types::PyCFunction::new_with_keywords(#wrapper_ident, stringify!(#python_name), #doc, args.into())
|
||||
let name = concat!(stringify!(#python_name), "\0");
|
||||
let name = std::ffi::CStr::from_bytes_with_nul(name.as_bytes()).unwrap();
|
||||
let doc = std::ffi::CStr::from_bytes_with_nul(#doc).unwrap();
|
||||
pyo3::types::PyCFunction::internal_new(
|
||||
name,
|
||||
doc,
|
||||
pyo3::class::PyMethodType::PyCFunctionWithKeywords(#wrapper_ident),
|
||||
pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS,
|
||||
args.into(),
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -215,14 +215,14 @@ fn impl_methods_inventory(cls: &syn::Ident) -> TokenStream {
|
|||
quote! {
|
||||
#[doc(hidden)]
|
||||
pub struct #inventory_cls {
|
||||
methods: &'static [pyo3::class::PyMethodDefType],
|
||||
methods: Vec<pyo3::class::PyMethodDefType>,
|
||||
}
|
||||
impl pyo3::class::methods::PyMethodsInventory for #inventory_cls {
|
||||
fn new(methods: &'static [pyo3::class::PyMethodDefType]) -> Self {
|
||||
fn new(methods: Vec<pyo3::class::PyMethodDefType>) -> Self {
|
||||
Self { methods }
|
||||
}
|
||||
fn get(&self) -> &'static [pyo3::class::PyMethodDefType] {
|
||||
self.methods
|
||||
fn get(&'static self) -> &'static [pyo3::class::PyMethodDefType] {
|
||||
&self.methods
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -483,7 +483,7 @@ fn impl_descriptors(
|
|||
pyo3::inventory::submit! {
|
||||
#![crate = pyo3] {
|
||||
type Inventory = <#cls as pyo3::class::methods::HasMethodsInventory>::Methods;
|
||||
<Inventory as pyo3::class::methods::PyMethodsInventory>::new(&[#(#py_methods),*])
|
||||
<Inventory as pyo3::class::methods::PyMethodsInventory>::new(vec![#(#py_methods),*])
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -43,7 +43,7 @@ pub fn impl_methods(ty: &syn::Type, impls: &mut Vec<syn::ImplItem>) -> syn::Resu
|
|||
pyo3::inventory::submit! {
|
||||
#![crate = pyo3] {
|
||||
type Inventory = <#ty as pyo3::class::methods::HasMethodsInventory>::Methods;
|
||||
<Inventory as pyo3::class::methods::PyMethodsInventory>::new(&[#(
|
||||
<Inventory as pyo3::class::methods::PyMethodsInventory>::new(vec![#(
|
||||
#(#cfg_attributes)*
|
||||
#methods
|
||||
),*])
|
||||
|
|
|
@ -570,12 +570,11 @@ pub fn impl_py_method_def(spec: &FnSpec, wrapper: &TokenStream) -> TokenStream {
|
|||
pyo3::class::PyMethodDefType::Method({
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#python_name),
|
||||
ml_meth: pyo3::class::PyMethodType::PyCFunction(__wrap),
|
||||
ml_flags: pyo3::ffi::METH_NOARGS,
|
||||
ml_doc: #doc,
|
||||
}
|
||||
pyo3::class::PyMethodDef::cfunction(
|
||||
concat!(stringify!(#python_name), "\0"),
|
||||
__wrap,
|
||||
#doc
|
||||
)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
|
@ -583,12 +582,12 @@ pub fn impl_py_method_def(spec: &FnSpec, wrapper: &TokenStream) -> TokenStream {
|
|||
pyo3::class::PyMethodDefType::Method({
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#python_name),
|
||||
ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap),
|
||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS,
|
||||
ml_doc: #doc,
|
||||
}
|
||||
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
||||
concat!(stringify!(#python_name), "\0"),
|
||||
__wrap,
|
||||
0,
|
||||
#doc
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -601,12 +600,7 @@ pub fn impl_py_method_def_new(spec: &FnSpec, wrapper: &TokenStream) -> TokenStre
|
|||
pyo3::class::PyMethodDefType::New({
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#python_name),
|
||||
ml_meth: pyo3::class::PyMethodType::PyNewFunc(__wrap),
|
||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS,
|
||||
ml_doc: #doc,
|
||||
}
|
||||
pyo3::class::PyMethodDef::new_func(concat!(stringify!(#python_name), "\0"), __wrap, #doc)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -618,13 +612,12 @@ pub fn impl_py_method_def_class(spec: &FnSpec, wrapper: &TokenStream) -> TokenSt
|
|||
pyo3::class::PyMethodDefType::Class({
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#python_name),
|
||||
ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap),
|
||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS |
|
||||
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
||||
concat!(stringify!(#python_name), "\0"),
|
||||
__wrap,
|
||||
pyo3::ffi::METH_CLASS,
|
||||
ml_doc: #doc,
|
||||
}
|
||||
#doc
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -636,12 +629,12 @@ pub fn impl_py_method_def_static(spec: &FnSpec, wrapper: &TokenStream) -> TokenS
|
|||
pyo3::class::PyMethodDefType::Static({
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#python_name),
|
||||
ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap),
|
||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS | pyo3::ffi::METH_STATIC,
|
||||
ml_doc: #doc,
|
||||
}
|
||||
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
||||
concat!(stringify!(#python_name), "\0"),
|
||||
__wrap,
|
||||
pyo3::ffi::METH_STATIC,
|
||||
#doc
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -652,10 +645,7 @@ pub fn impl_py_method_class_attribute(spec: &FnSpec<'_>, wrapper: &TokenStream)
|
|||
pyo3::class::PyMethodDefType::ClassAttribute({
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PyClassAttributeDef {
|
||||
name: stringify!(#python_name),
|
||||
meth: __wrap,
|
||||
}
|
||||
pyo3::class::PyClassAttributeDef::new(concat!(stringify!(#python_name), "\0"), __wrap)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -666,10 +656,7 @@ pub fn impl_py_const_class_attribute(spec: &ConstSpec, wrapper: &TokenStream) ->
|
|||
pyo3::class::PyMethodDefType::ClassAttribute({
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PyClassAttributeDef {
|
||||
name: stringify!(#python_name),
|
||||
meth: __wrap,
|
||||
}
|
||||
pyo3::class::PyClassAttributeDef::new(concat!(stringify!(#python_name), "\0"), __wrap)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -681,12 +668,12 @@ pub fn impl_py_method_def_call(spec: &FnSpec, wrapper: &TokenStream) -> TokenStr
|
|||
pyo3::class::PyMethodDefType::Call({
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#python_name),
|
||||
ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap),
|
||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS,
|
||||
ml_doc: #doc,
|
||||
}
|
||||
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
||||
concat!(stringify!(#python_name), "\0"),
|
||||
__wrap,
|
||||
pyo3::ffi::METH_STATIC,
|
||||
#doc
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -700,11 +687,7 @@ pub(crate) fn impl_py_setter_def(
|
|||
pyo3::class::PyMethodDefType::Setter({
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PySetterDef {
|
||||
name: stringify!(#python_name),
|
||||
meth: __wrap,
|
||||
doc: #doc,
|
||||
}
|
||||
pyo3::class::PySetterDef::new(concat!(stringify!(#python_name), "\0"), __wrap, #doc)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -718,11 +701,7 @@ pub(crate) fn impl_py_getter_def(
|
|||
pyo3::class::PyMethodDefType::Getter({
|
||||
#wrapper
|
||||
|
||||
pyo3::class::PyGetterDef {
|
||||
name: stringify!(#python_name),
|
||||
meth: __wrap,
|
||||
doc: #doc,
|
||||
}
|
||||
pyo3::class::PyGetterDef::new(concat!(stringify!(#python_name), "\0"), __wrap, #doc)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,12 +95,12 @@ fn impl_proto_impl(
|
|||
py_methods.push(quote! {
|
||||
pyo3::class::PyMethodDefType::Method({
|
||||
#method
|
||||
pyo3::class::PyMethodDef {
|
||||
ml_name: stringify!(#name),
|
||||
ml_meth: pyo3::class::PyMethodType::PyCFunctionWithKeywords(__wrap),
|
||||
ml_flags: pyo3::ffi::METH_VARARGS | pyo3::ffi::METH_KEYWORDS | #coexist,
|
||||
ml_doc: ""
|
||||
}
|
||||
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
||||
concat!(stringify!(#name), "\0"),
|
||||
__wrap,
|
||||
#coexist,
|
||||
"\0"
|
||||
)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ fn inventory_submission(py_methods: Vec<TokenStream>, ty: &syn::Type) -> TokenSt
|
|||
pyo3::inventory::submit! {
|
||||
#![crate = pyo3] {
|
||||
type Inventory = <#ty as pyo3::class::methods::HasMethodsInventory>::Methods;
|
||||
<Inventory as pyo3::class::methods::PyMethodsInventory>::new(&[#(#py_methods),*])
|
||||
<Inventory as pyo3::class::methods::PyMethodsInventory>::new(vec![#(#py_methods),*])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -132,18 +132,12 @@ fn standard_element_type_from_type_char(type_char: u8) -> ElementType {
|
|||
|
||||
#[cfg(target_endian = "little")]
|
||||
fn is_matching_endian(c: u8) -> bool {
|
||||
match c {
|
||||
b'@' | b'=' | b'<' => true,
|
||||
_ => false,
|
||||
}
|
||||
c == b'@' || c == b'=' || c == b'>'
|
||||
}
|
||||
|
||||
#[cfg(target_endian = "big")]
|
||||
fn is_matching_endian(c: u8) -> bool {
|
||||
match c {
|
||||
b'@' | b'=' | b'>' | b'!' => true,
|
||||
_ => false,
|
||||
}
|
||||
c == b'@' || c == b'=' || c == b'>' || c == b'!'
|
||||
}
|
||||
|
||||
/// Trait implemented for possible element types of `PyBuffer`.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use crate::{ffi, PyObject, Python};
|
||||
use libc::c_int;
|
||||
use std::ffi::CString;
|
||||
use std::ffi::CStr;
|
||||
use std::fmt;
|
||||
|
||||
/// `PyMethodDefType` represents different types of Python callable objects.
|
||||
|
@ -35,32 +35,32 @@ pub enum PyMethodType {
|
|||
PyInitFunc(ffi::initproc),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PyMethodDef {
|
||||
pub ml_name: &'static str,
|
||||
pub ml_meth: PyMethodType,
|
||||
pub ml_flags: c_int,
|
||||
pub ml_doc: &'static str,
|
||||
pub(crate) ml_name: &'static CStr,
|
||||
pub(crate) ml_meth: PyMethodType,
|
||||
pub(crate) ml_flags: c_int,
|
||||
pub(crate) ml_doc: &'static CStr,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct PyClassAttributeDef {
|
||||
pub name: &'static str,
|
||||
pub meth: for<'p> fn(Python<'p>) -> PyObject,
|
||||
pub(crate) name: &'static CStr,
|
||||
pub(crate) meth: for<'p> fn(Python<'p>) -> PyObject,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PyGetterDef {
|
||||
pub name: &'static str,
|
||||
pub meth: ffi::getter,
|
||||
pub doc: &'static str,
|
||||
pub(crate) name: &'static CStr,
|
||||
pub(crate) meth: ffi::getter,
|
||||
doc: &'static CStr,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PySetterDef {
|
||||
pub name: &'static str,
|
||||
pub meth: ffi::setter,
|
||||
pub doc: &'static str,
|
||||
pub(crate) name: &'static CStr,
|
||||
pub(crate) meth: ffi::setter,
|
||||
doc: &'static CStr,
|
||||
}
|
||||
|
||||
unsafe impl Sync for PyMethodDef {}
|
||||
|
@ -73,7 +73,67 @@ unsafe impl Sync for PySetterDef {}
|
|||
|
||||
unsafe impl Sync for ffi::PyGetSetDef {}
|
||||
|
||||
fn get_name(name: &str) -> &CStr {
|
||||
CStr::from_bytes_with_nul(name.as_bytes())
|
||||
.expect("Method name must be terminated with NULL byte")
|
||||
}
|
||||
|
||||
fn get_doc(doc: &str) -> &CStr {
|
||||
CStr::from_bytes_with_nul(doc.as_bytes()).expect("Document must be terminated with NULL byte")
|
||||
}
|
||||
|
||||
impl PyMethodDef {
|
||||
pub(crate) fn get_new_func(&self) -> Option<ffi::newfunc> {
|
||||
if let PyMethodType::PyNewFunc(new_func) = self.ml_meth {
|
||||
Some(new_func)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_cfunction_with_keywords(&self) -> Option<ffi::PyCFunctionWithKeywords> {
|
||||
if let PyMethodType::PyCFunctionWithKeywords(func) = self.ml_meth {
|
||||
Some(func)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Define a function with no `*args` and `**kwargs`.
|
||||
pub fn cfunction(name: &'static str, cfunction: ffi::PyCFunction, doc: &'static str) -> Self {
|
||||
Self {
|
||||
ml_name: get_name(name),
|
||||
ml_meth: PyMethodType::PyCFunction(cfunction),
|
||||
ml_flags: ffi::METH_NOARGS,
|
||||
ml_doc: get_doc(doc),
|
||||
}
|
||||
}
|
||||
|
||||
/// Define a `__new__` function.
|
||||
pub fn new_func(name: &'static str, newfunc: ffi::newfunc, doc: &'static str) -> Self {
|
||||
Self {
|
||||
ml_name: get_name(name),
|
||||
ml_meth: PyMethodType::PyNewFunc(newfunc),
|
||||
ml_flags: ffi::METH_VARARGS | ffi::METH_KEYWORDS,
|
||||
ml_doc: get_doc(doc),
|
||||
}
|
||||
}
|
||||
|
||||
/// Define a function that can take `*args` and `**kwargs`.
|
||||
pub fn cfunction_with_keywords(
|
||||
name: &'static str,
|
||||
cfunction: ffi::PyCFunctionWithKeywords,
|
||||
flags: c_int,
|
||||
doc: &'static str,
|
||||
) -> Self {
|
||||
Self {
|
||||
ml_name: get_name(name),
|
||||
ml_meth: PyMethodType::PyCFunctionWithKeywords(cfunction),
|
||||
ml_flags: flags | ffi::METH_VARARGS | ffi::METH_KEYWORDS,
|
||||
ml_doc: get_doc(doc),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert `PyMethodDef` to Python method definition struct `ffi::PyMethodDef`
|
||||
pub fn as_method_def(&self) -> ffi::PyMethodDef {
|
||||
let meth = match self.ml_meth {
|
||||
|
@ -84,12 +144,20 @@ impl PyMethodDef {
|
|||
};
|
||||
|
||||
ffi::PyMethodDef {
|
||||
ml_name: CString::new(self.ml_name)
|
||||
.expect("Method name must not contain NULL byte")
|
||||
.into_raw(),
|
||||
ml_name: self.ml_name.as_ptr(),
|
||||
ml_meth: Some(meth),
|
||||
ml_flags: self.ml_flags,
|
||||
ml_doc: self.ml_doc.as_ptr() as *const _,
|
||||
ml_doc: self.ml_doc.as_ptr(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PyClassAttributeDef {
|
||||
/// Define a class attribute.
|
||||
pub fn new(name: &'static str, meth: for<'p> fn(Python<'p>) -> PyObject) -> Self {
|
||||
Self {
|
||||
name: get_name(name),
|
||||
meth,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,30 +173,44 @@ impl fmt::Debug for PyClassAttributeDef {
|
|||
}
|
||||
|
||||
impl PyGetterDef {
|
||||
/// Define a getter.
|
||||
pub fn new(name: &'static str, getter: ffi::getter, doc: &'static str) -> Self {
|
||||
Self {
|
||||
name: get_name(name),
|
||||
meth: getter,
|
||||
doc: get_doc(doc),
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy descriptor information to `ffi::PyGetSetDef`
|
||||
pub fn copy_to(&self, dst: &mut ffi::PyGetSetDef) {
|
||||
if dst.name.is_null() {
|
||||
dst.name = CString::new(self.name)
|
||||
.expect("Method name must not contain NULL byte")
|
||||
.into_raw();
|
||||
dst.name = self.name.as_ptr() as _;
|
||||
}
|
||||
if dst.doc.is_null() {
|
||||
dst.doc = self.doc.as_ptr() as *mut libc::c_char;
|
||||
dst.doc = self.doc.as_ptr() as _;
|
||||
}
|
||||
dst.get = Some(self.meth);
|
||||
}
|
||||
}
|
||||
|
||||
impl PySetterDef {
|
||||
/// Define a setter.
|
||||
pub fn new(name: &'static str, setter: ffi::setter, doc: &'static str) -> Self {
|
||||
Self {
|
||||
name: get_name(name),
|
||||
meth: setter,
|
||||
doc: get_doc(doc),
|
||||
}
|
||||
}
|
||||
|
||||
/// Copy descriptor information to `ffi::PyGetSetDef`
|
||||
pub fn copy_to(&self, dst: &mut ffi::PyGetSetDef) {
|
||||
if dst.name.is_null() {
|
||||
dst.name = CString::new(self.name)
|
||||
.expect("Method name must not contain NULL byte")
|
||||
.into_raw();
|
||||
dst.name = self.name.as_ptr() as _;
|
||||
}
|
||||
if dst.doc.is_null() {
|
||||
dst.doc = self.doc.as_ptr() as *mut libc::c_char;
|
||||
dst.doc = self.doc.as_ptr() as _;
|
||||
}
|
||||
dst.set = Some(self.meth);
|
||||
}
|
||||
|
@ -150,10 +232,10 @@ pub trait PyMethods {
|
|||
#[cfg(feature = "macros")]
|
||||
pub trait PyMethodsInventory: inventory::Collect {
|
||||
/// Create a new instance
|
||||
fn new(methods: &'static [PyMethodDefType]) -> Self;
|
||||
fn new(methods: Vec<PyMethodDefType>) -> Self;
|
||||
|
||||
/// Returns the methods for a single `#[pymethods] impl` block
|
||||
fn get(&self) -> &'static [PyMethodDefType];
|
||||
fn get(&'static self) -> &'static [PyMethodDefType];
|
||||
}
|
||||
|
||||
/// Implemented for `#[pyclass]` in our proc macro code.
|
||||
|
|
|
@ -13,6 +13,7 @@ pub mod descr;
|
|||
pub mod gc;
|
||||
pub mod iter;
|
||||
pub mod mapping;
|
||||
#[doc(hidden)]
|
||||
pub mod methods;
|
||||
pub mod number;
|
||||
pub mod proto_methods;
|
||||
|
@ -27,6 +28,7 @@ pub use self::descr::PyDescrProtocol;
|
|||
pub use self::gc::{PyGCProtocol, PyTraverseError, PyVisit};
|
||||
pub use self::iter::PyIterProtocol;
|
||||
pub use self::mapping::PyMappingProtocol;
|
||||
#[doc(hidden)]
|
||||
pub use self::methods::{
|
||||
PyClassAttributeDef, PyGetterDef, PyMethodDef, PyMethodDefType, PyMethodType, PySetterDef,
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::derive_utils::PyBaseTypeUtils;
|
|||
use crate::pyclass_slots::{PyClassDict, PyClassWeakRef};
|
||||
use crate::type_object::{type_flags, PyLayout};
|
||||
use crate::types::PyAny;
|
||||
use crate::{class, ffi, PyCell, PyErr, PyNativeType, PyResult, PyTypeInfo, Python};
|
||||
use crate::{ffi, PyCell, PyErr, PyNativeType, PyResult, PyTypeInfo, Python};
|
||||
use std::ffi::CString;
|
||||
use std::marker::PhantomData;
|
||||
use std::os::raw::c_void;
|
||||
|
@ -239,7 +239,7 @@ fn py_class_flags<T: PyTypeInfo>(type_object: &mut ffi::PyTypeObject) {
|
|||
|
||||
pub(crate) fn py_class_attributes<T: PyMethods>() -> impl Iterator<Item = PyClassAttributeDef> {
|
||||
T::py_methods().into_iter().filter_map(|def| match def {
|
||||
PyMethodDefType::ClassAttribute(attr) => Some(*attr),
|
||||
PyMethodDefType::ClassAttribute(attr) => Some(attr.to_owned()),
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
|
@ -256,16 +256,12 @@ fn py_class_method_defs<T: PyMethods>() -> (
|
|||
for def in T::py_methods() {
|
||||
match *def {
|
||||
PyMethodDefType::New(ref def) => {
|
||||
if let class::methods::PyMethodType::PyNewFunc(meth) = def.ml_meth {
|
||||
new = Some(meth)
|
||||
}
|
||||
new = def.get_new_func();
|
||||
debug_assert!(new.is_some());
|
||||
}
|
||||
PyMethodDefType::Call(ref def) => {
|
||||
if let class::methods::PyMethodType::PyCFunctionWithKeywords(meth) = def.ml_meth {
|
||||
call = Some(meth)
|
||||
} else {
|
||||
panic!("Method type is not supoorted by tp_call slot")
|
||||
}
|
||||
call = def.get_cfunction_with_keywords();
|
||||
debug_assert!(call.is_some());
|
||||
}
|
||||
PyMethodDefType::Method(ref def)
|
||||
| PyMethodDefType::Class(ref def)
|
||||
|
@ -285,19 +281,17 @@ fn py_class_properties<T: PyMethods>() -> Vec<ffi::PyGetSetDef> {
|
|||
for def in T::py_methods() {
|
||||
match *def {
|
||||
PyMethodDefType::Getter(ref getter) => {
|
||||
let name = getter.name.to_string();
|
||||
if !defs.contains_key(&name) {
|
||||
let _ = defs.insert(name.clone(), ffi::PyGetSetDef_INIT);
|
||||
if !defs.contains_key(getter.name) {
|
||||
let _ = defs.insert(getter.name.to_owned(), ffi::PyGetSetDef_INIT);
|
||||
}
|
||||
let def = defs.get_mut(&name).expect("Failed to call get_mut");
|
||||
let def = defs.get_mut(getter.name).expect("Failed to call get_mut");
|
||||
getter.copy_to(def);
|
||||
}
|
||||
PyMethodDefType::Setter(ref setter) => {
|
||||
let name = setter.name.to_string();
|
||||
if !defs.contains_key(&name) {
|
||||
let _ = defs.insert(name.clone(), ffi::PyGetSetDef_INIT);
|
||||
if !defs.contains_key(setter.name) {
|
||||
let _ = defs.insert(setter.name.to_owned(), ffi::PyGetSetDef_INIT);
|
||||
}
|
||||
let def = defs.get_mut(&name).expect("Failed to call get_mut");
|
||||
let def = defs.get_mut(setter.name).expect("Failed to call get_mut");
|
||||
setter.copy_to(def);
|
||||
}
|
||||
_ => (),
|
||||
|
|
|
@ -227,18 +227,15 @@ impl LazyStaticType {
|
|||
fn initialize_tp_dict(
|
||||
py: Python,
|
||||
tp_dict: *mut ffi::PyObject,
|
||||
items: Vec<(&'static str, PyObject)>,
|
||||
items: Vec<(&'static std::ffi::CStr, PyObject)>,
|
||||
) -> PyResult<()> {
|
||||
// We hold the GIL: the dictionary update can be considered atomic from
|
||||
// the POV of other threads.
|
||||
for (key, val) in items {
|
||||
crate::types::with_tmp_string(py, key, |key| {
|
||||
let ret = unsafe { ffi::PyDict_SetItem(tp_dict, key, val.into_ptr()) };
|
||||
if ret < 0 {
|
||||
return Err(PyErr::fetch(py));
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
let ret = unsafe { ffi::PyDict_SetItemString(tp_dict, key.as_ptr(), val.into_ptr()) };
|
||||
if ret < 0 {
|
||||
return Err(PyErr::fetch(py));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::ffi::{CStr, CString};
|
|||
use crate::derive_utils::PyFunctionArguments;
|
||||
use crate::exceptions::PyValueError;
|
||||
use crate::prelude::*;
|
||||
use crate::{class, ffi, AsPyPointer, PyMethodType};
|
||||
use crate::{ffi, AsPyPointer, PyMethodDef, PyMethodType};
|
||||
|
||||
/// Represents a builtin Python function object.
|
||||
#[repr(transparent)]
|
||||
|
@ -11,6 +11,18 @@ pub struct PyCFunction(PyAny);
|
|||
|
||||
pyobject_native_var_type!(PyCFunction, ffi::PyCFunction_Type, ffi::PyCFunction_Check);
|
||||
|
||||
fn get_name(name: &str) -> PyResult<&'static CStr> {
|
||||
let cstr = CString::new(name)
|
||||
.map_err(|_| PyValueError::new_err("Function name cannot contain contain NULL byte."))?;
|
||||
Ok(Box::leak(cstr.into_boxed_c_str()))
|
||||
}
|
||||
|
||||
fn get_doc(doc: &str) -> PyResult<&'static CStr> {
|
||||
let cstr = CString::new(doc)
|
||||
.map_err(|_| PyValueError::new_err("Document cannot contain contain NULL byte."))?;
|
||||
Ok(Box::leak(cstr.into_boxed_c_str()))
|
||||
}
|
||||
|
||||
impl PyCFunction {
|
||||
/// Create a new built-in function with keywords.
|
||||
///
|
||||
|
@ -18,55 +30,50 @@ impl PyCFunction {
|
|||
pub fn new_with_keywords<'a>(
|
||||
fun: ffi::PyCFunctionWithKeywords,
|
||||
name: &str,
|
||||
doc: &'static str,
|
||||
doc: &str,
|
||||
py_or_module: PyFunctionArguments<'a>,
|
||||
) -> PyResult<&'a PyCFunction> {
|
||||
let fun = PyMethodType::PyCFunctionWithKeywords(fun);
|
||||
Self::new_(fun, name, doc, py_or_module)
|
||||
) -> PyResult<&'a Self> {
|
||||
Self::internal_new(
|
||||
get_name(name)?,
|
||||
get_doc(doc)?,
|
||||
PyMethodType::PyCFunctionWithKeywords(fun),
|
||||
ffi::METH_VARARGS | ffi::METH_KEYWORDS,
|
||||
py_or_module,
|
||||
)
|
||||
}
|
||||
|
||||
/// Create a new built-in function without keywords.
|
||||
pub fn new<'a>(
|
||||
fun: ffi::PyCFunction,
|
||||
name: &str,
|
||||
doc: &'static str,
|
||||
doc: &str,
|
||||
py_or_module: PyFunctionArguments<'a>,
|
||||
) -> PyResult<&'a PyCFunction> {
|
||||
let fun = PyMethodType::PyCFunction(fun);
|
||||
Self::new_(fun, name, doc, py_or_module)
|
||||
) -> PyResult<&'a Self> {
|
||||
Self::internal_new(
|
||||
get_name(name)?,
|
||||
get_doc(doc)?,
|
||||
PyMethodType::PyCFunction(fun),
|
||||
ffi::METH_NOARGS,
|
||||
py_or_module,
|
||||
)
|
||||
}
|
||||
|
||||
fn new_<'a>(
|
||||
fun: class::PyMethodType,
|
||||
name: &str,
|
||||
doc: &'static str,
|
||||
#[doc(hidden)]
|
||||
pub fn internal_new<'a>(
|
||||
name: &'static CStr,
|
||||
doc: &'static CStr,
|
||||
method_type: PyMethodType,
|
||||
flags: std::os::raw::c_int,
|
||||
py_or_module: PyFunctionArguments<'a>,
|
||||
) -> PyResult<&'a PyCFunction> {
|
||||
) -> PyResult<&'a Self> {
|
||||
let (py, module) = py_or_module.into_py_and_maybe_module();
|
||||
let doc: &'static CStr = CStr::from_bytes_with_nul(doc.as_bytes())
|
||||
.map_err(|_| PyValueError::new_err("docstring must end with NULL byte."))?;
|
||||
let name = CString::new(name.as_bytes()).map_err(|_| {
|
||||
PyValueError::new_err("Function name cannot contain contain NULL byte.")
|
||||
})?;
|
||||
let def = match fun {
|
||||
PyMethodType::PyCFunction(fun) => ffi::PyMethodDef {
|
||||
ml_name: name.into_raw() as _,
|
||||
ml_meth: Some(fun),
|
||||
ml_flags: ffi::METH_VARARGS,
|
||||
ml_doc: doc.as_ptr() as _,
|
||||
},
|
||||
PyMethodType::PyCFunctionWithKeywords(fun) => ffi::PyMethodDef {
|
||||
ml_name: name.into_raw() as _,
|
||||
ml_meth: Some(unsafe { std::mem::transmute(fun) }),
|
||||
ml_flags: ffi::METH_VARARGS | ffi::METH_KEYWORDS,
|
||||
ml_doc: doc.as_ptr() as _,
|
||||
},
|
||||
_ => {
|
||||
return Err(PyValueError::new_err(
|
||||
"Only PyCFunction and PyCFunctionWithKeywords are valid.",
|
||||
))
|
||||
}
|
||||
let method_def = PyMethodDef {
|
||||
ml_name: name,
|
||||
ml_meth: method_type,
|
||||
ml_flags: flags,
|
||||
ml_doc: doc,
|
||||
};
|
||||
let def = method_def.as_method_def();
|
||||
let (mod_ptr, module_name) = if let Some(m) = module {
|
||||
let mod_ptr = m.as_ptr();
|
||||
let name = m.name()?.into_py(py);
|
||||
|
|
|
@ -100,7 +100,7 @@ fn test_raw_function() {
|
|||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let raw_func = raw_pycfunction!(optional_bool);
|
||||
let fun = PyCFunction::new_with_keywords(raw_func, "fun", "\0", py.into()).unwrap();
|
||||
let fun = PyCFunction::new_with_keywords(raw_func, "fun", "", py.into()).unwrap();
|
||||
let res = fun.call((), None).unwrap().extract::<&str>().unwrap();
|
||||
assert_eq!(res, "Some(true)");
|
||||
let res = fun.call((false,), None).unwrap().extract::<&str>().unwrap();
|
||||
|
|
Loading…
Reference in a new issue