2930: add better error message for Python in signature r=adamreichold a=davidhewitt

Inspired by #2929, this just adds a better error message when `Python` arguments are accidentally included in the signature.

Co-authored-by: David Hewitt <1939362+davidhewitt@users.noreply.github.com>
This commit is contained in:
bors[bot] 2023-02-03 09:11:26 +00:00 committed by GitHub
commit 864aee0916
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 36 additions and 13 deletions

View file

@ -352,10 +352,21 @@ impl<'a> FunctionSignature<'a> {
let mut parse_state = ParseState::Positional;
let mut python_signature = PythonSignature::default();
let mut args_iter = arguments.iter_mut().filter(|arg| !arg.py); // Python<'_> arguments don't show on the Python side.
let mut args_iter = arguments.iter_mut();
let mut next_non_py_argument_checked = |name: &syn::Ident| {
for fn_arg in args_iter.by_ref() {
if fn_arg.py {
// If the user incorrectly tried to include py: Python in the
// signature, give a useful error as a hint.
ensure_spanned!(
name != fn_arg.name,
name.span() => "arguments of type `Python` must not be part of the signature"
);
// Otherwise try next argument.
continue;
}
let mut next_argument_checked = |name: &syn::Ident| match args_iter.next() {
Some(fn_arg) => {
ensure_spanned!(
name == fn_arg.name,
name.span() => format!(
@ -364,17 +375,17 @@ impl<'a> FunctionSignature<'a> {
name.unraw(),
)
);
Ok(fn_arg)
return Ok(fn_arg);
}
None => bail_spanned!(
bail_spanned!(
name.span() => "signature entry does not have a corresponding function argument"
),
)
};
for item in &attribute.value.items {
match item {
SignatureItem::Argument(arg) => {
let fn_arg = next_argument_checked(&arg.ident)?;
let fn_arg = next_non_py_argument_checked(&arg.ident)?;
parse_state.add_argument(
&mut python_signature,
arg.ident.unraw().to_string(),
@ -389,12 +400,12 @@ impl<'a> FunctionSignature<'a> {
parse_state.finish_pos_args(&python_signature, sep.span())?
}
SignatureItem::Varargs(varargs) => {
let fn_arg = next_argument_checked(&varargs.ident)?;
let fn_arg = next_non_py_argument_checked(&varargs.ident)?;
fn_arg.is_varargs = true;
parse_state.add_varargs(&mut python_signature, &varargs)?;
}
SignatureItem::Kwargs(kwargs) => {
let fn_arg = next_argument_checked(&kwargs.ident)?;
let fn_arg = next_non_py_argument_checked(&kwargs.ident)?;
fn_arg.is_kwargs = true;
parse_state.add_kwargs(&mut python_signature, &kwargs)?;
}
@ -404,7 +415,8 @@ impl<'a> FunctionSignature<'a> {
};
}
if let Some(arg) = args_iter.next() {
// Ensure no non-py arguments remain
if let Some(arg) = args_iter.find(|arg| !arg.py) {
bail_spanned!(
attribute.kw.span() => format!("missing signature entry for argument `{}`", arg.name)
);

View file

@ -44,6 +44,11 @@ fn function_with_kwargs_after_kwargs(kwargs_a: Option<&PyDict>, kwargs_b: Option
let _ = kwargs_b;
}
#[pyfunction(signature = (py))]
fn signature_contains_py(py: Python<'_>) {
let _ = py;
}
#[pyclass]
struct MyClass;

View file

@ -46,8 +46,14 @@ error: `**kwargs_b` not allowed after `**kwargs_a`
41 | #[pyo3(signature = (**kwargs_a, **kwargs_b))]
| ^
error: cannot define both function signature and legacy arguments
--> tests/ui/invalid_pyfunction_signatures.rs:53:12
error: arguments of type `Python` must not be part of the signature
--> tests/ui/invalid_pyfunction_signatures.rs:47:27
|
53 | #[pyo3(signature = (x))]
47 | #[pyfunction(signature = (py))]
| ^^
error: cannot define both function signature and legacy arguments
--> tests/ui/invalid_pyfunction_signatures.rs:58:12
|
58 | #[pyo3(signature = (x))]
| ^^^^^^^^^