support `text_signature` on `#[new]`
This commit is contained in:
parent
157e0b4ad6
commit
8bd17f02c7
|
@ -722,22 +722,20 @@ py_args=(), py_kwargs=None, name=World, num=-1, num_before=44
|
||||||
|
|
||||||
## Making class method signatures available to Python
|
## Making class method signatures available to Python
|
||||||
|
|
||||||
The [`text_signature = "..."`](./function.md#text_signature) option for `#[pyfunction]` also works for classes and methods:
|
The [`text_signature = "..."`](./function.md#text_signature) option for `#[pyfunction]` also works for `#[pymethods]`:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# #![allow(dead_code)]
|
# #![allow(dead_code)]
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
use pyo3::types::PyType;
|
use pyo3::types::PyType;
|
||||||
|
|
||||||
// it works even if the item is not documented:
|
#[pyclass]
|
||||||
#[pyclass(text_signature = "(c, d, /)")]
|
|
||||||
struct MyClass {}
|
struct MyClass {}
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl MyClass {
|
impl MyClass {
|
||||||
// the signature for the constructor is attached
|
|
||||||
// to the struct definition instead.
|
|
||||||
#[new]
|
#[new]
|
||||||
|
#[pyo3(text_signature = "(c, d)")]
|
||||||
fn new(c: i32, d: &str) -> Self {
|
fn new(c: i32, d: &str) -> Self {
|
||||||
Self {}
|
Self {}
|
||||||
}
|
}
|
||||||
|
@ -746,8 +744,9 @@ impl MyClass {
|
||||||
fn my_method(&self, e: i32, f: i32) -> i32 {
|
fn my_method(&self, e: i32, f: i32) -> i32 {
|
||||||
e + f
|
e + f
|
||||||
}
|
}
|
||||||
|
// similarly for classmethod arguments, use $cls
|
||||||
#[classmethod]
|
#[classmethod]
|
||||||
#[pyo3(text_signature = "(cls, e, f)")]
|
#[pyo3(text_signature = "($cls, e, f)")]
|
||||||
fn my_class_method(cls: &PyType, e: i32, f: i32) -> i32 {
|
fn my_class_method(cls: &PyType, e: i32, f: i32) -> i32 {
|
||||||
e + f
|
e + f
|
||||||
}
|
}
|
||||||
|
@ -773,7 +772,7 @@ impl MyClass {
|
||||||
# .call1((class,))?
|
# .call1((class,))?
|
||||||
# .call_method0("__str__")?
|
# .call_method0("__str__")?
|
||||||
# .extract()?;
|
# .extract()?;
|
||||||
# assert_eq!(sig, "(c, d, /)");
|
# assert_eq!(sig, "(c, d)");
|
||||||
# } else {
|
# } else {
|
||||||
# let doc: String = class.getattr("__doc__")?.extract()?;
|
# let doc: String = class.getattr("__doc__")?.extract()?;
|
||||||
# assert_eq!(doc, "");
|
# assert_eq!(doc, "");
|
||||||
|
@ -802,7 +801,7 @@ impl MyClass {
|
||||||
# .call1((method,))?
|
# .call1((method,))?
|
||||||
# .call_method0("__str__")?
|
# .call_method0("__str__")?
|
||||||
# .extract()?;
|
# .extract()?;
|
||||||
# assert_eq!(sig, "(cls, e, f)");
|
# assert_eq!(sig, "(e, f)"); // inspect.signature skips the $cls arg
|
||||||
# }
|
# }
|
||||||
#
|
#
|
||||||
# {
|
# {
|
||||||
|
@ -822,7 +821,7 @@ impl MyClass {
|
||||||
# }
|
# }
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that `text_signature` on classes is not compatible with compilation in
|
Note that `text_signature` on `#[new]` is not compatible with compilation in
|
||||||
`abi3` mode until Python 3.10 or greater.
|
`abi3` mode until Python 3.10 or greater.
|
||||||
|
|
||||||
## #[pyclass] enums
|
## #[pyclass] enums
|
||||||
|
@ -1018,7 +1017,6 @@ impl pyo3::IntoPy<PyObject> for MyClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
|
impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
|
||||||
const DOC: &'static str = "Class for demonstration\u{0}";
|
|
||||||
const IS_BASETYPE: bool = false;
|
const IS_BASETYPE: bool = false;
|
||||||
const IS_SUBCLASS: bool = false;
|
const IS_SUBCLASS: bool = false;
|
||||||
type Layout = PyCell<MyClass>;
|
type Layout = PyCell<MyClass>;
|
||||||
|
@ -1041,6 +1039,15 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
|
||||||
static TYPE_OBJECT: LazyTypeObject<MyClass> = LazyTypeObject::new();
|
static TYPE_OBJECT: LazyTypeObject<MyClass> = LazyTypeObject::new();
|
||||||
&TYPE_OBJECT
|
&TYPE_OBJECT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn doc(py: Python<'_>) -> pyo3::PyResult<&'static ::std::ffi::CStr> {
|
||||||
|
use pyo3::impl_::pyclass::*;
|
||||||
|
static DOC: pyo3::once_cell::GILOnceCell<::std::borrow::Cow<'static, ::std::ffi::CStr>> = pyo3::once_cell::GILOnceCell::new();
|
||||||
|
DOC.get_or_try_init(py, || {
|
||||||
|
let collector = PyClassImplCollector::<Self>::new();
|
||||||
|
build_pyclass_doc(<MyClass as pyo3::PyTypeInfo>::NAME, "", None.or_else(|| collector.new_text_signature()))
|
||||||
|
}).map(::std::ops::Deref::deref)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Python::with_gil(|py| {
|
# Python::with_gil(|py| {
|
||||||
|
|
|
@ -42,7 +42,7 @@ fn wrap(obj: &PyAny) -> Result<i32, PyErr> {
|
||||||
Ok(val as i32)
|
Ok(val as i32)
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
We also add documentation, via `///` comments and the `#[pyo3(text_signature = "...")]` attribute, both of which are visible to Python users.
|
We also add documentation, via `///` comments, which are visible to Python users.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# #![allow(dead_code)]
|
# #![allow(dead_code)]
|
||||||
|
@ -57,7 +57,6 @@ fn wrap(obj: &PyAny) -> Result<i32, PyErr> {
|
||||||
/// Did you ever hear the tragedy of Darth Signed The Overfloweth? I thought not.
|
/// Did you ever hear the tragedy of Darth Signed The Overfloweth? I thought not.
|
||||||
/// It's not a story C would tell you. It's a Rust legend.
|
/// It's not a story C would tell you. It's a Rust legend.
|
||||||
#[pyclass(module = "my_module")]
|
#[pyclass(module = "my_module")]
|
||||||
#[pyo3(text_signature = "(int)")]
|
|
||||||
struct Number(i32);
|
struct Number(i32);
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
|
@ -223,7 +222,6 @@ fn wrap(obj: &PyAny) -> Result<i32, PyErr> {
|
||||||
/// Did you ever hear the tragedy of Darth Signed The Overfloweth? I thought not.
|
/// Did you ever hear the tragedy of Darth Signed The Overfloweth? I thought not.
|
||||||
/// It's not a story C would tell you. It's a Rust legend.
|
/// It's not a story C would tell you. It's a Rust legend.
|
||||||
#[pyclass(module = "my_module")]
|
#[pyclass(module = "my_module")]
|
||||||
#[pyo3(text_signature = "(int)")]
|
|
||||||
struct Number(i32);
|
struct Number(i32);
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
|
@ -377,7 +375,7 @@ fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
|
||||||
# assert Number(12345234523452) == Number(1498514748)
|
# assert Number(12345234523452) == Number(1498514748)
|
||||||
# try:
|
# try:
|
||||||
# import inspect
|
# import inspect
|
||||||
# assert inspect.signature(Number).__str__() == '(int)'
|
# assert inspect.signature(Number).__str__() == '(value)'
|
||||||
# except ValueError:
|
# except ValueError:
|
||||||
# # Not supported with `abi3` before Python 3.10
|
# # Not supported with `abi3` before Python 3.10
|
||||||
# pass
|
# pass
|
||||||
|
|
|
@ -272,9 +272,7 @@ impl MyClass {
|
||||||
|
|
||||||
The function signature is exposed to Python via the `__text_signature__` attribute. PyO3 automatically generates this for every `#[pyfunction]` and all `#[pymethods]` directly from the Rust function, taking into account any override done with the `#[pyo3(signature = (...))]` option.
|
The function signature is exposed to Python via the `__text_signature__` attribute. PyO3 automatically generates this for every `#[pyfunction]` and all `#[pymethods]` directly from the Rust function, taking into account any override done with the `#[pyo3(signature = (...))]` option.
|
||||||
|
|
||||||
This automatic generation has some limitations, which may be improved in the future:
|
This automatic generation can only display the value of default arguments for strings, integers, boolean types, and `None`. Any other default arguments will be displayed as `...`. (`.pyi` type stub files commonly also use `...` for default arguments in the same way.)
|
||||||
- It will not include the value of default arguments, replacing them all with `...`. (`.pyi` type stub files commonly also use `...` for all default arguments in the same way.)
|
|
||||||
- Nothing is generated for the `#[new]` method of a `#[pyclass]`.
|
|
||||||
|
|
||||||
In cases where the automatically-generated signature needs adjusting, it can [be overridden](#overriding-the-generated-signature) using the `#[pyo3(text_signature)]` option.)
|
In cases where the automatically-generated signature needs adjusting, it can [be overridden](#overriding-the-generated-signature) using the `#[pyo3(text_signature)]` option.)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Support `text_signature` option (and automatically generate signature) for `#[new]` in `#[pymethods]`.
|
|
@ -0,0 +1 @@
|
||||||
|
Deprecate `text_signature` option on `#[pyclass]`.
|
|
@ -2,6 +2,7 @@ use proc_macro2::{Span, TokenStream};
|
||||||
use quote::{quote_spanned, ToTokens};
|
use quote::{quote_spanned, ToTokens};
|
||||||
|
|
||||||
pub enum Deprecation {
|
pub enum Deprecation {
|
||||||
|
PyClassTextSignature,
|
||||||
PyFunctionArguments,
|
PyFunctionArguments,
|
||||||
PyMethodArgsAttribute,
|
PyMethodArgsAttribute,
|
||||||
RequiredArgumentAfterOption,
|
RequiredArgumentAfterOption,
|
||||||
|
@ -10,6 +11,7 @@ pub enum Deprecation {
|
||||||
impl Deprecation {
|
impl Deprecation {
|
||||||
fn ident(&self, span: Span) -> syn::Ident {
|
fn ident(&self, span: Span) -> syn::Ident {
|
||||||
let string = match self {
|
let string = match self {
|
||||||
|
Deprecation::PyClassTextSignature => "PYCLASS_TEXT_SIGNATURE",
|
||||||
Deprecation::PyFunctionArguments => "PYFUNCTION_ARGUMENTS",
|
Deprecation::PyFunctionArguments => "PYFUNCTION_ARGUMENTS",
|
||||||
Deprecation::PyMethodArgsAttribute => "PYMETHODS_ARGS_ATTRIBUTE",
|
Deprecation::PyMethodArgsAttribute => "PYMETHODS_ARGS_ATTRIBUTE",
|
||||||
Deprecation::RequiredArgumentAfterOption => "REQUIRED_ARGUMENT_AFTER_OPTION",
|
Deprecation::RequiredArgumentAfterOption => "REQUIRED_ARGUMENT_AFTER_OPTION",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||||
|
|
||||||
use crate::attributes::TextSignatureAttribute;
|
use crate::attributes::{TextSignatureAttribute, TextSignatureAttributeValue};
|
||||||
use crate::deprecations::{Deprecation, Deprecations};
|
use crate::deprecations::{Deprecation, Deprecations};
|
||||||
use crate::params::impl_arg_params;
|
use crate::params::impl_arg_params;
|
||||||
use crate::pyfunction::{DeprecatedArgs, FunctionSignature, PyFunctionArgPyO3Attributes};
|
use crate::pyfunction::{DeprecatedArgs, FunctionSignature, PyFunctionArgPyO3Attributes};
|
||||||
|
@ -593,6 +593,33 @@ impl<'a> FnSpec<'a> {
|
||||||
CallingConvention::TpNew => unreachable!("tp_new cannot get a methoddef"),
|
CallingConvention::TpNew => unreachable!("tp_new cannot get a methoddef"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Forwards to [utils::get_doc] with the text signature of this spec.
|
||||||
|
pub fn get_doc(&self, attrs: &[syn::Attribute]) -> PythonDoc {
|
||||||
|
let text_signature = self
|
||||||
|
.text_signature_call_signature()
|
||||||
|
.map(|sig| format!("{}{}", self.python_name, sig));
|
||||||
|
utils::get_doc(attrs, text_signature)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates the parenthesised arguments list for `__text_signature__` snippet based on this spec's signature
|
||||||
|
/// and/or attributes. Prepend the callable name to make a complete `__text_signature__`.
|
||||||
|
pub fn text_signature_call_signature(&self) -> Option<String> {
|
||||||
|
let self_argument = match &self.tp {
|
||||||
|
// Getters / Setters / ClassAttribute are not callables on the Python side
|
||||||
|
FnType::Getter(_) | FnType::Setter(_) | FnType::ClassAttribute => return None,
|
||||||
|
FnType::Fn(_) => Some("self"),
|
||||||
|
FnType::FnModule => Some("module"),
|
||||||
|
FnType::FnClass => Some("cls"),
|
||||||
|
FnType::FnStatic | FnType::FnNew => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
match self.text_signature.as_ref().map(|attr| &attr.value) {
|
||||||
|
Some(TextSignatureAttributeValue::Str(s)) => Some(s.value()),
|
||||||
|
None => Some(self.signature.text_signature(self_argument)),
|
||||||
|
Some(TextSignatureAttributeValue::Disabled(_)) => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -755,11 +782,6 @@ fn ensure_signatures_on_valid_method(
|
||||||
}
|
}
|
||||||
if let Some(text_signature) = text_signature {
|
if let Some(text_signature) = text_signature {
|
||||||
match fn_type {
|
match fn_type {
|
||||||
FnType::FnNew => bail_spanned!(
|
|
||||||
text_signature.kw.span() =>
|
|
||||||
"`text_signature` not allowed on `__new__`; if you want to add a signature on \
|
|
||||||
`__new__`, put it on the struct definition instead"
|
|
||||||
),
|
|
||||||
FnType::Getter(_) => {
|
FnType::Getter(_) => {
|
||||||
bail_spanned!(text_signature.kw.span() => "`text_signature` not allowed with `getter`")
|
bail_spanned!(text_signature.kw.span() => "`text_signature` not allowed with `getter`")
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,11 @@ use std::borrow::Cow;
|
||||||
use crate::attributes::{
|
use crate::attributes::{
|
||||||
self, kw, take_pyo3_options, CrateAttribute, ExtendsAttribute, FreelistAttribute,
|
self, kw, take_pyo3_options, CrateAttribute, ExtendsAttribute, FreelistAttribute,
|
||||||
ModuleAttribute, NameAttribute, NameLitStr, TextSignatureAttribute,
|
ModuleAttribute, NameAttribute, NameLitStr, TextSignatureAttribute,
|
||||||
|
TextSignatureAttributeValue,
|
||||||
};
|
};
|
||||||
use crate::deprecations::Deprecations;
|
use crate::deprecations::{Deprecation, Deprecations};
|
||||||
use crate::konst::{ConstAttributes, ConstSpec};
|
use crate::konst::{ConstAttributes, ConstSpec};
|
||||||
use crate::method::FnSpec;
|
use crate::method::FnSpec;
|
||||||
use crate::pyfunction::text_signature_or_none;
|
|
||||||
use crate::pyimpl::{gen_py_const, PyClassMethodsType};
|
use crate::pyimpl::{gen_py_const, PyClassMethodsType};
|
||||||
use crate::pymethod::{
|
use crate::pymethod::{
|
||||||
impl_py_getter_def, impl_py_setter_def, MethodAndMethodDef, MethodAndSlotDef, PropertyType,
|
impl_py_getter_def, impl_py_setter_def, MethodAndMethodDef, MethodAndSlotDef, PropertyType,
|
||||||
|
@ -177,7 +177,11 @@ impl PyClassPyO3Options {
|
||||||
PyClassPyO3Option::Sequence(sequence) => set_option!(sequence),
|
PyClassPyO3Option::Sequence(sequence) => set_option!(sequence),
|
||||||
PyClassPyO3Option::SetAll(set_all) => set_option!(set_all),
|
PyClassPyO3Option::SetAll(set_all) => set_option!(set_all),
|
||||||
PyClassPyO3Option::Subclass(subclass) => set_option!(subclass),
|
PyClassPyO3Option::Subclass(subclass) => set_option!(subclass),
|
||||||
PyClassPyO3Option::TextSignature(text_signature) => set_option!(text_signature),
|
PyClassPyO3Option::TextSignature(text_signature) => {
|
||||||
|
self.deprecations
|
||||||
|
.push(Deprecation::PyClassTextSignature, text_signature.span());
|
||||||
|
set_option!(text_signature)
|
||||||
|
}
|
||||||
PyClassPyO3Option::Unsendable(unsendable) => set_option!(unsendable),
|
PyClassPyO3Option::Unsendable(unsendable) => set_option!(unsendable),
|
||||||
PyClassPyO3Option::Weakref(weakref) => set_option!(weakref),
|
PyClassPyO3Option::Weakref(weakref) => set_option!(weakref),
|
||||||
}
|
}
|
||||||
|
@ -191,11 +195,7 @@ pub fn build_py_class(
|
||||||
methods_type: PyClassMethodsType,
|
methods_type: PyClassMethodsType,
|
||||||
) -> syn::Result<TokenStream> {
|
) -> syn::Result<TokenStream> {
|
||||||
args.options.take_pyo3_options(&mut class.attrs)?;
|
args.options.take_pyo3_options(&mut class.attrs)?;
|
||||||
let text_signature_string = text_signature_or_none(args.options.text_signature.as_ref());
|
let doc = utils::get_doc(&class.attrs, None);
|
||||||
let doc = utils::get_doc(
|
|
||||||
&class.attrs,
|
|
||||||
text_signature_string.map(|s| (get_class_python_name(&class.ident, &args), s)),
|
|
||||||
);
|
|
||||||
let krate = get_pyo3_crate(&args.options.krate);
|
let krate = get_pyo3_crate(&args.options.krate);
|
||||||
|
|
||||||
if let Some(lt) = class.generics.lifetimes().next() {
|
if let Some(lt) = class.generics.lifetimes().next() {
|
||||||
|
@ -452,12 +452,7 @@ pub fn build_py_enum(
|
||||||
bail_spanned!(enum_.brace_token.span => "#[pyclass] can't be used on enums without any variants");
|
bail_spanned!(enum_.brace_token.span => "#[pyclass] can't be used on enums without any variants");
|
||||||
}
|
}
|
||||||
|
|
||||||
let text_signature_string = text_signature_or_none(args.options.text_signature.as_ref());
|
let doc = utils::get_doc(&enum_.attrs, None);
|
||||||
|
|
||||||
let doc = utils::get_doc(
|
|
||||||
&enum_.attrs,
|
|
||||||
text_signature_string.map(|s| (get_class_python_name(&enum_.ident, &args), s)),
|
|
||||||
);
|
|
||||||
let enum_ = PyClassEnum::new(enum_)?;
|
let enum_ = PyClassEnum::new(enum_)?;
|
||||||
impl_enum(enum_, &args, doc, method_type)
|
impl_enum(enum_, &args, doc, method_type)
|
||||||
}
|
}
|
||||||
|
@ -509,16 +504,6 @@ fn impl_enum(
|
||||||
methods_type: PyClassMethodsType,
|
methods_type: PyClassMethodsType,
|
||||||
) -> Result<TokenStream> {
|
) -> Result<TokenStream> {
|
||||||
let krate = get_pyo3_crate(&args.options.krate);
|
let krate = get_pyo3_crate(&args.options.krate);
|
||||||
impl_enum_class(enum_, args, doc, methods_type, krate)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn impl_enum_class(
|
|
||||||
enum_: PyClassEnum<'_>,
|
|
||||||
args: &PyClassArgs,
|
|
||||||
doc: PythonDoc,
|
|
||||||
methods_type: PyClassMethodsType,
|
|
||||||
krate: syn::Path,
|
|
||||||
) -> Result<TokenStream> {
|
|
||||||
let cls = enum_.ident;
|
let cls = enum_.ident;
|
||||||
let ty: syn::Type = syn::parse_quote!(#cls);
|
let ty: syn::Type = syn::parse_quote!(#cls);
|
||||||
let variants = enum_.variants;
|
let variants = enum_.variants;
|
||||||
|
@ -889,6 +874,18 @@ impl<'a> PyClassImplsBuilder<'a> {
|
||||||
fn impl_pyclassimpl(&self) -> Result<TokenStream> {
|
fn impl_pyclassimpl(&self) -> Result<TokenStream> {
|
||||||
let cls = self.cls;
|
let cls = self.cls;
|
||||||
let doc = self.doc.as_ref().map_or(quote! {"\0"}, |doc| quote! {#doc});
|
let doc = self.doc.as_ref().map_or(quote! {"\0"}, |doc| quote! {#doc});
|
||||||
|
let deprecated_text_signature = match self
|
||||||
|
.attr
|
||||||
|
.options
|
||||||
|
.text_signature
|
||||||
|
.as_ref()
|
||||||
|
.map(|attr| &attr.value)
|
||||||
|
{
|
||||||
|
Some(TextSignatureAttributeValue::Str(s)) => quote!(::std::option::Option::Some(#s)),
|
||||||
|
Some(TextSignatureAttributeValue::Disabled(_)) | None => {
|
||||||
|
quote!(::std::option::Option::None)
|
||||||
|
}
|
||||||
|
};
|
||||||
let is_basetype = self.attr.options.subclass.is_some();
|
let is_basetype = self.attr.options.subclass.is_some();
|
||||||
let base = self
|
let base = self
|
||||||
.attr
|
.attr
|
||||||
|
@ -1009,7 +1006,6 @@ impl<'a> PyClassImplsBuilder<'a> {
|
||||||
|
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
impl _pyo3::impl_::pyclass::PyClassImpl for #cls {
|
impl _pyo3::impl_::pyclass::PyClassImpl for #cls {
|
||||||
const DOC: &'static str = #doc;
|
|
||||||
const IS_BASETYPE: bool = #is_basetype;
|
const IS_BASETYPE: bool = #is_basetype;
|
||||||
const IS_SUBCLASS: bool = #is_subclass;
|
const IS_SUBCLASS: bool = #is_subclass;
|
||||||
const IS_MAPPING: bool = #is_mapping;
|
const IS_MAPPING: bool = #is_mapping;
|
||||||
|
@ -1035,6 +1031,15 @@ impl<'a> PyClassImplsBuilder<'a> {
|
||||||
PyClassItemsIter::new(&INTRINSIC_ITEMS, #pymethods_items)
|
PyClassItemsIter::new(&INTRINSIC_ITEMS, #pymethods_items)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn doc(py: _pyo3::Python<'_>) -> _pyo3::PyResult<&'static ::std::ffi::CStr> {
|
||||||
|
use _pyo3::impl_::pyclass::*;
|
||||||
|
static DOC: _pyo3::once_cell::GILOnceCell<::std::borrow::Cow<'static, ::std::ffi::CStr>> = _pyo3::once_cell::GILOnceCell::new();
|
||||||
|
DOC.get_or_try_init(py, || {
|
||||||
|
let collector = PyClassImplCollector::<Self>::new();
|
||||||
|
build_pyclass_doc(<#cls as _pyo3::PyTypeInfo>::NAME, #doc, #deprecated_text_signature.or_else(|| collector.new_text_signature()))
|
||||||
|
}).map(::std::ops::Deref::deref)
|
||||||
|
}
|
||||||
|
|
||||||
#dict_offset
|
#dict_offset
|
||||||
|
|
||||||
#weaklist_offset
|
#weaklist_offset
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||||
|
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
attributes::{
|
attributes::{
|
||||||
self, get_pyo3_options, take_attributes, take_pyo3_options, CrateAttribute,
|
self, get_pyo3_options, take_attributes, take_pyo3_options, CrateAttribute,
|
||||||
FromPyWithAttribute, NameAttribute, TextSignatureAttribute, TextSignatureAttributeValue,
|
FromPyWithAttribute, NameAttribute, TextSignatureAttribute,
|
||||||
},
|
},
|
||||||
deprecations::{Deprecation, Deprecations},
|
deprecations::{Deprecation, Deprecations},
|
||||||
method::{self, CallingConvention, FnArg, FnType},
|
method::{self, CallingConvention, FnArg},
|
||||||
pymethod::check_generic,
|
pymethod::check_generic,
|
||||||
utils::{self, ensure_not_async_fn, get_pyo3_crate},
|
utils::{ensure_not_async_fn, get_pyo3_crate},
|
||||||
};
|
};
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
|
@ -409,15 +407,6 @@ pub fn impl_wrap_pyfunction(
|
||||||
|
|
||||||
let ty = method::get_return_info(&func.sig.output);
|
let ty = method::get_return_info(&func.sig.output);
|
||||||
|
|
||||||
let text_signature_string = text_signature_or_auto(text_signature.as_ref(), &signature, &tp);
|
|
||||||
|
|
||||||
let doc = utils::get_doc(
|
|
||||||
&func.attrs,
|
|
||||||
text_signature_string.map(|s| (Cow::Borrowed(&python_name), s)),
|
|
||||||
);
|
|
||||||
|
|
||||||
let krate = get_pyo3_crate(&krate);
|
|
||||||
|
|
||||||
let spec = method::FnSpec {
|
let spec = method::FnSpec {
|
||||||
tp,
|
tp,
|
||||||
name: &func.sig.ident,
|
name: &func.sig.ident,
|
||||||
|
@ -430,12 +419,14 @@ pub fn impl_wrap_pyfunction(
|
||||||
unsafety: func.sig.unsafety,
|
unsafety: func.sig.unsafety,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let krate = get_pyo3_crate(&krate);
|
||||||
|
|
||||||
let vis = &func.vis;
|
let vis = &func.vis;
|
||||||
let name = &func.sig.ident;
|
let name = &func.sig.ident;
|
||||||
|
|
||||||
let wrapper_ident = format_ident!("__pyfunction_{}", spec.name);
|
let wrapper_ident = format_ident!("__pyfunction_{}", spec.name);
|
||||||
let wrapper = spec.get_wrapper_function(&wrapper_ident, None)?;
|
let wrapper = spec.get_wrapper_function(&wrapper_ident, None)?;
|
||||||
let methoddef = spec.get_methoddef(wrapper_ident, &doc);
|
let methoddef = spec.get_methoddef(wrapper_ident, &spec.get_doc(&func.attrs));
|
||||||
|
|
||||||
let wrapped_pyfunction = quote! {
|
let wrapped_pyfunction = quote! {
|
||||||
|
|
||||||
|
@ -480,26 +471,3 @@ fn type_is_pymodule(ty: &syn::Type) -> bool {
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper to get a text signature string, or None if unset or disabled
|
|
||||||
pub(crate) fn text_signature_or_none(
|
|
||||||
text_signature: Option<&TextSignatureAttribute>,
|
|
||||||
) -> Option<String> {
|
|
||||||
match text_signature.map(|attr| &attr.value) {
|
|
||||||
Some(TextSignatureAttributeValue::Str(s)) => Some(s.value()),
|
|
||||||
Some(TextSignatureAttributeValue::Disabled(_)) | None => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Helper to get a text signature string, using automatic generation if unset, or None if disabled
|
|
||||||
pub(crate) fn text_signature_or_auto(
|
|
||||||
text_signature: Option<&TextSignatureAttribute>,
|
|
||||||
signature: &FunctionSignature<'_>,
|
|
||||||
fn_type: &FnType,
|
|
||||||
) -> Option<String> {
|
|
||||||
match text_signature.map(|attr| &attr.value) {
|
|
||||||
Some(TextSignatureAttributeValue::Str(s)) => Some(s.value()),
|
|
||||||
None => Some(signature.text_signature(fn_type)),
|
|
||||||
Some(TextSignatureAttributeValue::Disabled(_)) => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ use syn::{
|
||||||
use crate::{
|
use crate::{
|
||||||
attributes::{kw, KeywordAttribute},
|
attributes::{kw, KeywordAttribute},
|
||||||
deprecations::{Deprecation, Deprecations},
|
deprecations::{Deprecation, Deprecations},
|
||||||
method::{FnArg, FnType},
|
method::FnArg,
|
||||||
pyfunction::Argument,
|
pyfunction::Argument,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -623,18 +623,7 @@ impl<'a> FunctionSignature<'a> {
|
||||||
default
|
default
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn text_signature(&self, fn_type: &FnType) -> String {
|
pub fn text_signature(&self, self_argument: Option<&str>) -> String {
|
||||||
// automatic text signature generation
|
|
||||||
let self_argument = match fn_type {
|
|
||||||
FnType::FnNew | FnType::Getter(_) | FnType::Setter(_) | FnType::ClassAttribute => {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
FnType::Fn(_) => Some("self"),
|
|
||||||
FnType::FnModule => Some("module"),
|
|
||||||
FnType::FnClass => Some("cls"),
|
|
||||||
FnType::FnStatic => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
output.push('(');
|
output.push('(');
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::attributes::NameAttribute;
|
use crate::attributes::NameAttribute;
|
||||||
use crate::method::{CallingConvention, ExtractErrorMode};
|
use crate::method::{CallingConvention, ExtractErrorMode};
|
||||||
use crate::pyfunction::text_signature_or_auto;
|
|
||||||
use crate::utils::{ensure_not_async_fn, PythonDoc};
|
use crate::utils::{ensure_not_async_fn, PythonDoc};
|
||||||
use crate::{deprecations::Deprecations, utils};
|
use crate::{deprecations::Deprecations, utils};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -219,19 +218,19 @@ pub fn gen_py_method(
|
||||||
(_, FnType::Fn(_)) => GeneratedPyMethod::Method(impl_py_method_def(
|
(_, FnType::Fn(_)) => GeneratedPyMethod::Method(impl_py_method_def(
|
||||||
cls,
|
cls,
|
||||||
spec,
|
spec,
|
||||||
&create_doc(meth_attrs, spec),
|
&spec.get_doc(meth_attrs),
|
||||||
None,
|
None,
|
||||||
)?),
|
)?),
|
||||||
(_, FnType::FnClass) => GeneratedPyMethod::Method(impl_py_method_def(
|
(_, FnType::FnClass) => GeneratedPyMethod::Method(impl_py_method_def(
|
||||||
cls,
|
cls,
|
||||||
spec,
|
spec,
|
||||||
&create_doc(meth_attrs, spec),
|
&spec.get_doc(meth_attrs),
|
||||||
Some(quote!(_pyo3::ffi::METH_CLASS)),
|
Some(quote!(_pyo3::ffi::METH_CLASS)),
|
||||||
)?),
|
)?),
|
||||||
(_, FnType::FnStatic) => GeneratedPyMethod::Method(impl_py_method_def(
|
(_, FnType::FnStatic) => GeneratedPyMethod::Method(impl_py_method_def(
|
||||||
cls,
|
cls,
|
||||||
spec,
|
spec,
|
||||||
&create_doc(meth_attrs, spec),
|
&spec.get_doc(meth_attrs),
|
||||||
Some(quote!(_pyo3::ffi::METH_STATIC)),
|
Some(quote!(_pyo3::ffi::METH_STATIC)),
|
||||||
)?),
|
)?),
|
||||||
// special prototypes
|
// special prototypes
|
||||||
|
@ -242,7 +241,7 @@ pub fn gen_py_method(
|
||||||
PropertyType::Function {
|
PropertyType::Function {
|
||||||
self_type,
|
self_type,
|
||||||
spec,
|
spec,
|
||||||
doc: create_doc(meth_attrs, spec),
|
doc: spec.get_doc(meth_attrs),
|
||||||
},
|
},
|
||||||
)?),
|
)?),
|
||||||
(_, FnType::Setter(self_type)) => GeneratedPyMethod::Method(impl_py_setter_def(
|
(_, FnType::Setter(self_type)) => GeneratedPyMethod::Method(impl_py_setter_def(
|
||||||
|
@ -250,7 +249,7 @@ pub fn gen_py_method(
|
||||||
PropertyType::Function {
|
PropertyType::Function {
|
||||||
self_type,
|
self_type,
|
||||||
spec,
|
spec,
|
||||||
doc: create_doc(meth_attrs, spec),
|
doc: spec.get_doc(meth_attrs),
|
||||||
},
|
},
|
||||||
)?),
|
)?),
|
||||||
(_, FnType::FnModule) => {
|
(_, FnType::FnModule) => {
|
||||||
|
@ -259,18 +258,6 @@ pub fn gen_py_method(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_doc(meth_attrs: &[syn::Attribute], spec: &FnSpec<'_>) -> PythonDoc {
|
|
||||||
let text_signature_string = match &spec.tp {
|
|
||||||
FnType::FnNew | FnType::Getter(_) | FnType::Setter(_) | FnType::ClassAttribute => None,
|
|
||||||
_ => text_signature_or_auto(spec.text_signature.as_ref(), &spec.signature, &spec.tp),
|
|
||||||
};
|
|
||||||
|
|
||||||
utils::get_doc(
|
|
||||||
meth_attrs,
|
|
||||||
text_signature_string.map(|sig| (Cow::Borrowed(&spec.python_name), sig)),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_generic(sig: &syn::Signature) -> syn::Result<()> {
|
pub fn check_generic(sig: &syn::Signature) -> syn::Result<()> {
|
||||||
let err_msg = |typ| format!("Python functions cannot have generic {} parameters", typ);
|
let err_msg = |typ| format!("Python functions cannot have generic {} parameters", typ);
|
||||||
for param in &sig.generics.params {
|
for param in &sig.generics.params {
|
||||||
|
@ -335,6 +322,13 @@ pub fn impl_py_method_def(
|
||||||
fn impl_py_method_def_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<MethodAndSlotDef> {
|
fn impl_py_method_def_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<MethodAndSlotDef> {
|
||||||
let wrapper_ident = syn::Ident::new("__pymethod___new____", Span::call_site());
|
let wrapper_ident = syn::Ident::new("__pymethod___new____", Span::call_site());
|
||||||
let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls))?;
|
let associated_method = spec.get_wrapper_function(&wrapper_ident, Some(cls))?;
|
||||||
|
// Use just the text_signature_call_signature() because the class' Python name
|
||||||
|
// isn't known to `#[pymethods]` - that has to be attached at runtime from the PyClassImpl
|
||||||
|
// trait implementation created by `#[pyclass]`.
|
||||||
|
let text_signature_body = spec.text_signature_call_signature().map_or_else(
|
||||||
|
|| quote!(::std::option::Option::None),
|
||||||
|
|text_signature| quote!(::std::option::Option::Some(#text_signature)),
|
||||||
|
);
|
||||||
let slot_def = quote! {
|
let slot_def = quote! {
|
||||||
_pyo3::ffi::PyType_Slot {
|
_pyo3::ffi::PyType_Slot {
|
||||||
slot: _pyo3::ffi::Py_tp_new,
|
slot: _pyo3::ffi::Py_tp_new,
|
||||||
|
@ -345,6 +339,14 @@ fn impl_py_method_def_new(cls: &syn::Type, spec: &FnSpec<'_>) -> Result<MethodAn
|
||||||
kwargs: *mut _pyo3::ffi::PyObject,
|
kwargs: *mut _pyo3::ffi::PyObject,
|
||||||
) -> *mut _pyo3::ffi::PyObject
|
) -> *mut _pyo3::ffi::PyObject
|
||||||
{
|
{
|
||||||
|
use _pyo3::impl_::pyclass::*;
|
||||||
|
impl PyClassNewTextSignature<#cls> for PyClassImplCollector<#cls> {
|
||||||
|
#[inline]
|
||||||
|
fn new_text_signature(self) -> ::std::option::Option<&'static str> {
|
||||||
|
#text_signature_body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_pyo3::impl_::trampoline::newfunc(
|
_pyo3::impl_::trampoline::newfunc(
|
||||||
subtype,
|
subtype,
|
||||||
args,
|
args,
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
use std::{borrow::Cow, fmt::Write};
|
|
||||||
|
|
||||||
// Copyright (c) 2017-present PyO3 Project and Contributors
|
// Copyright (c) 2017-present PyO3 Project and Contributors
|
||||||
use proc_macro2::{Span, TokenStream};
|
use proc_macro2::{Span, TokenStream};
|
||||||
use quote::ToTokens;
|
use quote::ToTokens;
|
||||||
use syn::{punctuated::Punctuated, spanned::Spanned, Ident, Token};
|
use syn::{punctuated::Punctuated, spanned::Spanned, Token};
|
||||||
|
|
||||||
use crate::attributes::CrateAttribute;
|
use crate::attributes::CrateAttribute;
|
||||||
|
|
||||||
|
@ -67,24 +65,20 @@ pub fn option_type_argument(ty: &syn::Type) -> Option<&syn::Type> {
|
||||||
pub struct PythonDoc(TokenStream);
|
pub struct PythonDoc(TokenStream);
|
||||||
|
|
||||||
/// Collects all #[doc = "..."] attributes into a TokenStream evaluating to a null-terminated string.
|
/// Collects all #[doc = "..."] attributes into a TokenStream evaluating to a null-terminated string.
|
||||||
pub fn get_doc(
|
///
|
||||||
attrs: &[syn::Attribute],
|
/// If this doc is for a callable, the provided `text_signature` can be passed to prepend
|
||||||
text_signature: Option<(Cow<'_, Ident>, String)>,
|
/// this to the documentation suitable for Python to extract this into the `__text_signature__`
|
||||||
) -> PythonDoc {
|
/// attribute.
|
||||||
let mut parts = Punctuated::<TokenStream, Token![,]>::new();
|
pub fn get_doc(attrs: &[syn::Attribute], mut text_signature: Option<String>) -> PythonDoc {
|
||||||
let mut current_part = String::new();
|
// insert special divider between `__text_signature__` and doc
|
||||||
|
// (assume text_signature is itself well-formed)
|
||||||
if let Some((python_name, text_signature)) = text_signature {
|
if let Some(text_signature) = &mut text_signature {
|
||||||
// create special doc string lines to set `__text_signature__`
|
text_signature.push_str("\n--\n\n");
|
||||||
write!(
|
|
||||||
&mut current_part,
|
|
||||||
"{}{}\n--\n\n",
|
|
||||||
python_name, text_signature
|
|
||||||
)
|
|
||||||
.expect("error occurred while trying to format text_signature to string")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut parts = Punctuated::<TokenStream, Token![,]>::new();
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
|
let mut current_part = text_signature.unwrap_or_default();
|
||||||
|
|
||||||
for attr in attrs.iter() {
|
for attr in attrs.iter() {
|
||||||
if attr.path.is_ident("doc") {
|
if attr.path.is_ident("doc") {
|
||||||
|
|
|
@ -17,3 +17,9 @@ pub const PYMETHODS_ARGS_ATTRIBUTE: () = ();
|
||||||
note = "required arguments after an `Option<_>` argument are ambiguous and being phased out\n= help: add a `#[pyo3(signature)]` annotation on this function to unambiguously specify the default values for all optional parameters"
|
note = "required arguments after an `Option<_>` argument are ambiguous and being phased out\n= help: add a `#[pyo3(signature)]` annotation on this function to unambiguously specify the default values for all optional parameters"
|
||||||
)]
|
)]
|
||||||
pub const REQUIRED_ARGUMENT_AFTER_OPTION: () = ();
|
pub const REQUIRED_ARGUMENT_AFTER_OPTION: () = ();
|
||||||
|
|
||||||
|
#[deprecated(
|
||||||
|
since = "0.19.0",
|
||||||
|
note = "put `text_signature` on `#[new]` instead of `#[pyclass]`"
|
||||||
|
)]
|
||||||
|
pub const PYCLASS_TEXT_SIGNATURE: () = ();
|
||||||
|
|
|
@ -1,14 +1,17 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
exceptions::{PyAttributeError, PyNotImplementedError},
|
exceptions::{PyAttributeError, PyNotImplementedError, PyValueError},
|
||||||
ffi,
|
ffi,
|
||||||
impl_::freelist::FreeList,
|
impl_::freelist::FreeList,
|
||||||
impl_::pycell::{GetBorrowChecker, PyClassMutability},
|
impl_::pycell::{GetBorrowChecker, PyClassMutability},
|
||||||
|
internal_tricks::extract_c_string,
|
||||||
pycell::PyCellLayout,
|
pycell::PyCellLayout,
|
||||||
pyclass_init::PyObjectInit,
|
pyclass_init::PyObjectInit,
|
||||||
type_object::PyLayout,
|
type_object::PyLayout,
|
||||||
Py, PyAny, PyCell, PyClass, PyErr, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo, Python,
|
Py, PyAny, PyCell, PyClass, PyErr, PyMethodDefType, PyNativeType, PyResult, PyTypeInfo, Python,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
ffi::{CStr, CString},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
os::raw::{c_int, c_void},
|
os::raw::{c_int, c_void},
|
||||||
ptr::NonNull,
|
ptr::NonNull,
|
||||||
|
@ -138,9 +141,6 @@ unsafe impl Sync for PyClassItems {}
|
||||||
/// Users are discouraged from implementing this trait manually; it is a PyO3 implementation detail
|
/// Users are discouraged from implementing this trait manually; it is a PyO3 implementation detail
|
||||||
/// and may be changed at any time.
|
/// and may be changed at any time.
|
||||||
pub trait PyClassImpl: Sized + 'static {
|
pub trait PyClassImpl: Sized + 'static {
|
||||||
/// Class doc string
|
|
||||||
const DOC: &'static str = "\0";
|
|
||||||
|
|
||||||
/// #[pyclass(subclass)]
|
/// #[pyclass(subclass)]
|
||||||
const IS_BASETYPE: bool = false;
|
const IS_BASETYPE: bool = false;
|
||||||
|
|
||||||
|
@ -184,12 +184,16 @@ pub trait PyClassImpl: Sized + 'static {
|
||||||
#[cfg(feature = "multiple-pymethods")]
|
#[cfg(feature = "multiple-pymethods")]
|
||||||
type Inventory: PyClassInventory;
|
type Inventory: PyClassInventory;
|
||||||
|
|
||||||
|
/// Rendered class doc
|
||||||
|
fn doc(py: Python<'_>) -> PyResult<&'static CStr>;
|
||||||
|
|
||||||
fn items_iter() -> PyClassItemsIter;
|
fn items_iter() -> PyClassItemsIter;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn dict_offset() -> Option<ffi::Py_ssize_t> {
|
fn dict_offset() -> Option<ffi::Py_ssize_t> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn weaklist_offset() -> Option<ffi::Py_ssize_t> {
|
fn weaklist_offset() -> Option<ffi::Py_ssize_t> {
|
||||||
None
|
None
|
||||||
|
@ -198,6 +202,29 @@ pub trait PyClassImpl: Sized + 'static {
|
||||||
fn lazy_type_object() -> &'static LazyTypeObject<Self>;
|
fn lazy_type_object() -> &'static LazyTypeObject<Self>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runtime helper to build a class docstring from the `doc` and `text_signature`.
|
||||||
|
///
|
||||||
|
/// This is done at runtime because the class text signature is collected via dtolnay
|
||||||
|
/// specialization in to the `#[pyclass]` macro from the `#[pymethods]` macro.
|
||||||
|
pub fn build_pyclass_doc(
|
||||||
|
class_name: &'static str,
|
||||||
|
doc: &'static str,
|
||||||
|
text_signature: Option<&'static str>,
|
||||||
|
) -> PyResult<Cow<'static, CStr>> {
|
||||||
|
if let Some(text_signature) = text_signature {
|
||||||
|
let doc = CString::new(format!(
|
||||||
|
"{}{}\n--\n\n{}",
|
||||||
|
class_name,
|
||||||
|
text_signature,
|
||||||
|
doc.trim_end_matches('\0')
|
||||||
|
))
|
||||||
|
.map_err(|_| PyValueError::new_err("class doc cannot contain nul bytes"))?;
|
||||||
|
Ok(Cow::Owned(doc))
|
||||||
|
} else {
|
||||||
|
extract_c_string(doc, "class doc cannot contain nul bytes")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Iterator used to process all class items during type instantiation.
|
/// Iterator used to process all class items during type instantiation.
|
||||||
pub struct PyClassItemsIter {
|
pub struct PyClassItemsIter {
|
||||||
/// Iteration state
|
/// Iteration state
|
||||||
|
@ -840,6 +867,18 @@ impl<T> PyMethods<T> for &'_ PyClassImplCollector<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Text signature for __new__
|
||||||
|
pub trait PyClassNewTextSignature<T> {
|
||||||
|
fn new_text_signature(self) -> Option<&'static str>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> PyClassNewTextSignature<T> for &'_ PyClassImplCollector<T> {
|
||||||
|
#[inline]
|
||||||
|
fn new_text_signature(self) -> Option<&'static str> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Thread checkers
|
// Thread checkers
|
||||||
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::exceptions::PyValueError;
|
use crate::internal_tricks::extract_c_string;
|
||||||
use crate::{ffi, IntoPy, Py, PyAny, PyErr, PyObject, PyResult, PyTraverseError, Python};
|
use crate::{ffi, IntoPy, Py, PyAny, PyErr, PyObject, PyResult, PyTraverseError, Python};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::ffi::{CStr, CString};
|
use std::ffi::CStr;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::os::raw::c_int;
|
use std::os::raw::c_int;
|
||||||
|
|
||||||
|
@ -319,25 +319,3 @@ where
|
||||||
self.map(|o| o.into_py(py))
|
self.map(|o| o.into_py(py))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extract_c_string(src: &'static str, err_msg: &'static str) -> PyResult<Cow<'static, CStr>> {
|
|
||||||
let bytes = src.as_bytes();
|
|
||||||
let cow = match bytes {
|
|
||||||
[] => {
|
|
||||||
// Empty string, we can trivially refer to a static "\0" string
|
|
||||||
Cow::Borrowed(unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") })
|
|
||||||
}
|
|
||||||
[.., 0] => {
|
|
||||||
// Last byte is a nul; try to create as a CStr
|
|
||||||
let c_str =
|
|
||||||
CStr::from_bytes_with_nul(bytes).map_err(|_| PyValueError::new_err(err_msg))?;
|
|
||||||
Cow::Borrowed(c_str)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
// Allocate a new CString for this
|
|
||||||
let c_string = CString::new(bytes).map_err(|_| PyValueError::new_err(err_msg))?;
|
|
||||||
Cow::Owned(c_string)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(cow)
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,4 +1,13 @@
|
||||||
use crate::ffi::{Py_ssize_t, PY_SSIZE_T_MAX};
|
use std::{
|
||||||
|
borrow::Cow,
|
||||||
|
ffi::{CStr, CString},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
exceptions::PyValueError,
|
||||||
|
ffi::{Py_ssize_t, PY_SSIZE_T_MAX},
|
||||||
|
PyResult,
|
||||||
|
};
|
||||||
pub struct PrivateMarker;
|
pub struct PrivateMarker;
|
||||||
|
|
||||||
macro_rules! private_decl {
|
macro_rules! private_decl {
|
||||||
|
@ -178,3 +187,28 @@ pub(crate) fn slice_end_index_len_fail(index: usize, ty_name: &str, len: usize)
|
||||||
pub(crate) fn slice_index_order_fail(index: usize, end: usize) -> ! {
|
pub(crate) fn slice_index_order_fail(index: usize, end: usize) -> ! {
|
||||||
panic!("slice index starts at {} but ends at {}", index, end);
|
panic!("slice index starts at {} but ends at {}", index, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn extract_c_string(
|
||||||
|
src: &'static str,
|
||||||
|
err_msg: &'static str,
|
||||||
|
) -> PyResult<Cow<'static, CStr>> {
|
||||||
|
let bytes = src.as_bytes();
|
||||||
|
let cow = match bytes {
|
||||||
|
[] => {
|
||||||
|
// Empty string, we can trivially refer to a static "\0" string
|
||||||
|
Cow::Borrowed(unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") })
|
||||||
|
}
|
||||||
|
[.., 0] => {
|
||||||
|
// Last byte is a nul; try to create as a CStr
|
||||||
|
let c_str =
|
||||||
|
CStr::from_bytes_with_nul(bytes).map_err(|_| PyValueError::new_err(err_msg))?;
|
||||||
|
Cow::Borrowed(c_str)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Allocate a new CString for this
|
||||||
|
let c_string = CString::new(bytes).map_err(|_| PyValueError::new_err(err_msg))?;
|
||||||
|
Cow::Owned(c_string)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Ok(cow)
|
||||||
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ where
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
PyTypeBuilder::default()
|
PyTypeBuilder::default()
|
||||||
.type_doc(T::DOC)
|
.type_doc(T::doc(py)?)
|
||||||
.offsets(T::dict_offset(), T::weaklist_offset())
|
.offsets(T::dict_offset(), T::weaklist_offset())
|
||||||
.slot(ffi::Py_tp_base, T::BaseType::type_object_raw(py))
|
.slot(ffi::Py_tp_base, T::BaseType::type_object_raw(py))
|
||||||
.slot(ffi::Py_tp_dealloc, tp_dealloc::<T> as *mut c_void)
|
.slot(ffi::Py_tp_dealloc, tp_dealloc::<T> as *mut c_void)
|
||||||
|
@ -233,14 +233,14 @@ impl PyTypeBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn type_doc(mut self, type_doc: &'static str) -> Self {
|
fn type_doc(mut self, type_doc: &'static CStr) -> Self {
|
||||||
if let Some(doc) = py_class_doc(type_doc) {
|
let slice = type_doc.to_bytes();
|
||||||
unsafe { self.push_slot(ffi::Py_tp_doc, doc) }
|
if !slice.is_empty() {
|
||||||
}
|
unsafe { self.push_slot(ffi::Py_tp_doc, type_doc.as_ptr() as *mut c_char) }
|
||||||
|
|
||||||
// Running this causes PyPy to segfault.
|
// Running this causes PyPy to segfault.
|
||||||
#[cfg(all(not(PyPy), not(Py_LIMITED_API), not(Py_3_10)))]
|
#[cfg(all(not(PyPy), not(Py_LIMITED_API), not(Py_3_10)))]
|
||||||
if type_doc != "\0" {
|
{
|
||||||
// Until CPython 3.10, tp_doc was treated specially for
|
// Until CPython 3.10, tp_doc was treated specially for
|
||||||
// heap-types, and it removed the text_signature value from it.
|
// heap-types, and it removed the text_signature value from it.
|
||||||
// We go in after the fact and replace tp_doc with something
|
// We go in after the fact and replace tp_doc with something
|
||||||
|
@ -248,11 +248,12 @@ impl PyTypeBuilder {
|
||||||
self.cleanup
|
self.cleanup
|
||||||
.push(Box::new(move |_self, type_object| unsafe {
|
.push(Box::new(move |_self, type_object| unsafe {
|
||||||
ffi::PyObject_Free((*type_object).tp_doc as _);
|
ffi::PyObject_Free((*type_object).tp_doc as _);
|
||||||
let data = ffi::PyObject_Malloc(type_doc.len());
|
let data = ffi::PyMem_Malloc(slice.len());
|
||||||
data.copy_from(type_doc.as_ptr() as _, type_doc.len());
|
data.copy_from(slice.as_ptr() as _, slice.len());
|
||||||
(*type_object).tp_doc = data as _;
|
(*type_object).tp_doc = data as _;
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -383,24 +384,6 @@ impl PyTypeBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn py_class_doc(class_doc: &str) -> Option<*mut c_char> {
|
|
||||||
match class_doc {
|
|
||||||
"\0" => None,
|
|
||||||
s => {
|
|
||||||
// To pass *mut pointer to python safely, leak a CString in whichever case
|
|
||||||
let cstring = if s.as_bytes().last() == Some(&0) {
|
|
||||||
CStr::from_bytes_with_nul(s.as_bytes())
|
|
||||||
.unwrap_or_else(|e| panic!("doc contains interior nul byte: {:?} in {}", e, s))
|
|
||||||
.to_owned()
|
|
||||||
} else {
|
|
||||||
CString::new(s)
|
|
||||||
.unwrap_or_else(|e| panic!("doc contains interior nul byte: {:?} in {}", e, s))
|
|
||||||
};
|
|
||||||
Some(cstring.into_raw())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn py_class_qualified_name(module_name: Option<&str>, class_name: &str) -> PyResult<*mut c_char> {
|
fn py_class_qualified_name(module_name: Option<&str>, class_name: &str) -> PyResult<*mut c_char> {
|
||||||
Ok(CString::new(format!(
|
Ok(CString::new(format!(
|
||||||
"{}.{}",
|
"{}.{}",
|
||||||
|
|
|
@ -36,18 +36,14 @@ fn class_with_docs() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(all(Py_LIMITED_API, not(Py_3_10)), ignore)]
|
#[cfg_attr(all(Py_LIMITED_API, not(Py_3_10)), ignore)]
|
||||||
fn class_with_docs_and_signature() {
|
fn class_with_signature_no_doc() {
|
||||||
/// docs line1
|
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
/// docs line2
|
|
||||||
#[pyo3(text_signature = "(a, b=None, *, c=42)")]
|
|
||||||
/// docs line3
|
|
||||||
struct MyClass {}
|
struct MyClass {}
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl MyClass {
|
impl MyClass {
|
||||||
#[new]
|
#[new]
|
||||||
#[pyo3(signature = (a, b=None, *, c=42))]
|
#[pyo3(signature = (a, b=None, *, c=42), text_signature = "(a, b=None, *, c=42)")]
|
||||||
fn __new__(a: i32, b: Option<i32>, c: i32) -> Self {
|
fn __new__(a: i32, b: Option<i32>, c: i32) -> Self {
|
||||||
let _ = (a, b, c);
|
let _ = (a, b, c);
|
||||||
Self {}
|
Self {}
|
||||||
|
@ -56,12 +52,7 @@ fn class_with_docs_and_signature() {
|
||||||
|
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
let typeobj = py.get_type::<MyClass>();
|
let typeobj = py.get_type::<MyClass>();
|
||||||
|
py_assert!(py, typeobj, "typeobj.__doc__ == ''");
|
||||||
py_assert!(
|
|
||||||
py,
|
|
||||||
typeobj,
|
|
||||||
"typeobj.__doc__ == 'docs line1\\ndocs line2\\ndocs line3'"
|
|
||||||
);
|
|
||||||
py_assert!(
|
py_assert!(
|
||||||
py,
|
py,
|
||||||
typeobj,
|
typeobj,
|
||||||
|
@ -72,15 +63,16 @@ fn class_with_docs_and_signature() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg_attr(all(Py_LIMITED_API, not(Py_3_10)), ignore)]
|
#[cfg_attr(all(Py_LIMITED_API, not(Py_3_10)), ignore)]
|
||||||
fn class_with_signature() {
|
fn class_with_docs_and_signature() {
|
||||||
|
/// docs line1
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[pyo3(text_signature = "(a, b=None, *, c=42)")]
|
/// docs line2
|
||||||
struct MyClass {}
|
struct MyClass {}
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl MyClass {
|
impl MyClass {
|
||||||
#[new]
|
#[new]
|
||||||
#[pyo3(signature = (a, b=None, *, c=42))]
|
#[pyo3(signature = (a, b=None, *, c=42), text_signature = "(a, b=None, *, c=42)")]
|
||||||
fn __new__(a: i32, b: Option<i32>, c: i32) -> Self {
|
fn __new__(a: i32, b: Option<i32>, c: i32) -> Self {
|
||||||
let _ = (a, b, c);
|
let _ = (a, b, c);
|
||||||
Self {}
|
Self {}
|
||||||
|
@ -90,11 +82,7 @@ fn class_with_signature() {
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
let typeobj = py.get_type::<MyClass>();
|
let typeobj = py.get_type::<MyClass>();
|
||||||
|
|
||||||
py_assert!(
|
py_assert!(py, typeobj, "typeobj.__doc__ == 'docs line1\\ndocs line2'");
|
||||||
py,
|
|
||||||
typeobj,
|
|
||||||
"typeobj.__doc__ is None or typeobj.__doc__ == ''"
|
|
||||||
);
|
|
||||||
py_assert!(
|
py_assert!(
|
||||||
py,
|
py,
|
||||||
typeobj,
|
typeobj,
|
||||||
|
@ -209,6 +197,12 @@ fn test_auto_test_signature_method() {
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl MyClass {
|
impl MyClass {
|
||||||
|
#[new]
|
||||||
|
fn new(a: i32, b: i32, c: i32) -> Self {
|
||||||
|
let _ = (a, b, c);
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
|
||||||
fn method(&self, a: i32, b: i32, c: i32) {
|
fn method(&self, a: i32, b: i32, c: i32) {
|
||||||
let _ = (a, b, c);
|
let _ = (a, b, c);
|
||||||
}
|
}
|
||||||
|
@ -244,6 +238,7 @@ fn test_auto_test_signature_method() {
|
||||||
|
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
let cls = py.get_type::<MyClass>();
|
let cls = py.get_type::<MyClass>();
|
||||||
|
py_assert!(py, cls, "cls.__text_signature__ == '(a, b, c)'");
|
||||||
py_assert!(
|
py_assert!(
|
||||||
py,
|
py,
|
||||||
cls,
|
cls,
|
||||||
|
@ -289,6 +284,13 @@ fn test_auto_test_signature_opt_out() {
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl MyClass {
|
impl MyClass {
|
||||||
|
#[new]
|
||||||
|
#[pyo3(text_signature = None)]
|
||||||
|
fn new(a: i32, b: i32, c: i32) -> Self {
|
||||||
|
let _ = (a, b, c);
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
|
||||||
#[pyo3(text_signature = None)]
|
#[pyo3(text_signature = None)]
|
||||||
fn method(&self, a: i32, b: i32, c: i32) {
|
fn method(&self, a: i32, b: i32, c: i32) {
|
||||||
let _ = (a, b, c);
|
let _ = (a, b, c);
|
||||||
|
@ -320,6 +322,7 @@ fn test_auto_test_signature_opt_out() {
|
||||||
py_assert!(py, f, "f.__text_signature__ == None");
|
py_assert!(py, f, "f.__text_signature__ == None");
|
||||||
|
|
||||||
let cls = py.get_type::<MyClass>();
|
let cls = py.get_type::<MyClass>();
|
||||||
|
py_assert!(py, cls, "cls.__text_signature__ == None");
|
||||||
py_assert!(py, cls, "cls.method.__text_signature__ == None");
|
py_assert!(py, cls, "cls.method.__text_signature__ == None");
|
||||||
py_assert!(py, cls, "cls.method_2.__text_signature__ == None");
|
py_assert!(py, cls, "cls.method_2.__text_signature__ == None");
|
||||||
py_assert!(py, cls, "cls.staticmethod.__text_signature__ == None");
|
py_assert!(py, cls, "cls.staticmethod.__text_signature__ == None");
|
||||||
|
@ -407,7 +410,6 @@ fn test_methods() {
|
||||||
#[cfg_attr(all(Py_LIMITED_API, not(Py_3_10)), ignore)]
|
#[cfg_attr(all(Py_LIMITED_API, not(Py_3_10)), ignore)]
|
||||||
fn test_raw_identifiers() {
|
fn test_raw_identifiers() {
|
||||||
#[pyclass]
|
#[pyclass]
|
||||||
#[pyo3(text_signature = "($self)")]
|
|
||||||
struct r#MyClass {}
|
struct r#MyClass {}
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
|
@ -416,14 +418,13 @@ fn test_raw_identifiers() {
|
||||||
fn new() -> MyClass {
|
fn new() -> MyClass {
|
||||||
MyClass {}
|
MyClass {}
|
||||||
}
|
}
|
||||||
#[pyo3(text_signature = "($self)")]
|
|
||||||
fn r#method(&self) {}
|
fn r#method(&self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
Python::with_gil(|py| {
|
Python::with_gil(|py| {
|
||||||
let typeobj = py.get_type::<MyClass>();
|
let typeobj = py.get_type::<MyClass>();
|
||||||
|
|
||||||
py_assert!(py, typeobj, "typeobj.__text_signature__ == '($self)'");
|
py_assert!(py, typeobj, "typeobj.__text_signature__ == '()'");
|
||||||
|
|
||||||
py_assert!(
|
py_assert!(
|
||||||
py,
|
py,
|
||||||
|
@ -432,3 +433,111 @@ fn test_raw_identifiers() {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
|
mod deprecated {
|
||||||
|
use crate::py_assert;
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(all(Py_LIMITED_API, not(Py_3_10)), ignore)]
|
||||||
|
fn class_with_docs_and_signature() {
|
||||||
|
/// docs line1
|
||||||
|
#[pyclass]
|
||||||
|
/// docs line2
|
||||||
|
#[pyo3(text_signature = "(a, b=None, *, c=42)")]
|
||||||
|
/// docs line3
|
||||||
|
struct MyClass {}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl MyClass {
|
||||||
|
#[new]
|
||||||
|
#[pyo3(signature = (a, b=None, *, c=42))]
|
||||||
|
fn __new__(a: i32, b: Option<i32>, c: i32) -> Self {
|
||||||
|
let _ = (a, b, c);
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
let typeobj = py.get_type::<MyClass>();
|
||||||
|
|
||||||
|
py_assert!(
|
||||||
|
py,
|
||||||
|
typeobj,
|
||||||
|
"typeobj.__doc__ == 'docs line1\\ndocs line2\\ndocs line3'"
|
||||||
|
);
|
||||||
|
py_assert!(
|
||||||
|
py,
|
||||||
|
typeobj,
|
||||||
|
"typeobj.__text_signature__ == '(a, b=None, *, c=42)'"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(all(Py_LIMITED_API, not(Py_3_10)), ignore)]
|
||||||
|
fn class_with_deprecated_text_signature() {
|
||||||
|
#[pyclass]
|
||||||
|
#[pyo3(text_signature = "(a, b=None, *, c=42)")]
|
||||||
|
struct MyClass {}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl MyClass {
|
||||||
|
#[new]
|
||||||
|
#[pyo3(signature = (a, b=None, *, c=42))]
|
||||||
|
fn __new__(a: i32, b: Option<i32>, c: i32) -> Self {
|
||||||
|
let _ = (a, b, c);
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
let typeobj = py.get_type::<MyClass>();
|
||||||
|
|
||||||
|
py_assert!(
|
||||||
|
py,
|
||||||
|
typeobj,
|
||||||
|
"typeobj.__doc__ is None or typeobj.__doc__ == ''"
|
||||||
|
);
|
||||||
|
py_assert!(
|
||||||
|
py,
|
||||||
|
typeobj,
|
||||||
|
"typeobj.__text_signature__ == '(a, b=None, *, c=42)'"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg_attr(all(Py_LIMITED_API, not(Py_3_10)), ignore)]
|
||||||
|
fn class_with_deprecated_text_signature_and_on_new() {
|
||||||
|
#[pyclass(text_signature = "(a, b=None, *, c=42)")]
|
||||||
|
struct MyClass {}
|
||||||
|
|
||||||
|
#[pymethods]
|
||||||
|
impl MyClass {
|
||||||
|
#[new]
|
||||||
|
#[pyo3(signature = (a, b=None, *, c=42), text_signature = "(NOT, THIS, ONE)")]
|
||||||
|
fn __new__(a: i32, b: Option<i32>, c: i32) -> Self {
|
||||||
|
let _ = (a, b, c);
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
let typeobj = py.get_type::<MyClass>();
|
||||||
|
py_assert!(
|
||||||
|
py,
|
||||||
|
typeobj,
|
||||||
|
"typeobj.__doc__ is None or typeobj.__doc__ == ''"
|
||||||
|
);
|
||||||
|
// Deprecated `#[pyclass(text_signature)]` attribute will be preferred
|
||||||
|
// for backwards-compatibility.
|
||||||
|
py_assert!(
|
||||||
|
py,
|
||||||
|
typeobj,
|
||||||
|
"typeobj.__text_signature__ == '(a, b=None, *, c=42)'"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -45,13 +45,6 @@ impl MyClass {
|
||||||
fn setter_without_receiver() {}
|
fn setter_without_receiver() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pymethods]
|
|
||||||
impl MyClass {
|
|
||||||
#[new]
|
|
||||||
#[pyo3(text_signature = "()")]
|
|
||||||
fn text_signature_on_new() {}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[pymethods]
|
#[pymethods]
|
||||||
impl MyClass {
|
impl MyClass {
|
||||||
#[pyo3(name = "__call__", text_signature = "()")]
|
#[pyo3(name = "__call__", text_signature = "()")]
|
||||||
|
|
|
@ -34,115 +34,120 @@ error: expected receiver for #[setter]
|
||||||
45 | fn setter_without_receiver() {}
|
45 | fn setter_without_receiver() {}
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: `text_signature` not allowed on `__new__`; if you want to add a signature on `__new__`, put it on the struct definition instead
|
|
||||||
--> tests/ui/invalid_pymethods.rs:51:12
|
|
||||||
|
|
|
||||||
51 | #[pyo3(text_signature = "()")]
|
|
||||||
| ^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: static method needs #[staticmethod] attribute
|
error: static method needs #[staticmethod] attribute
|
||||||
--> tests/ui/invalid_pymethods.rs:58:5
|
--> tests/ui/invalid_pymethods.rs:51:5
|
||||||
|
|
|
|
||||||
58 | fn text_signature_on_call() {}
|
51 | fn text_signature_on_call() {}
|
||||||
| ^^
|
| ^^
|
||||||
|
|
||||||
error: `text_signature` not allowed with `getter`
|
error: `text_signature` not allowed with `getter`
|
||||||
|
--> tests/ui/invalid_pymethods.rs:57:12
|
||||||
|
|
|
||||||
|
57 | #[pyo3(text_signature = "()")]
|
||||||
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: `text_signature` not allowed with `setter`
|
||||||
--> tests/ui/invalid_pymethods.rs:64:12
|
--> tests/ui/invalid_pymethods.rs:64:12
|
||||||
|
|
|
|
||||||
64 | #[pyo3(text_signature = "()")]
|
64 | #[pyo3(text_signature = "()")]
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: `text_signature` not allowed with `setter`
|
error: `text_signature` not allowed with `classattr`
|
||||||
--> tests/ui/invalid_pymethods.rs:71:12
|
--> tests/ui/invalid_pymethods.rs:71:12
|
||||||
|
|
|
|
||||||
71 | #[pyo3(text_signature = "()")]
|
71 | #[pyo3(text_signature = "()")]
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: `text_signature` not allowed with `classattr`
|
|
||||||
--> tests/ui/invalid_pymethods.rs:78:12
|
|
||||||
|
|
|
||||||
78 | #[pyo3(text_signature = "()")]
|
|
||||||
| ^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
error: expected a string literal or `None`
|
error: expected a string literal or `None`
|
||||||
--> tests/ui/invalid_pymethods.rs:84:30
|
--> tests/ui/invalid_pymethods.rs:77:30
|
||||||
|
|
|
|
||||||
84 | #[pyo3(text_signature = 1)]
|
77 | #[pyo3(text_signature = 1)]
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: `text_signature` may only be specified once
|
error: `text_signature` may only be specified once
|
||||||
--> tests/ui/invalid_pymethods.rs:91:12
|
--> tests/ui/invalid_pymethods.rs:84:12
|
||||||
|
|
|
|
||||||
91 | #[pyo3(text_signature = None)]
|
84 | #[pyo3(text_signature = None)]
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: `signature` not allowed with `getter`
|
error: `signature` not allowed with `getter`
|
||||||
|
--> tests/ui/invalid_pymethods.rs:91:12
|
||||||
|
|
|
||||||
|
91 | #[pyo3(signature = ())]
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
||||||
|
error: `signature` not allowed with `setter`
|
||||||
--> tests/ui/invalid_pymethods.rs:98:12
|
--> tests/ui/invalid_pymethods.rs:98:12
|
||||||
|
|
|
|
||||||
98 | #[pyo3(signature = ())]
|
98 | #[pyo3(signature = ())]
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^
|
||||||
|
|
||||||
error: `signature` not allowed with `setter`
|
error: `signature` not allowed with `classattr`
|
||||||
--> tests/ui/invalid_pymethods.rs:105:12
|
--> tests/ui/invalid_pymethods.rs:105:12
|
||||||
|
|
|
|
||||||
105 | #[pyo3(signature = ())]
|
105 | #[pyo3(signature = ())]
|
||||||
| ^^^^^^^^^
|
| ^^^^^^^^^
|
||||||
|
|
||||||
error: `signature` not allowed with `classattr`
|
|
||||||
--> tests/ui/invalid_pymethods.rs:112:12
|
|
||||||
|
|
|
||||||
112 | #[pyo3(signature = ())]
|
|
||||||
| ^^^^^^^^^
|
|
||||||
|
|
||||||
error: cannot specify a second method type
|
error: cannot specify a second method type
|
||||||
--> tests/ui/invalid_pymethods.rs:119:7
|
--> tests/ui/invalid_pymethods.rs:112:7
|
||||||
|
|
|
|
||||||
119 | #[staticmethod]
|
112 | #[staticmethod]
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
error: Python functions cannot have generic type parameters
|
error: Python functions cannot have generic type parameters
|
||||||
--> tests/ui/invalid_pymethods.rs:125:23
|
--> tests/ui/invalid_pymethods.rs:118:23
|
||||||
|
|
|
|
||||||
125 | fn generic_method<T>(value: T) {}
|
118 | fn generic_method<T>(value: T) {}
|
||||||
| ^
|
| ^
|
||||||
|
|
||||||
error: Python functions cannot have `impl Trait` arguments
|
error: Python functions cannot have `impl Trait` arguments
|
||||||
--> tests/ui/invalid_pymethods.rs:130:48
|
--> tests/ui/invalid_pymethods.rs:123:48
|
||||||
|
|
|
|
||||||
130 | fn impl_trait_method_first_arg(impl_trait: impl AsRef<PyAny>) {}
|
123 | fn impl_trait_method_first_arg(impl_trait: impl AsRef<PyAny>) {}
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
||||||
error: Python functions cannot have `impl Trait` arguments
|
error: Python functions cannot have `impl Trait` arguments
|
||||||
--> tests/ui/invalid_pymethods.rs:135:56
|
--> tests/ui/invalid_pymethods.rs:128:56
|
||||||
|
|
|
|
||||||
135 | fn impl_trait_method_second_arg(&self, impl_trait: impl AsRef<PyAny>) {}
|
128 | fn impl_trait_method_second_arg(&self, impl_trait: impl AsRef<PyAny>) {}
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
||||||
error: `async fn` is not yet supported for Python functions.
|
error: `async fn` is not yet supported for Python functions.
|
||||||
|
|
||||||
Additional crates such as `pyo3-asyncio` can be used to integrate async Rust and Python. For more information, see https://github.com/PyO3/pyo3/issues/1632
|
Additional crates such as `pyo3-asyncio` can be used to integrate async Rust and Python. For more information, see https://github.com/PyO3/pyo3/issues/1632
|
||||||
--> tests/ui/invalid_pymethods.rs:140:5
|
--> tests/ui/invalid_pymethods.rs:133:5
|
||||||
|
|
|
|
||||||
140 | async fn async_method(&self) {}
|
133 | async fn async_method(&self) {}
|
||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
||||||
error: `pass_module` cannot be used on Python methods
|
error: `pass_module` cannot be used on Python methods
|
||||||
--> tests/ui/invalid_pymethods.rs:145:12
|
--> tests/ui/invalid_pymethods.rs:138:12
|
||||||
|
|
|
|
||||||
145 | #[pyo3(pass_module)]
|
138 | #[pyo3(pass_module)]
|
||||||
| ^^^^^^^^^^^
|
| ^^^^^^^^^^^
|
||||||
|
|
||||||
error: Python objects are shared, so 'self' cannot be moved out of the Python interpreter.
|
error: Python objects are shared, so 'self' cannot be moved out of the Python interpreter.
|
||||||
Try `&self`, `&mut self, `slf: PyRef<'_, Self>` or `slf: PyRefMut<'_, Self>`.
|
Try `&self`, `&mut self, `slf: PyRef<'_, Self>` or `slf: PyRefMut<'_, Self>`.
|
||||||
--> tests/ui/invalid_pymethods.rs:151:29
|
--> tests/ui/invalid_pymethods.rs:144:29
|
||||||
|
|
|
|
||||||
151 | fn method_self_by_value(self) {}
|
144 | fn method_self_by_value(self) {}
|
||||||
| ^^^^
|
| ^^^^
|
||||||
|
|
||||||
error[E0592]: duplicate definitions with name `__pymethod___new____`
|
error[E0119]: conflicting implementations of trait `pyo3::impl_::pyclass::PyClassNewTextSignature<TwoNew>` for type `pyo3::impl_::pyclass::PyClassImplCollector<TwoNew>`
|
||||||
--> tests/ui/invalid_pymethods.rs:156:1
|
--> tests/ui/invalid_pymethods.rs:149:1
|
||||||
|
|
|
|
||||||
156 | #[pymethods]
|
149 | #[pymethods]
|
||||||
|
| ^^^^^^^^^^^^
|
||||||
|
| |
|
||||||
|
| first implementation here
|
||||||
|
| conflicting implementation for `pyo3::impl_::pyclass::PyClassImplCollector<TwoNew>`
|
||||||
|
|
|
||||||
|
= note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error[E0592]: duplicate definitions with name `__pymethod___new____`
|
||||||
|
--> tests/ui/invalid_pymethods.rs:149:1
|
||||||
|
|
|
||||||
|
149 | #[pymethods]
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
| |
|
| |
|
||||||
| duplicate definitions for `__pymethod___new____`
|
| duplicate definitions for `__pymethod___new____`
|
||||||
|
@ -151,9 +156,9 @@ error[E0592]: duplicate definitions with name `__pymethod___new____`
|
||||||
= note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0592]: duplicate definitions with name `__pymethod_func__`
|
error[E0592]: duplicate definitions with name `__pymethod_func__`
|
||||||
--> tests/ui/invalid_pymethods.rs:171:1
|
--> tests/ui/invalid_pymethods.rs:164:1
|
||||||
|
|
|
|
||||||
171 | #[pymethods]
|
164 | #[pymethods]
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
| |
|
| |
|
||||||
| duplicate definitions for `__pymethod_func__`
|
| duplicate definitions for `__pymethod_func__`
|
||||||
|
|
Loading…
Reference in New Issue