2784: Automatically generate `__text_signature__` for all functions r=davidhewitt a=davidhewitt This PR makes it so that PyO3 generates `__text_signature__` by default for all functions. It also introduces `#[pyo3(text_signature = false)]` to disable the built-in generation. There are a few limitations which we can improve later: - All default values are currently set to `...`. I think this is ok because `.pyi` files often do the same. Maybe for numbers, strings, `None` and `True`/`False` we could render these in a future PR. - No support for `#[new]` yet. Alternative design ideas: - Only autogenerate for methods with `#[pyo3(signature = (...))]` annotation. I started with this, and then decided it made sense to do it for everything. - Opt-out with `#[pyo3(text_signature = None)]`. This is slightly harder to parse in the macro, but matches the final result in Python better, so if this looks preferable to others, I can change from `text_signature = false` to `text_signature = None`. There's some small tidying up / refactoring to do before this merges (happy to take suggestions on this), however the general logic, design and docs are ready for review. 2827: pypy: enable `PyList::get_item_unchecked` r=adamreichold a=davidhewitt Split out from #2826. Approved previously as part of that review. Co-authored-by: David Hewitt <1939362+davidhewitt@users.noreply.github.com>
This commit is contained in:
commit
e5ae4e266b
|
@ -73,39 +73,7 @@ The `#[pyo3]` attribute can be used to modify properties of the generated Python
|
|||
|
||||
- <a name="text_signature"></a> `#[pyo3(text_signature = "...")]`
|
||||
|
||||
Sets the function signature visible in Python tooling (such as via [`inspect.signature`]).
|
||||
|
||||
The example below creates a function `add` which has a signature describing two positional-only
|
||||
arguments `a` and `b`.
|
||||
|
||||
```rust
|
||||
use pyo3::prelude::*;
|
||||
|
||||
/// This function adds two unsigned 64-bit integers.
|
||||
#[pyfunction]
|
||||
#[pyo3(text_signature = "(a, b, /)")]
|
||||
fn add(a: u64, b: u64) -> u64 {
|
||||
a + b
|
||||
}
|
||||
#
|
||||
# fn main() -> PyResult<()> {
|
||||
# Python::with_gil(|py| {
|
||||
# let fun = pyo3::wrap_pyfunction!(add, py)?;
|
||||
#
|
||||
# let doc: String = fun.getattr("__doc__")?.extract()?;
|
||||
# assert_eq!(doc, "This function adds two unsigned 64-bit integers.");
|
||||
#
|
||||
# let inspect = PyModule::import(py, "inspect")?.getattr("signature")?;
|
||||
# let sig: String = inspect
|
||||
# .call1((fun,))?
|
||||
# .call_method0("__str__")?
|
||||
# .extract()?;
|
||||
# assert_eq!(sig, "(a, b, /)");
|
||||
#
|
||||
# Ok(())
|
||||
# })
|
||||
# }
|
||||
```
|
||||
Overrides the PyO3-generated function signature visible in Python tooling (such as via [`inspect.signature`]). See the [corresponding topic in the Function Signatures subchapter](./function/signature.md#making-the-function-signature-available-to-python).
|
||||
|
||||
- <a name="pass_module" ></a> `#[pyo3(pass_module)]`
|
||||
|
||||
|
@ -161,47 +129,6 @@ The `#[pyo3]` attribute can be used on individual arguments to modify properties
|
|||
|
||||
## Advanced function patterns
|
||||
|
||||
### Making the function signature available to Python (old method)
|
||||
|
||||
Alternatively, simply make sure the first line of your docstring is
|
||||
formatted like in the following example. Please note that the newline after the
|
||||
`--` is mandatory. The `/` signifies the end of positional-only arguments.
|
||||
|
||||
`#[pyo3(text_signature)]` should be preferred, since it will override automatically
|
||||
generated signatures when those are added in a future version of PyO3.
|
||||
|
||||
```rust
|
||||
# #![allow(dead_code)]
|
||||
use pyo3::prelude::*;
|
||||
|
||||
/// add(a, b, /)
|
||||
/// --
|
||||
///
|
||||
/// This function adds two unsigned 64-bit integers.
|
||||
#[pyfunction]
|
||||
fn add(a: u64, b: u64) -> u64 {
|
||||
a + b
|
||||
}
|
||||
|
||||
// a function with a signature but without docs. Both blank lines after the `--` are mandatory.
|
||||
|
||||
/// sub(a, b, /)
|
||||
/// --
|
||||
#[pyfunction]
|
||||
fn sub(a: u64, b: u64) -> u64 {
|
||||
a - b
|
||||
}
|
||||
```
|
||||
|
||||
When annotated like this, signatures are also correctly displayed in IPython.
|
||||
|
||||
```text
|
||||
>>> pyo3_test.add?
|
||||
Signature: pyo3_test.add(a, b, /)
|
||||
Docstring: This function adds two unsigned 64-bit integers.
|
||||
Type: builtin_function_or_method
|
||||
```
|
||||
|
||||
### Calling Python functions in Rust
|
||||
|
||||
You can pass Python `def`'d functions and built-in functions to Rust functions [`PyFunction`]
|
||||
|
|
|
@ -188,3 +188,138 @@ impl MyClass {
|
|||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Making the function signature available to Python
|
||||
|
||||
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:
|
||||
- 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.)
|
||||
|
||||
The example below creates a function `add` which accepts two positional-only arguments `a` and `b`, where `b` has a default value of zero.
|
||||
|
||||
```rust
|
||||
use pyo3::prelude::*;
|
||||
|
||||
/// This function adds two unsigned 64-bit integers.
|
||||
#[pyfunction]
|
||||
#[pyo3(signature = (a, b=0, /))]
|
||||
fn add(a: u64, b: u64) -> u64 {
|
||||
a + b
|
||||
}
|
||||
#
|
||||
# fn main() -> PyResult<()> {
|
||||
# Python::with_gil(|py| {
|
||||
# let fun = pyo3::wrap_pyfunction!(add, py)?;
|
||||
#
|
||||
# let doc: String = fun.getattr("__doc__")?.extract()?;
|
||||
# assert_eq!(doc, "This function adds two unsigned 64-bit integers.");
|
||||
#
|
||||
# let inspect = PyModule::import(py, "inspect")?.getattr("signature")?;
|
||||
# let sig: String = inspect
|
||||
# .call1((fun,))?
|
||||
# .call_method0("__str__")?
|
||||
# .extract()?;
|
||||
#
|
||||
# #[cfg(Py_3_8)] // on 3.7 the signature doesn't render b, upstream bug?
|
||||
# assert_eq!(sig, "(a, b=Ellipsis, /)");
|
||||
#
|
||||
# Ok(())
|
||||
# })
|
||||
# }
|
||||
```
|
||||
|
||||
The following IPython output demonstrates how this generated signature will be seen from Python tooling:
|
||||
|
||||
```text
|
||||
>>> pyo3_test.add.__text_signature__
|
||||
'(a, b=..., /)'
|
||||
>>> pyo3_test.add?
|
||||
Signature: pyo3_test.add(a, b=Ellipsis, /)
|
||||
Docstring: This function adds two unsigned 64-bit integers.
|
||||
Type: builtin_function_or_method
|
||||
```
|
||||
|
||||
### Overriding the generated signature
|
||||
|
||||
The `#[pyo3(text_signature = "(<some signature>)")]` attribute can be used to override the default generated signature.
|
||||
|
||||
In the snippet below, the text signature attribute is used to include the default value of `0` for the argument `b`, instead of the automatically-generated default value of `...`:
|
||||
|
||||
```rust
|
||||
use pyo3::prelude::*;
|
||||
|
||||
/// This function adds two unsigned 64-bit integers.
|
||||
#[pyfunction]
|
||||
#[pyo3(signature = (a, b=0, /), text_signature = "(a, b=0, /)")]
|
||||
fn add(a: u64, b: u64) -> u64 {
|
||||
a + b
|
||||
}
|
||||
#
|
||||
# fn main() -> PyResult<()> {
|
||||
# Python::with_gil(|py| {
|
||||
# let fun = pyo3::wrap_pyfunction!(add, py)?;
|
||||
#
|
||||
# let doc: String = fun.getattr("__doc__")?.extract()?;
|
||||
# assert_eq!(doc, "This function adds two unsigned 64-bit integers.");
|
||||
#
|
||||
# let inspect = PyModule::import(py, "inspect")?.getattr("signature")?;
|
||||
# let sig: String = inspect
|
||||
# .call1((fun,))?
|
||||
# .call_method0("__str__")?
|
||||
# .extract()?;
|
||||
# assert_eq!(sig, "(a, b=0, /)");
|
||||
#
|
||||
# Ok(())
|
||||
# })
|
||||
# }
|
||||
```
|
||||
|
||||
PyO3 will include the contents of the annotation unmodified as the `__text_signature`. Below shows how IPython will now present this (see the default value of 0 for b):
|
||||
|
||||
```text
|
||||
>>> pyo3_test.add.__text_signature__
|
||||
'(a, b=0, /)'
|
||||
>>> pyo3_test.add?
|
||||
Signature: pyo3_test.add(a, b=0, /)
|
||||
Docstring: This function adds two unsigned 64-bit integers.
|
||||
Type: builtin_function_or_method
|
||||
```
|
||||
|
||||
If no signature is wanted at all, `#[pyo3(text_signature = None)]` will disable the built-in signature. The snippet below demonstrates use of this:
|
||||
|
||||
```rust
|
||||
use pyo3::prelude::*;
|
||||
|
||||
/// This function adds two unsigned 64-bit integers.
|
||||
#[pyfunction]
|
||||
#[pyo3(signature = (a, b=0, /), text_signature = None)]
|
||||
fn add(a: u64, b: u64) -> u64 {
|
||||
a + b
|
||||
}
|
||||
#
|
||||
# fn main() -> PyResult<()> {
|
||||
# Python::with_gil(|py| {
|
||||
# let fun = pyo3::wrap_pyfunction!(add, py)?;
|
||||
#
|
||||
# let doc: String = fun.getattr("__doc__")?.extract()?;
|
||||
# assert_eq!(doc, "This function adds two unsigned 64-bit integers.");
|
||||
# assert!(fun.getattr("__text_signature__")?.is_none());
|
||||
#
|
||||
# Ok(())
|
||||
# })
|
||||
# }
|
||||
```
|
||||
|
||||
Now the function's `__text_signature__` will be set to `None`, and IPython will not display any signature in the help:
|
||||
|
||||
```text
|
||||
>>> pyo3_test.add.__text_signature__ == None
|
||||
True
|
||||
>>> pyo3_test.add?
|
||||
Docstring: This function adds two unsigned 64-bit integers.
|
||||
Type: builtin_function_or_method
|
||||
```
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Automatically generate `__text_signature__` for all Python functions created using `#[pyfunction]` and `#[pymethods]`.
|
|
@ -0,0 +1 @@
|
|||
Add `PyList::get_item_unchecked` for PyPy.
|
|
@ -81,11 +81,46 @@ impl ToTokens for NameLitStr {
|
|||
}
|
||||
}
|
||||
|
||||
/// Text signatue can be either a literal string or opt-in/out
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum TextSignatureAttributeValue {
|
||||
Str(LitStr),
|
||||
// `None` ident to disable automatic text signature generation
|
||||
Disabled(Ident),
|
||||
}
|
||||
|
||||
impl Parse for TextSignatureAttributeValue {
|
||||
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||
if let Ok(lit_str) = input.parse::<LitStr>() {
|
||||
return Ok(TextSignatureAttributeValue::Str(lit_str));
|
||||
}
|
||||
|
||||
let err_span = match input.parse::<Ident>() {
|
||||
Ok(ident) if ident == "None" => {
|
||||
return Ok(TextSignatureAttributeValue::Disabled(ident));
|
||||
}
|
||||
Ok(other_ident) => other_ident.span(),
|
||||
Err(e) => e.span(),
|
||||
};
|
||||
|
||||
Err(err_spanned!(err_span => "expected a string literal or `None`"))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for TextSignatureAttributeValue {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
TextSignatureAttributeValue::Str(s) => s.to_tokens(tokens),
|
||||
TextSignatureAttributeValue::Disabled(b) => b.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type ExtendsAttribute = KeywordAttribute<kw::extends, Path>;
|
||||
pub type FreelistAttribute = KeywordAttribute<kw::freelist, Box<Expr>>;
|
||||
pub type ModuleAttribute = KeywordAttribute<kw::module, LitStr>;
|
||||
pub type NameAttribute = KeywordAttribute<kw::name, NameLitStr>;
|
||||
pub type TextSignatureAttribute = KeywordAttribute<kw::text_signature, LitStr>;
|
||||
pub type TextSignatureAttribute = KeywordAttribute<kw::text_signature, TextSignatureAttributeValue>;
|
||||
|
||||
impl<K: Parse + std::fmt::Debug, V: Parse> Parse for KeywordAttribute<K, V> {
|
||||
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||
|
|
|
@ -5,7 +5,9 @@ use std::borrow::Cow;
|
|||
use crate::attributes::TextSignatureAttribute;
|
||||
use crate::deprecations::{Deprecation, Deprecations};
|
||||
use crate::params::impl_arg_params;
|
||||
use crate::pyfunction::{DeprecatedArgs, FunctionSignature, PyFunctionArgPyO3Attributes};
|
||||
use crate::pyfunction::{
|
||||
text_signature_or_auto, DeprecatedArgs, FunctionSignature, PyFunctionArgPyO3Attributes,
|
||||
};
|
||||
use crate::pyfunction::{PyFunctionOptions, SignatureAttribute};
|
||||
use crate::utils::{self, PythonDoc};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
|
@ -202,7 +204,7 @@ impl CallingConvention {
|
|||
pub fn from_signature(signature: &FunctionSignature<'_>) -> Self {
|
||||
if signature.python_signature.has_no_args() {
|
||||
Self::Noargs
|
||||
} else if signature.python_signature.accepts_kwargs {
|
||||
} else if signature.python_signature.kwargs.is_some() {
|
||||
// for functions that accept **kwargs, always prefer varargs
|
||||
Self::Varargs
|
||||
} else if cfg!(not(feature = "abi3")) {
|
||||
|
@ -288,13 +290,6 @@ impl<'a> FnSpec<'a> {
|
|||
let ty = get_return_info(&sig.output);
|
||||
let python_name = python_name.as_ref().unwrap_or(name).unraw();
|
||||
|
||||
let doc = utils::get_doc(
|
||||
meth_attrs,
|
||||
text_signature
|
||||
.as_ref()
|
||||
.map(|attr| (Cow::Borrowed(&python_name), attr)),
|
||||
);
|
||||
|
||||
let arguments: Vec<_> = if skip_first_arg {
|
||||
sig.inputs
|
||||
.iter_mut()
|
||||
|
@ -320,6 +315,16 @@ impl<'a> FnSpec<'a> {
|
|||
FunctionSignature::from_arguments(arguments)
|
||||
};
|
||||
|
||||
let text_signature_string = match &fn_type {
|
||||
FnType::FnNew | FnType::Getter(_) | FnType::Setter(_) | FnType::ClassAttribute => None,
|
||||
_ => text_signature_or_auto(text_signature.as_ref(), &signature, &fn_type),
|
||||
};
|
||||
|
||||
let doc = utils::get_doc(
|
||||
meth_attrs,
|
||||
text_signature_string.map(|sig| (Cow::Borrowed(&python_name), sig)),
|
||||
);
|
||||
|
||||
let convention =
|
||||
fixed_convention.unwrap_or_else(|| CallingConvention::from_signature(&signature));
|
||||
|
||||
|
|
|
@ -82,12 +82,12 @@ pub fn impl_arg_params(
|
|||
.map(|arg| impl_arg_param(arg, &mut option_pos, py, &args_array))
|
||||
.collect::<Result<_>>()?;
|
||||
|
||||
let args_handler = if spec.signature.python_signature.accepts_varargs {
|
||||
let args_handler = if spec.signature.python_signature.varargs.is_some() {
|
||||
quote! { _pyo3::impl_::extract_argument::TupleVarargs }
|
||||
} else {
|
||||
quote! { _pyo3::impl_::extract_argument::NoVarargs }
|
||||
};
|
||||
let kwargs_handler = if spec.signature.python_signature.accepts_kwargs {
|
||||
let kwargs_handler = if spec.signature.python_signature.kwargs.is_some() {
|
||||
quote! { _pyo3::impl_::extract_argument::DictVarkeywords }
|
||||
} else {
|
||||
quote! { _pyo3::impl_::extract_argument::NoVarkeywords }
|
||||
|
|
|
@ -9,6 +9,7 @@ use crate::attributes::{
|
|||
use crate::deprecations::{Deprecation, Deprecations};
|
||||
use crate::konst::{ConstAttributes, ConstSpec};
|
||||
use crate::method::FnSpec;
|
||||
use crate::pyfunction::text_signature_or_none;
|
||||
use crate::pyimpl::{gen_py_const, PyClassMethodsType};
|
||||
use crate::pymethod::{
|
||||
impl_py_getter_def, impl_py_setter_def, MethodAndMethodDef, MethodAndSlotDef, PropertyType,
|
||||
|
@ -198,12 +199,10 @@ pub fn build_py_class(
|
|||
methods_type: PyClassMethodsType,
|
||||
) -> syn::Result<TokenStream> {
|
||||
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,
|
||||
args.options
|
||||
.text_signature
|
||||
.as_ref()
|
||||
.map(|attr| (get_class_python_name(&class.ident, &args), attr)),
|
||||
text_signature_string.map(|s| (get_class_python_name(&class.ident, &args), s)),
|
||||
);
|
||||
let krate = get_pyo3_crate(&args.options.krate);
|
||||
|
||||
|
@ -461,12 +460,11 @@ pub fn build_py_enum(
|
|||
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,
|
||||
args.options
|
||||
.text_signature
|
||||
.as_ref()
|
||||
.map(|attr| (get_class_python_name(&enum_.ident, &args), attr)),
|
||||
text_signature_string.map(|s| (get_class_python_name(&enum_.ident, &args), s)),
|
||||
);
|
||||
let enum_ = PyClassEnum::new(enum_)?;
|
||||
impl_enum(enum_, &args, doc, method_type)
|
||||
|
|
|
@ -5,10 +5,10 @@ use std::borrow::Cow;
|
|||
use crate::{
|
||||
attributes::{
|
||||
self, get_pyo3_options, take_attributes, take_pyo3_options, CrateAttribute,
|
||||
FromPyWithAttribute, NameAttribute, TextSignatureAttribute,
|
||||
FromPyWithAttribute, NameAttribute, TextSignatureAttribute, TextSignatureAttributeValue,
|
||||
},
|
||||
deprecations::{Deprecation, Deprecations},
|
||||
method::{self, CallingConvention, FnArg},
|
||||
method::{self, CallingConvention, FnArg, FnType},
|
||||
pymethod::check_generic,
|
||||
utils::{self, ensure_not_async_fn, get_pyo3_crate},
|
||||
};
|
||||
|
@ -409,11 +409,11 @@ pub fn impl_wrap_pyfunction(
|
|||
|
||||
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
|
||||
.as_ref()
|
||||
.map(|attr| (Cow::Borrowed(&python_name), attr)),
|
||||
text_signature_string.map(|s| (Cow::Borrowed(&python_name), s)),
|
||||
);
|
||||
|
||||
let krate = get_pyo3_crate(&krate);
|
||||
|
@ -480,3 +480,26 @@ fn type_is_pymodule(ty: &syn::Type) -> bool {
|
|||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use syn::{
|
|||
|
||||
use crate::{
|
||||
attributes::{kw, KeywordAttribute},
|
||||
method::FnArg,
|
||||
method::{FnArg, FnType},
|
||||
pyfunction::Argument,
|
||||
};
|
||||
|
||||
|
@ -205,18 +205,18 @@ pub struct PythonSignature {
|
|||
pub positional_parameters: Vec<String>,
|
||||
pub positional_only_parameters: usize,
|
||||
pub required_positional_parameters: usize,
|
||||
pub accepts_varargs: bool,
|
||||
pub varargs: Option<String>,
|
||||
// Tuples of keyword name and whether it is required
|
||||
pub keyword_only_parameters: Vec<(String, bool)>,
|
||||
pub accepts_kwargs: bool,
|
||||
pub kwargs: Option<String>,
|
||||
}
|
||||
|
||||
impl PythonSignature {
|
||||
pub fn has_no_args(&self) -> bool {
|
||||
self.positional_parameters.is_empty()
|
||||
&& self.keyword_only_parameters.is_empty()
|
||||
&& !self.accepts_varargs
|
||||
&& !self.accepts_kwargs
|
||||
&& self.varargs.is_none()
|
||||
&& self.kwargs.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -232,9 +232,9 @@ pub enum ParseState {
|
|||
/// Accepting positional parameters after '/'
|
||||
PositionalAfterPosargs,
|
||||
/// Accepting keyword-only parameters after '*' or '*args'
|
||||
Keywords(Option<String>),
|
||||
Keywords,
|
||||
/// After `**kwargs` nothing is allowed
|
||||
Done(String),
|
||||
Done,
|
||||
}
|
||||
|
||||
impl ParseState {
|
||||
|
@ -257,12 +257,12 @@ impl ParseState {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
ParseState::Keywords(_) => {
|
||||
ParseState::Keywords => {
|
||||
signature.keyword_only_parameters.push((name, required));
|
||||
Ok(())
|
||||
}
|
||||
ParseState::Done(s) => {
|
||||
bail_spanned!(span => format!("no more arguments are allowed after `**{}`", s))
|
||||
ParseState::Done => {
|
||||
bail_spanned!(span => format!("no more arguments are allowed after `**{}`", signature.kwargs.as_deref().unwrap_or("")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -274,15 +274,15 @@ impl ParseState {
|
|||
) -> syn::Result<()> {
|
||||
match self {
|
||||
ParseState::Positional | ParseState::PositionalAfterPosargs => {
|
||||
signature.accepts_varargs = true;
|
||||
*self = ParseState::Keywords(Some(varargs.ident.to_string()));
|
||||
signature.varargs = Some(varargs.ident.to_string());
|
||||
*self = ParseState::Keywords;
|
||||
Ok(())
|
||||
}
|
||||
ParseState::Keywords(s) => {
|
||||
bail_spanned!(varargs.span() => format!("`*{}` not allowed after `*{}`", varargs.ident, s.as_deref().unwrap_or("")))
|
||||
ParseState::Keywords => {
|
||||
bail_spanned!(varargs.span() => format!("`*{}` not allowed after `*{}`", varargs.ident, signature.varargs.as_deref().unwrap_or("")))
|
||||
}
|
||||
ParseState::Done(s) => {
|
||||
bail_spanned!(varargs.span() => format!("`*{}` not allowed after `**{}`", varargs.ident, s))
|
||||
ParseState::Done => {
|
||||
bail_spanned!(varargs.span() => format!("`*{}` not allowed after `**{}`", varargs.ident, signature.kwargs.as_deref().unwrap_or("")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -293,15 +293,13 @@ impl ParseState {
|
|||
kwargs: &SignatureItemKwargs,
|
||||
) -> syn::Result<()> {
|
||||
match self {
|
||||
ParseState::Positional
|
||||
| ParseState::PositionalAfterPosargs
|
||||
| ParseState::Keywords(_) => {
|
||||
signature.accepts_kwargs = true;
|
||||
*self = ParseState::Done(kwargs.ident.to_string());
|
||||
ParseState::Positional | ParseState::PositionalAfterPosargs | ParseState::Keywords => {
|
||||
signature.kwargs = Some(kwargs.ident.to_string());
|
||||
*self = ParseState::Done;
|
||||
Ok(())
|
||||
}
|
||||
ParseState::Done(s) => {
|
||||
bail_spanned!(kwargs.span() => format!("`**{}` not allowed after `**{}`", kwargs.ident, s))
|
||||
ParseState::Done => {
|
||||
bail_spanned!(kwargs.span() => format!("`**{}` not allowed after `**{}`", kwargs.ident, signature.kwargs.as_deref().unwrap_or("")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -320,26 +318,26 @@ impl ParseState {
|
|||
ParseState::PositionalAfterPosargs => {
|
||||
bail_spanned!(span => "`/` not allowed after `/`")
|
||||
}
|
||||
ParseState::Keywords(s) => {
|
||||
bail_spanned!(span => format!("`/` not allowed after `*{}`", s.as_deref().unwrap_or("")))
|
||||
ParseState::Keywords => {
|
||||
bail_spanned!(span => format!("`/` not allowed after `*{}`", signature.varargs.as_deref().unwrap_or("")))
|
||||
}
|
||||
ParseState::Done(s) => {
|
||||
bail_spanned!(span => format!("`/` not allowed after `**{}`", s))
|
||||
ParseState::Done => {
|
||||
bail_spanned!(span => format!("`/` not allowed after `**{}`", signature.kwargs.as_deref().unwrap_or("")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn finish_pos_args(&mut self, span: Span) -> syn::Result<()> {
|
||||
fn finish_pos_args(&mut self, signature: &PythonSignature, span: Span) -> syn::Result<()> {
|
||||
match self {
|
||||
ParseState::Positional | ParseState::PositionalAfterPosargs => {
|
||||
*self = ParseState::Keywords(None);
|
||||
*self = ParseState::Keywords;
|
||||
Ok(())
|
||||
}
|
||||
ParseState::Keywords(s) => {
|
||||
bail_spanned!(span => format!("`*` not allowed after `*{}`", s.as_deref().unwrap_or("")))
|
||||
ParseState::Keywords => {
|
||||
bail_spanned!(span => format!("`*` not allowed after `*{}`", signature.varargs.as_deref().unwrap_or("")))
|
||||
}
|
||||
ParseState::Done(s) => {
|
||||
bail_spanned!(span => format!("`*` not allowed after `**{}`", s))
|
||||
ParseState::Done => {
|
||||
bail_spanned!(span => format!("`*` not allowed after `**{}`", signature.kwargs.as_deref().unwrap_or("")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -386,7 +384,9 @@ impl<'a> FunctionSignature<'a> {
|
|||
fn_arg.default = Some(default.clone());
|
||||
}
|
||||
}
|
||||
SignatureItem::VarargsSep(sep) => parse_state.finish_pos_args(sep.span())?,
|
||||
SignatureItem::VarargsSep(sep) => {
|
||||
parse_state.finish_pos_args(&python_signature, sep.span())?
|
||||
}
|
||||
SignatureItem::Varargs(varargs) => {
|
||||
let fn_arg = next_argument_checked(&varargs.ident)?;
|
||||
fn_arg.is_varargs = true;
|
||||
|
@ -423,8 +423,8 @@ impl<'a> FunctionSignature<'a> {
|
|||
mut arguments: Vec<FnArg<'a>>,
|
||||
deprecated_args: DeprecatedArgs,
|
||||
) -> syn::Result<Self> {
|
||||
let mut accepts_varargs = false;
|
||||
let mut accepts_kwargs = false;
|
||||
let mut varargs = None;
|
||||
let mut kwargs = None;
|
||||
let mut keyword_only_parameters = Vec::new();
|
||||
|
||||
fn first_n_argument_names(arguments: &[FnArg<'_>], count: usize) -> Vec<String> {
|
||||
|
@ -481,13 +481,21 @@ impl<'a> FunctionSignature<'a> {
|
|||
}
|
||||
Argument::PosOnlyArgsSeparator => {}
|
||||
Argument::VarArgsSeparator => {}
|
||||
Argument::VarArgs(_) => {
|
||||
Argument::VarArgs(path) => {
|
||||
fn_arg.is_varargs = true;
|
||||
accepts_varargs = true;
|
||||
if let Some(ident) = path.get_ident() {
|
||||
varargs = Some(ident.to_string());
|
||||
} else {
|
||||
bail_spanned!(path.span() => "expected ident for *args");
|
||||
};
|
||||
}
|
||||
Argument::KeywordArgs(_) => {
|
||||
Argument::KeywordArgs(path) => {
|
||||
fn_arg.is_kwargs = true;
|
||||
accepts_kwargs = true;
|
||||
if let Some(ident) = path.get_ident() {
|
||||
kwargs = Some(ident.to_string());
|
||||
} else {
|
||||
bail_spanned!(path.span() => "expected ident for **kwargs");
|
||||
};
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -513,9 +521,9 @@ impl<'a> FunctionSignature<'a> {
|
|||
positional_parameters,
|
||||
positional_only_parameters,
|
||||
required_positional_parameters,
|
||||
accepts_varargs,
|
||||
varargs,
|
||||
keyword_only_parameters,
|
||||
accepts_kwargs,
|
||||
kwargs,
|
||||
},
|
||||
attribute: None,
|
||||
})
|
||||
|
@ -556,4 +564,80 @@ impl<'a> FunctionSignature<'a> {
|
|||
attribute: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn text_signature(&self, fn_type: &FnType) -> 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();
|
||||
output.push('(');
|
||||
|
||||
if let Some(arg) = self_argument {
|
||||
output.push('$');
|
||||
output.push_str(arg);
|
||||
}
|
||||
|
||||
let mut maybe_push_comma = {
|
||||
let mut first = self_argument.is_none();
|
||||
move |output: &mut String| {
|
||||
if !first {
|
||||
output.push_str(", ");
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let py_sig = &self.python_signature;
|
||||
|
||||
for (i, parameter) in py_sig.positional_parameters.iter().enumerate() {
|
||||
maybe_push_comma(&mut output);
|
||||
|
||||
output.push_str(parameter);
|
||||
|
||||
if i >= py_sig.required_positional_parameters {
|
||||
// has a default, just use ... for now
|
||||
output.push_str("=...");
|
||||
}
|
||||
|
||||
if py_sig.positional_only_parameters > 0 && i + 1 == py_sig.positional_only_parameters {
|
||||
output.push_str(", /")
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(varargs) = &py_sig.varargs {
|
||||
maybe_push_comma(&mut output);
|
||||
output.push('*');
|
||||
output.push_str(varargs);
|
||||
} else if !py_sig.keyword_only_parameters.is_empty() {
|
||||
maybe_push_comma(&mut output);
|
||||
output.push('*');
|
||||
}
|
||||
|
||||
for (parameter, required) in &py_sig.keyword_only_parameters {
|
||||
maybe_push_comma(&mut output);
|
||||
output.push_str(parameter);
|
||||
if !required {
|
||||
// has a default, just use ... for now
|
||||
output.push_str("=...")
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(kwargs) = &py_sig.kwargs {
|
||||
maybe_push_comma(&mut output);
|
||||
output.push_str("**");
|
||||
output.push_str(kwargs);
|
||||
}
|
||||
|
||||
output.push(')');
|
||||
output
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use proc_macro2::{Span, TokenStream};
|
|||
use quote::ToTokens;
|
||||
use syn::{spanned::Spanned, Ident};
|
||||
|
||||
use crate::attributes::{CrateAttribute, TextSignatureAttribute};
|
||||
use crate::attributes::CrateAttribute;
|
||||
|
||||
/// Macro inspired by `anyhow::anyhow!` to create a compiler error with the given span.
|
||||
macro_rules! err_spanned {
|
||||
|
@ -68,7 +68,7 @@ pub struct PythonDoc(TokenStream);
|
|||
/// e.g. concat!("...", "\n", "\0")
|
||||
pub fn get_doc(
|
||||
attrs: &[syn::Attribute],
|
||||
text_signature: Option<(Cow<'_, Ident>, &TextSignatureAttribute)>,
|
||||
text_signature: Option<(Cow<'_, Ident>, String)>,
|
||||
) -> PythonDoc {
|
||||
let mut tokens = TokenStream::new();
|
||||
let comma = syn::token::Comma(Span::call_site());
|
||||
|
@ -79,8 +79,7 @@ pub fn get_doc(
|
|||
syn::token::Bracket(Span::call_site()).surround(&mut tokens, |tokens| {
|
||||
if let Some((python_name, text_signature)) = text_signature {
|
||||
// create special doc string lines to set `__text_signature__`
|
||||
let signature_lines =
|
||||
format!("{}{}\n--\n\n", python_name, text_signature.value.value());
|
||||
let signature_lines = format!("{}{}\n--\n\n", python_name, text_signature);
|
||||
signature_lines.to_tokens(tokens);
|
||||
comma.to_tokens(tokens);
|
||||
}
|
||||
|
|
|
@ -142,7 +142,7 @@ impl PyList {
|
|||
/// # Safety
|
||||
///
|
||||
/// Caller must verify that the index is within the bounds of the list.
|
||||
#[cfg(not(any(Py_LIMITED_API, PyPy)))]
|
||||
#[cfg(not(Py_LIMITED_API))]
|
||||
pub unsafe fn get_item_unchecked(&self, index: usize) -> &PyAny {
|
||||
let item = ffi::PyList_GET_ITEM(self.as_ptr(), index as Py_ssize_t);
|
||||
// PyList_GET_ITEM return borrowed ptr; must make owned for safety (see #890).
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#![cfg(feature = "macros")]
|
||||
|
||||
use pyo3::prelude::*;
|
||||
use pyo3::types::{PyDict, PyTuple};
|
||||
use pyo3::{types::PyType, wrap_pymodule, PyCell};
|
||||
|
||||
mod common;
|
||||
|
@ -117,6 +118,179 @@ fn test_function() {
|
|||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auto_test_signature_function() {
|
||||
#[pyfunction]
|
||||
fn my_function(a: i32, b: Option<i32>, c: i32) {
|
||||
let _ = (a, b, c);
|
||||
}
|
||||
|
||||
#[pyfunction(pass_module)]
|
||||
fn my_function_2(module: &PyModule, a: i32, b: Option<i32>, c: i32) {
|
||||
let _ = (module, a, b, c);
|
||||
}
|
||||
|
||||
#[pyfunction(signature = (a, /, b = None, *, c = 5))]
|
||||
fn my_function_3(a: i32, b: Option<i32>, c: i32) {
|
||||
let _ = (a, b, c);
|
||||
}
|
||||
|
||||
#[pyfunction(signature = (a, /, b = None, *args, c, d=5, **kwargs))]
|
||||
fn my_function_4(
|
||||
a: i32,
|
||||
b: Option<i32>,
|
||||
args: &PyTuple,
|
||||
c: i32,
|
||||
d: i32,
|
||||
kwargs: Option<&PyDict>,
|
||||
) {
|
||||
let _ = (a, b, args, c, d, kwargs);
|
||||
}
|
||||
|
||||
Python::with_gil(|py| {
|
||||
let f = wrap_pyfunction!(my_function)(py).unwrap();
|
||||
py_assert!(py, f, "f.__text_signature__ == '(a, b, c)'");
|
||||
|
||||
let f = wrap_pyfunction!(my_function_2)(py).unwrap();
|
||||
py_assert!(py, f, "f.__text_signature__ == '($module, a, b, c)'");
|
||||
|
||||
let f = wrap_pyfunction!(my_function_3)(py).unwrap();
|
||||
py_assert!(py, f, "f.__text_signature__ == '(a, /, b=..., *, c=...)'");
|
||||
|
||||
let f = wrap_pyfunction!(my_function_4)(py).unwrap();
|
||||
py_assert!(
|
||||
py,
|
||||
f,
|
||||
"f.__text_signature__ == '(a, /, b=..., *args, c, d=..., **kwargs)'"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auto_test_signature_method() {
|
||||
#[pyclass]
|
||||
struct MyClass {}
|
||||
|
||||
#[pymethods]
|
||||
impl MyClass {
|
||||
fn method(&self, a: i32, b: Option<i32>, c: i32) {
|
||||
let _ = (a, b, c);
|
||||
}
|
||||
|
||||
#[pyo3(signature = (a, /, b = None, *, c = 5))]
|
||||
fn method_2(&self, a: i32, b: Option<i32>, c: i32) {
|
||||
let _ = (a, b, c);
|
||||
}
|
||||
|
||||
#[pyo3(signature = (a, /, b = None, *args, c, d=5, **kwargs))]
|
||||
fn method_3(
|
||||
&self,
|
||||
a: i32,
|
||||
b: Option<i32>,
|
||||
args: &PyTuple,
|
||||
c: i32,
|
||||
d: i32,
|
||||
kwargs: Option<&PyDict>,
|
||||
) {
|
||||
let _ = (a, b, args, c, d, kwargs);
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
fn staticmethod(a: i32, b: Option<i32>, c: i32) {
|
||||
let _ = (a, b, c);
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
fn classmethod(cls: &PyType, a: i32, b: Option<i32>, c: i32) {
|
||||
let _ = (cls, a, b, c);
|
||||
}
|
||||
}
|
||||
|
||||
Python::with_gil(|py| {
|
||||
let cls = py.get_type::<MyClass>();
|
||||
py_assert!(
|
||||
py,
|
||||
cls,
|
||||
"cls.method.__text_signature__ == '($self, a, b, c)'"
|
||||
);
|
||||
py_assert!(
|
||||
py,
|
||||
cls,
|
||||
"cls.method_2.__text_signature__ == '($self, a, /, b=..., *, c=...)'"
|
||||
);
|
||||
py_assert!(
|
||||
py,
|
||||
cls,
|
||||
"cls.method_3.__text_signature__ == '($self, a, /, b=..., *args, c, d=..., **kwargs)'"
|
||||
);
|
||||
py_assert!(
|
||||
py,
|
||||
cls,
|
||||
"cls.staticmethod.__text_signature__ == '(a, b, c)'"
|
||||
);
|
||||
py_assert!(
|
||||
py,
|
||||
cls,
|
||||
"cls.classmethod.__text_signature__ == '($cls, a, b, c)'"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_auto_test_signature_opt_out() {
|
||||
#[pyfunction(text_signature = None)]
|
||||
fn my_function(a: i32, b: Option<i32>, c: i32) {
|
||||
let _ = (a, b, c);
|
||||
}
|
||||
|
||||
#[pyfunction(signature = (a, /, b = None, *, c = 5), text_signature = None)]
|
||||
fn my_function_2(a: i32, b: Option<i32>, c: i32) {
|
||||
let _ = (a, b, c);
|
||||
}
|
||||
|
||||
#[pyclass]
|
||||
struct MyClass {}
|
||||
|
||||
#[pymethods]
|
||||
impl MyClass {
|
||||
#[pyo3(text_signature = None)]
|
||||
fn method(&self, a: i32, b: Option<i32>, c: i32) {
|
||||
let _ = (a, b, c);
|
||||
}
|
||||
|
||||
#[pyo3(signature = (a, /, b = None, *, c = 5), text_signature = None)]
|
||||
fn method_2(&self, a: i32, b: Option<i32>, c: i32) {
|
||||
let _ = (a, b, c);
|
||||
}
|
||||
|
||||
#[staticmethod]
|
||||
#[pyo3(text_signature = None)]
|
||||
fn staticmethod(a: i32, b: Option<i32>, c: i32) {
|
||||
let _ = (a, b, c);
|
||||
}
|
||||
|
||||
#[classmethod]
|
||||
#[pyo3(text_signature = None)]
|
||||
fn classmethod(cls: &PyType, a: i32, b: Option<i32>, c: i32) {
|
||||
let _ = (cls, a, b, c);
|
||||
}
|
||||
}
|
||||
|
||||
Python::with_gil(|py| {
|
||||
let f = wrap_pyfunction!(my_function)(py).unwrap();
|
||||
py_assert!(py, f, "f.__text_signature__ == None");
|
||||
|
||||
let f = wrap_pyfunction!(my_function_2)(py).unwrap();
|
||||
py_assert!(py, f, "f.__text_signature__ == None");
|
||||
|
||||
let cls = py.get_type::<MyClass>();
|
||||
py_assert!(py, cls, "cls.method.__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.classmethod.__text_signature__ == None");
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_pyfn() {
|
||||
#[pymodule]
|
||||
|
|
|
@ -79,6 +79,19 @@ impl MyClass {
|
|||
fn text_signature_on_classattr() {}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl MyClass {
|
||||
#[pyo3(text_signature = 1)]
|
||||
fn invalid_text_signature() {}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl MyClass {
|
||||
#[pyo3(text_signature = "()")]
|
||||
#[pyo3(text_signature = None)]
|
||||
fn duplicate_text_signature() {}
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
impl MyClass {
|
||||
#[getter(x)]
|
||||
|
|
|
@ -64,73 +64,85 @@ error: `text_signature` not allowed with `classattr`
|
|||
78 | #[pyo3(text_signature = "()")]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: `signature` not allowed with `getter`
|
||||
--> tests/ui/invalid_pymethods.rs:85:12
|
||||
error: expected a string literal or `None`
|
||||
--> tests/ui/invalid_pymethods.rs:84:30
|
||||
|
|
||||
85 | #[pyo3(signature = ())]
|
||||
84 | #[pyo3(text_signature = 1)]
|
||||
| ^
|
||||
|
||||
error: `text_signature` may only be specified once
|
||||
--> tests/ui/invalid_pymethods.rs:91:12
|
||||
|
|
||||
91 | #[pyo3(text_signature = None)]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
||||
error: `signature` not allowed with `getter`
|
||||
--> tests/ui/invalid_pymethods.rs:98:12
|
||||
|
|
||||
98 | #[pyo3(signature = ())]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `signature` not allowed with `setter`
|
||||
--> tests/ui/invalid_pymethods.rs:92:12
|
||||
|
|
||||
92 | #[pyo3(signature = ())]
|
||||
| ^^^^^^^^^
|
||||
--> tests/ui/invalid_pymethods.rs:105:12
|
||||
|
|
||||
105 | #[pyo3(signature = ())]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: `signature` not allowed with `classattr`
|
||||
--> tests/ui/invalid_pymethods.rs:99:12
|
||||
|
|
||||
99 | #[pyo3(signature = ())]
|
||||
| ^^^^^^^^^
|
||||
--> tests/ui/invalid_pymethods.rs:112:12
|
||||
|
|
||||
112 | #[pyo3(signature = ())]
|
||||
| ^^^^^^^^^
|
||||
|
||||
error: cannot specify a second method type
|
||||
--> tests/ui/invalid_pymethods.rs:106:7
|
||||
--> tests/ui/invalid_pymethods.rs:119:7
|
||||
|
|
||||
106 | #[staticmethod]
|
||||
119 | #[staticmethod]
|
||||
| ^^^^^^^^^^^^
|
||||
|
||||
error: Python functions cannot have generic type parameters
|
||||
--> tests/ui/invalid_pymethods.rs:112:23
|
||||
--> tests/ui/invalid_pymethods.rs:125:23
|
||||
|
|
||||
112 | fn generic_method<T>(value: T) {}
|
||||
125 | fn generic_method<T>(value: T) {}
|
||||
| ^
|
||||
|
||||
error: Python functions cannot have `impl Trait` arguments
|
||||
--> tests/ui/invalid_pymethods.rs:117:48
|
||||
--> tests/ui/invalid_pymethods.rs:130:48
|
||||
|
|
||||
117 | fn impl_trait_method_first_arg(impl_trait: impl AsRef<PyAny>) {}
|
||||
130 | fn impl_trait_method_first_arg(impl_trait: impl AsRef<PyAny>) {}
|
||||
| ^^^^
|
||||
|
||||
error: Python functions cannot have `impl Trait` arguments
|
||||
--> tests/ui/invalid_pymethods.rs:122:56
|
||||
--> tests/ui/invalid_pymethods.rs:135:56
|
||||
|
|
||||
122 | fn impl_trait_method_second_arg(&self, impl_trait: impl AsRef<PyAny>) {}
|
||||
135 | fn impl_trait_method_second_arg(&self, impl_trait: impl AsRef<PyAny>) {}
|
||||
| ^^^^
|
||||
|
||||
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
|
||||
--> tests/ui/invalid_pymethods.rs:127:5
|
||||
--> tests/ui/invalid_pymethods.rs:140:5
|
||||
|
|
||||
127 | async fn async_method(&self) {}
|
||||
140 | async fn async_method(&self) {}
|
||||
| ^^^^^
|
||||
|
||||
error: `pass_module` cannot be used on Python methods
|
||||
--> tests/ui/invalid_pymethods.rs:132:12
|
||||
--> tests/ui/invalid_pymethods.rs:145:12
|
||||
|
|
||||
132 | #[pyo3(pass_module)]
|
||||
145 | #[pyo3(pass_module)]
|
||||
| ^^^^^^^^^^^
|
||||
|
||||
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>`.
|
||||
--> tests/ui/invalid_pymethods.rs:138:29
|
||||
--> tests/ui/invalid_pymethods.rs:151:29
|
||||
|
|
||||
138 | fn method_self_by_value(self) {}
|
||||
151 | fn method_self_by_value(self) {}
|
||||
| ^^^^
|
||||
|
||||
error[E0592]: duplicate definitions with name `__pymethod___new____`
|
||||
--> tests/ui/invalid_pymethods.rs:143:1
|
||||
--> tests/ui/invalid_pymethods.rs:156:1
|
||||
|
|
||||
143 | #[pymethods]
|
||||
156 | #[pymethods]
|
||||
| ^^^^^^^^^^^^
|
||||
| |
|
||||
| duplicate definitions for `__pymethod___new____`
|
||||
|
@ -139,9 +151,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)
|
||||
|
||||
error[E0592]: duplicate definitions with name `__pymethod_func__`
|
||||
--> tests/ui/invalid_pymethods.rs:158:1
|
||||
--> tests/ui/invalid_pymethods.rs:171:1
|
||||
|
|
||||
158 | #[pymethods]
|
||||
171 | #[pymethods]
|
||||
| ^^^^^^^^^^^^
|
||||
| |
|
||||
| duplicate definitions for `__pymethod_func__`
|
||||
|
|
Loading…
Reference in New Issue