macros-backend: support noargs for pyproto py_methods
This commit is contained in:
parent
abe19e2ecc
commit
78080ebbd2
|
@ -41,6 +41,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
||||||
- Fix FFI definition `PyIndex_Check` missing with the `abi3` feature. [#1436](https://github.com/PyO3/pyo3/pull/1436)
|
- Fix FFI definition `PyIndex_Check` missing with the `abi3` feature. [#1436](https://github.com/PyO3/pyo3/pull/1436)
|
||||||
- Fix incorrect `TypeError` raised when keyword-only argument passed along with a positional argument in `*args`. [#1440](https://github.com/PyO3/pyo3/pull/1440)
|
- Fix incorrect `TypeError` raised when keyword-only argument passed along with a positional argument in `*args`. [#1440](https://github.com/PyO3/pyo3/pull/1440)
|
||||||
- Fix inability to use a named lifetime for `&PyTuple` of `*args` in `#[pyfunction]`. [#1440](https://github.com/PyO3/pyo3/pull/1440)
|
- Fix inability to use a named lifetime for `&PyTuple` of `*args` in `#[pyfunction]`. [#1440](https://github.com/PyO3/pyo3/pull/1440)
|
||||||
|
- Fix inability to add `#[text_signature]` to some `#[pyproto]` methods. [#1483](https://github.com/PyO3/pyo3/pull/1483)
|
||||||
|
|
||||||
## [0.13.2] - 2021-02-12
|
## [0.13.2] - 2021-02-12
|
||||||
### Packaging
|
### Packaging
|
||||||
|
|
|
@ -208,7 +208,6 @@ pub fn add_fn_to_module(
|
||||||
pyo3::class::methods::PyMethodDef::cfunction_with_keywords(
|
pyo3::class::methods::PyMethodDef::cfunction_with_keywords(
|
||||||
name,
|
name,
|
||||||
pyo3::class::methods::PyCFunctionWithKeywords(#wrapper_ident),
|
pyo3::class::methods::PyCFunctionWithKeywords(#wrapper_ident),
|
||||||
0,
|
|
||||||
#doc,
|
#doc,
|
||||||
),
|
),
|
||||||
args.into(),
|
args.into(),
|
||||||
|
|
|
@ -27,7 +27,9 @@ pub fn gen_py_method(
|
||||||
let spec = FnSpec::parse(sig, &mut *meth_attrs, true)?;
|
let spec = FnSpec::parse(sig, &mut *meth_attrs, true)?;
|
||||||
|
|
||||||
Ok(match &spec.tp {
|
Ok(match &spec.tp {
|
||||||
FnType::Fn(self_ty) => GeneratedPyMethod::Method(impl_py_method_def(cls, &spec, self_ty)?),
|
FnType::Fn(self_ty) => {
|
||||||
|
GeneratedPyMethod::Method(impl_py_method_def(cls, &spec, self_ty, None)?)
|
||||||
|
}
|
||||||
FnType::FnNew => GeneratedPyMethod::New(impl_py_method_def_new(cls, &spec)?),
|
FnType::FnNew => GeneratedPyMethod::New(impl_py_method_def_new(cls, &spec)?),
|
||||||
FnType::FnCall(self_ty) => {
|
FnType::FnCall(self_ty) => {
|
||||||
GeneratedPyMethod::Call(impl_py_method_def_call(cls, &spec, self_ty)?)
|
GeneratedPyMethod::Call(impl_py_method_def_call(cls, &spec, self_ty)?)
|
||||||
|
@ -83,72 +85,24 @@ pub fn gen_py_const(
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate function wrapper (PyCFunction, PyCFunctionWithKeywords)
|
/// Generate function wrapper for PyCFunctionWithKeywords
|
||||||
pub fn impl_wrap(
|
pub fn impl_wrap_cfunction_with_keywords(
|
||||||
cls: &syn::Type,
|
cls: &syn::Type,
|
||||||
spec: &FnSpec<'_>,
|
spec: &FnSpec<'_>,
|
||||||
self_ty: &SelfType,
|
self_ty: &SelfType,
|
||||||
noargs: bool,
|
|
||||||
) -> Result<TokenStream> {
|
) -> Result<TokenStream> {
|
||||||
let body = impl_call(cls, &spec);
|
let body = impl_call(cls, &spec);
|
||||||
let slf = self_ty.receiver(cls);
|
let slf = self_ty.receiver(cls);
|
||||||
let python_name = &spec.python_name;
|
let python_name = &spec.python_name;
|
||||||
if spec.args.is_empty() && noargs {
|
let body = impl_arg_params(&spec, Some(cls), body)?;
|
||||||
Ok(quote! {
|
|
||||||
unsafe extern "C" fn __wrap(
|
|
||||||
_slf: *mut pyo3::ffi::PyObject,
|
|
||||||
_args: *mut pyo3::ffi::PyObject,
|
|
||||||
) -> *mut pyo3::ffi::PyObject
|
|
||||||
{
|
|
||||||
const _LOCATION: &'static str = concat!(
|
|
||||||
stringify!(#cls), ".", stringify!(#python_name), "()");
|
|
||||||
pyo3::callback::handle_panic(|_py| {
|
|
||||||
#slf
|
|
||||||
#body
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
let body = impl_arg_params(&spec, Some(cls), body)?;
|
|
||||||
Ok(quote! {
|
|
||||||
unsafe extern "C" fn __wrap(
|
|
||||||
_slf: *mut pyo3::ffi::PyObject,
|
|
||||||
_args: *mut pyo3::ffi::PyObject,
|
|
||||||
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
|
|
||||||
{
|
|
||||||
const _LOCATION: &'static str = concat!(
|
|
||||||
stringify!(#cls), ".", stringify!(#python_name), "()");
|
|
||||||
pyo3::callback::handle_panic(|_py| {
|
|
||||||
#slf
|
|
||||||
let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
|
|
||||||
let _kwargs: Option<&pyo3::types::PyDict> = _py.from_borrowed_ptr_or_opt(_kwargs);
|
|
||||||
|
|
||||||
#body
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Generate function wrapper for protocol method (PyCFunction, PyCFunctionWithKeywords)
|
|
||||||
pub fn impl_proto_wrap(
|
|
||||||
cls: &syn::Type,
|
|
||||||
spec: &FnSpec<'_>,
|
|
||||||
self_ty: &SelfType,
|
|
||||||
) -> Result<TokenStream> {
|
|
||||||
let python_name = &spec.python_name;
|
|
||||||
let cb = impl_call(cls, &spec);
|
|
||||||
let body = impl_arg_params(&spec, Some(cls), cb)?;
|
|
||||||
let slf = self_ty.receiver(cls);
|
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
#[allow(unused_mut)]
|
|
||||||
unsafe extern "C" fn __wrap(
|
unsafe extern "C" fn __wrap(
|
||||||
_slf: *mut pyo3::ffi::PyObject,
|
_slf: *mut pyo3::ffi::PyObject,
|
||||||
_args: *mut pyo3::ffi::PyObject,
|
_args: *mut pyo3::ffi::PyObject,
|
||||||
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
|
_kwargs: *mut pyo3::ffi::PyObject) -> *mut pyo3::ffi::PyObject
|
||||||
{
|
{
|
||||||
const _LOCATION: &'static str = concat!(stringify!(#cls),".",stringify!(#python_name),"()");
|
const _LOCATION: &'static str = concat!(
|
||||||
|
stringify!(#cls), ".", stringify!(#python_name), "()");
|
||||||
pyo3::callback::handle_panic(|_py| {
|
pyo3::callback::handle_panic(|_py| {
|
||||||
#slf
|
#slf
|
||||||
let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
|
let _args = _py.from_borrowed_ptr::<pyo3::types::PyTuple>(_args);
|
||||||
|
@ -160,6 +114,28 @@ pub fn impl_proto_wrap(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generate function wrapper PyCFunction
|
||||||
|
pub fn impl_wrap_noargs(cls: &syn::Type, spec: &FnSpec<'_>, self_ty: &SelfType) -> TokenStream {
|
||||||
|
let body = impl_call(cls, &spec);
|
||||||
|
let slf = self_ty.receiver(cls);
|
||||||
|
let python_name = &spec.python_name;
|
||||||
|
assert!(spec.args.is_empty());
|
||||||
|
quote! {
|
||||||
|
unsafe extern "C" fn __wrap(
|
||||||
|
_slf: *mut pyo3::ffi::PyObject,
|
||||||
|
_args: *mut pyo3::ffi::PyObject,
|
||||||
|
) -> *mut pyo3::ffi::PyObject
|
||||||
|
{
|
||||||
|
const _LOCATION: &'static str = concat!(
|
||||||
|
stringify!(#cls), ".", stringify!(#python_name), "()");
|
||||||
|
pyo3::callback::handle_panic(|_py| {
|
||||||
|
#slf
|
||||||
|
#body
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate class method wrapper (PyCFunction, PyCFunctionWithKeywords)
|
/// Generate class method wrapper (PyCFunction, PyCFunctionWithKeywords)
|
||||||
pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<TokenStream> {
|
pub fn impl_wrap_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<TokenStream> {
|
||||||
let name = &spec.name;
|
let name = &spec.name;
|
||||||
|
@ -591,23 +567,28 @@ pub fn impl_py_method_def(
|
||||||
cls: &syn::Type,
|
cls: &syn::Type,
|
||||||
spec: &FnSpec,
|
spec: &FnSpec,
|
||||||
self_ty: &SelfType,
|
self_ty: &SelfType,
|
||||||
|
flags: Option<TokenStream>,
|
||||||
) -> Result<TokenStream> {
|
) -> Result<TokenStream> {
|
||||||
let wrapper = impl_wrap(cls, &spec, self_ty, true)?;
|
let add_flags = flags.map(|flags| quote!(.flags(#flags)));
|
||||||
let python_name = &spec.python_name;
|
let python_name = &spec.python_name;
|
||||||
let doc = &spec.doc;
|
let doc = &spec.doc;
|
||||||
if spec.args.is_empty() {
|
if spec.args.is_empty() {
|
||||||
|
let wrapper = impl_wrap_noargs(cls, spec, self_ty);
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
pyo3::class::PyMethodDefType::Method({
|
pyo3::class::PyMethodDefType::Method({
|
||||||
#wrapper
|
#wrapper
|
||||||
|
|
||||||
pyo3::class::PyMethodDef::cfunction(
|
pyo3::class::PyMethodDef::noargs(
|
||||||
concat!(stringify!(#python_name), "\0"),
|
concat!(stringify!(#python_name), "\0"),
|
||||||
pyo3::class::methods::PyCFunction(__wrap),
|
pyo3::class::methods::PyCFunction(__wrap),
|
||||||
#doc
|
#doc
|
||||||
)
|
)
|
||||||
|
#add_flags
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
let wrapper = impl_wrap_cfunction_with_keywords(cls, &spec, self_ty)?;
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
pyo3::class::PyMethodDefType::Method({
|
pyo3::class::PyMethodDefType::Method({
|
||||||
#wrapper
|
#wrapper
|
||||||
|
@ -615,9 +596,9 @@ pub fn impl_py_method_def(
|
||||||
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
||||||
concat!(stringify!(#python_name), "\0"),
|
concat!(stringify!(#python_name), "\0"),
|
||||||
pyo3::class::methods::PyCFunctionWithKeywords(__wrap),
|
pyo3::class::methods::PyCFunctionWithKeywords(__wrap),
|
||||||
0,
|
|
||||||
#doc
|
#doc
|
||||||
)
|
)
|
||||||
|
#add_flags
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -647,9 +628,8 @@ pub fn impl_py_method_def_class(cls: &syn::Type, spec: &FnSpec) -> Result<TokenS
|
||||||
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
||||||
concat!(stringify!(#python_name), "\0"),
|
concat!(stringify!(#python_name), "\0"),
|
||||||
pyo3::class::methods::PyCFunctionWithKeywords(__wrap),
|
pyo3::class::methods::PyCFunctionWithKeywords(__wrap),
|
||||||
pyo3::ffi::METH_CLASS,
|
|
||||||
#doc
|
#doc
|
||||||
)
|
).flags(pyo3::ffi::METH_CLASS)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -665,9 +645,8 @@ pub fn impl_py_method_def_static(cls: &syn::Type, spec: &FnSpec) -> Result<Token
|
||||||
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
||||||
concat!(stringify!(#python_name), "\0"),
|
concat!(stringify!(#python_name), "\0"),
|
||||||
pyo3::class::methods::PyCFunctionWithKeywords(__wrap),
|
pyo3::class::methods::PyCFunctionWithKeywords(__wrap),
|
||||||
pyo3::ffi::METH_STATIC,
|
|
||||||
#doc
|
#doc
|
||||||
)
|
).flags(pyo3::ffi::METH_STATIC)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -706,7 +685,7 @@ pub fn impl_py_method_def_call(
|
||||||
spec: &FnSpec,
|
spec: &FnSpec,
|
||||||
self_ty: &SelfType,
|
self_ty: &SelfType,
|
||||||
) -> Result<TokenStream> {
|
) -> Result<TokenStream> {
|
||||||
let wrapper = impl_wrap(cls, &spec, self_ty, false)?;
|
let wrapper = impl_wrap_cfunction_with_keywords(cls, &spec, self_ty)?;
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
impl pyo3::class::impl_::PyClassCallImpl<#cls> for pyo3::class::impl_::PyClassImplCollector<#cls> {
|
impl pyo3::class::impl_::PyClassCallImpl<#cls> for pyo3::class::impl_::PyClassImplCollector<#cls> {
|
||||||
fn call_impl(self) -> Option<pyo3::ffi::PyCFunctionWithKeywords> {
|
fn call_impl(self) -> Option<pyo3::ffi::PyCFunctionWithKeywords> {
|
||||||
|
|
|
@ -64,33 +64,22 @@ fn impl_proto_impl(
|
||||||
if let Some(m) = proto.get_method(&met.sig.ident) {
|
if let Some(m) = proto.get_method(&met.sig.ident) {
|
||||||
let fn_spec = FnSpec::parse(&mut met.sig, &mut met.attrs, false)?;
|
let fn_spec = FnSpec::parse(&mut met.sig, &mut met.attrs, false)?;
|
||||||
|
|
||||||
|
let flags = if m.can_coexist {
|
||||||
|
// We need METH_COEXIST here to prevent __add__ from overriding __radd__
|
||||||
|
Some(quote!(pyo3::ffi::METH_COEXIST))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let method = if let FnType::Fn(self_ty) = &fn_spec.tp {
|
let method = if let FnType::Fn(self_ty) = &fn_spec.tp {
|
||||||
pymethod::impl_proto_wrap(ty, &fn_spec, &self_ty)?
|
pymethod::impl_py_method_def(ty, &fn_spec, &self_ty, flags)?
|
||||||
} else {
|
} else {
|
||||||
bail_spanned!(
|
bail_spanned!(
|
||||||
met.sig.span() => "expected method with receiver for #[pyproto] method"
|
met.sig.span() => "expected method with receiver for #[pyproto] method"
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
let coexist = if m.can_coexist {
|
py_methods.push(method);
|
||||||
// We need METH_COEXIST here to prevent __add__ from overriding __radd__
|
|
||||||
quote!(pyo3::ffi::METH_COEXIST)
|
|
||||||
} else {
|
|
||||||
quote!(0)
|
|
||||||
};
|
|
||||||
let name = &met.sig.ident;
|
|
||||||
// TODO(kngwyu): Set ml_doc
|
|
||||||
py_methods.push(quote! {
|
|
||||||
pyo3::class::PyMethodDef::cfunction_with_keywords(
|
|
||||||
concat!(stringify!(#name), "\0"),
|
|
||||||
{
|
|
||||||
#method
|
|
||||||
pyo3::class::methods::PyCFunctionWithKeywords(__wrap)
|
|
||||||
},
|
|
||||||
#coexist,
|
|
||||||
"\0"
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +109,7 @@ fn impl_normal_methods(
|
||||||
{
|
{
|
||||||
fn #methods_trait_methods(self) -> &'static [pyo3::class::methods::PyMethodDefType] {
|
fn #methods_trait_methods(self) -> &'static [pyo3::class::methods::PyMethodDefType] {
|
||||||
static METHODS: &[pyo3::class::methods::PyMethodDefType] =
|
static METHODS: &[pyo3::class::methods::PyMethodDefType] =
|
||||||
&[#(pyo3::class::methods::PyMethodDefType::Method(#py_methods)),*];
|
&[#(#py_methods),*];
|
||||||
METHODS
|
METHODS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ unsafe impl Sync for PySetterDef {}
|
||||||
|
|
||||||
impl PyMethodDef {
|
impl PyMethodDef {
|
||||||
/// Define a function with no `*args` and `**kwargs`.
|
/// Define a function with no `*args` and `**kwargs`.
|
||||||
pub const fn cfunction(name: &'static str, cfunction: PyCFunction, doc: &'static str) -> Self {
|
pub const fn noargs(name: &'static str, cfunction: PyCFunction, doc: &'static str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ml_name: name,
|
ml_name: name,
|
||||||
ml_meth: PyMethodType::PyCFunction(cfunction),
|
ml_meth: PyMethodType::PyCFunction(cfunction),
|
||||||
|
@ -95,17 +95,21 @@ impl PyMethodDef {
|
||||||
pub const fn cfunction_with_keywords(
|
pub const fn cfunction_with_keywords(
|
||||||
name: &'static str,
|
name: &'static str,
|
||||||
cfunction: PyCFunctionWithKeywords,
|
cfunction: PyCFunctionWithKeywords,
|
||||||
flags: c_int,
|
|
||||||
doc: &'static str,
|
doc: &'static str,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ml_name: name,
|
ml_name: name,
|
||||||
ml_meth: PyMethodType::PyCFunctionWithKeywords(cfunction),
|
ml_meth: PyMethodType::PyCFunctionWithKeywords(cfunction),
|
||||||
ml_flags: flags | ffi::METH_VARARGS | ffi::METH_KEYWORDS,
|
ml_flags: ffi::METH_VARARGS | ffi::METH_KEYWORDS,
|
||||||
ml_doc: doc,
|
ml_doc: doc,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn flags(mut self, flags: c_int) -> Self {
|
||||||
|
self.ml_flags |= flags;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert `PyMethodDef` to Python method definition struct `ffi::PyMethodDef`
|
/// Convert `PyMethodDef` to Python method definition struct `ffi::PyMethodDef`
|
||||||
pub(crate) fn as_method_def(&self) -> Result<ffi::PyMethodDef, NulByteInString> {
|
pub(crate) fn as_method_def(&self) -> Result<ffi::PyMethodDef, NulByteInString> {
|
||||||
let meth = match self.ml_meth {
|
let meth = match self.ml_meth {
|
||||||
|
|
|
@ -23,12 +23,7 @@ impl PyCFunction {
|
||||||
py_or_module: PyFunctionArguments<'a>,
|
py_or_module: PyFunctionArguments<'a>,
|
||||||
) -> PyResult<&'a Self> {
|
) -> PyResult<&'a Self> {
|
||||||
Self::internal_new(
|
Self::internal_new(
|
||||||
PyMethodDef::cfunction_with_keywords(
|
PyMethodDef::cfunction_with_keywords(name, methods::PyCFunctionWithKeywords(fun), doc),
|
||||||
name,
|
|
||||||
methods::PyCFunctionWithKeywords(fun),
|
|
||||||
0,
|
|
||||||
doc,
|
|
||||||
),
|
|
||||||
py_or_module,
|
py_or_module,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -41,7 +36,7 @@ impl PyCFunction {
|
||||||
py_or_module: PyFunctionArguments<'a>,
|
py_or_module: PyFunctionArguments<'a>,
|
||||||
) -> PyResult<&'a Self> {
|
) -> PyResult<&'a Self> {
|
||||||
Self::internal_new(
|
Self::internal_new(
|
||||||
PyMethodDef::cfunction(name, methods::PyCFunction(fun), doc),
|
PyMethodDef::noargs(name, methods::PyCFunction(fun), doc),
|
||||||
py_or_module,
|
py_or_module,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,6 +109,10 @@ fn string_methods() {
|
||||||
py_assert!(py, obj, "repr(obj) == 'repr'");
|
py_assert!(py, obj, "repr(obj) == 'repr'");
|
||||||
py_assert!(py, obj, "'{0:x}'.format(obj) == 'format(x)'");
|
py_assert!(py, obj, "'{0:x}'.format(obj) == 'format(x)'");
|
||||||
py_assert!(py, obj, "bytes(obj) == b'bytes'");
|
py_assert!(py, obj, "bytes(obj) == b'bytes'");
|
||||||
|
|
||||||
|
// Test that `__bytes__` takes no arguments (should be METH_NOARGS)
|
||||||
|
py_assert!(py, obj, "obj.__bytes__() == b'bytes'");
|
||||||
|
py_expect_exception!(py, obj, "obj.__bytes__('unexpected argument')", PyTypeError);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
|
|
Loading…
Reference in New Issue