Skip to content

Commit

Permalink
use text_signature = None opt-out
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Dec 18, 2022
1 parent 1db7302 commit 426023f
Show file tree
Hide file tree
Showing 8 changed files with 42 additions and 42 deletions.
6 changes: 3 additions & 3 deletions guide/src/function/signature.md
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ 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]`.
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.)
Expand Down Expand Up @@ -287,14 +287,14 @@ Docstring: This function adds two unsigned 64-bit integers.
Type: builtin_function_or_method
```

If no signature is wanted at all, `#[pyo3(text_signature = false)]` will disable the built-in signature. The snippet below demonstrates use of this:
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 = false)]
#[pyo3(signature = (a, b=0, /), text_signature = None)]
fn add(a: u64, b: u64) -> u64 {
a + b
}
Expand Down
1 change: 1 addition & 0 deletions newsfragments/2784.changed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Automatically generate `__text_signature__` for all Python functions created using `#[pyfunction]` and `#[pymethods]`.
26 changes: 15 additions & 11 deletions pyo3-macros-backend/src/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use syn::{
punctuated::Punctuated,
spanned::Spanned,
token::Comma,
Attribute, Expr, ExprPath, Ident, Lit, LitStr, Path, Result, Token,
Attribute, Expr, ExprPath, Ident, LitStr, Path, Result, Token,
};

pub mod kw {
Expand Down Expand Up @@ -85,27 +85,31 @@ impl ToTokens for NameLitStr {
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TextSignatureAttributeValue {
Str(LitStr),
Bool(syn::LitBool),
// `None` ident to disable automatic text signature generation
Disabled(Ident),
}

impl Parse for TextSignatureAttributeValue {
fn parse(input: ParseStream<'_>) -> Result<Self> {
let span = match input.parse::<Lit>() {
Ok(Lit::Str(lit_str)) => return Ok(TextSignatureAttributeValue::Str(lit_str)),
Ok(Lit::Bool(lit_bool)) => return Ok(TextSignatureAttributeValue::Bool(lit_bool)),
Ok(lit) => lit.span(),
Err(_) => input.span(),
};

bail_spanned!(span => "expected a string literal, `true`, or `false`")
if let Ok(lit_str) = input.parse::<LitStr>() {
return Ok(TextSignatureAttributeValue::Str(lit_str));
}

if let Ok(ident) = input.parse::<Ident>() {
if ident == "None" {
return Ok(TextSignatureAttributeValue::Disabled(ident));
}
}

Err(input.error("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::Bool(b) => b.to_tokens(tokens),
TextSignatureAttributeValue::Disabled(b) => b.to_tokens(tokens),
}
}
}
Expand Down
7 changes: 4 additions & 3 deletions pyo3-macros-backend/src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use quote::ToTokens;
use quote::{quote, quote_spanned};
use syn::ext::IdentExt;
use syn::spanned::Spanned;
use syn::{LitBool, Result};
use syn::Result;

#[derive(Clone, Debug)]
pub struct FnArg<'a> {
Expand Down Expand Up @@ -315,7 +315,8 @@ impl<'a> FnSpec<'a> {

let text_signature_string = match text_signature.as_ref().map(|attr| &attr.value) {
Some(TextSignatureAttributeValue::Str(s)) => Some(s.value()),
Some(TextSignatureAttributeValue::Bool(LitBool { value: true, .. })) | None => {
None => {
// automatic text signature generation
match &fn_type {
FnType::FnNew
| FnType::Getter(_)
Expand All @@ -327,7 +328,7 @@ impl<'a> FnSpec<'a> {
FnType::FnStatic => Some(signature.text_signature(None)),
}
}
Some(TextSignatureAttributeValue::Bool(_)) => None,
Some(TextSignatureAttributeValue::Disabled(_)) => None,
};

let doc = utils::get_doc(
Expand Down
10 changes: 2 additions & 8 deletions pyo3-macros-backend/src/pyclass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,7 @@ pub fn build_py_class(
args.options.take_pyo3_options(&mut class.attrs)?;
let text_signature_string = match args.options.text_signature.as_ref().map(|attr| &attr.value) {
Some(TextSignatureAttributeValue::Str(s)) => Some(s.value()),
Some(TextSignatureAttributeValue::Bool(b)) => {
bail_spanned!(b.span() => "text_signature cannot be `true`/`false` for `#[pyclass]")
}
None => None,
Some(TextSignatureAttributeValue::Disabled(_)) | None => None,
};
let doc = utils::get_doc(
&class.attrs,
Expand Down Expand Up @@ -468,10 +465,7 @@ pub fn build_py_enum(

let text_signature_string = match args.options.text_signature.as_ref().map(|attr| &attr.value) {
Some(TextSignatureAttributeValue::Str(s)) => Some(s.value()),
Some(TextSignatureAttributeValue::Bool(b)) => {
bail_spanned!(b.span() => "text_signature cannot be `true`/`false` for `#[pyclass]")
}
None => None,
Some(TextSignatureAttributeValue::Disabled(_)) | None => None,
};

let doc = utils::get_doc(
Expand Down
8 changes: 3 additions & 5 deletions pyo3-macros-backend/src/pyfunction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use crate::{
};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{ext::IdentExt, spanned::Spanned, LitBool, NestedMeta, Result};
use syn::{ext::IdentExt, spanned::Spanned, NestedMeta, Result};
use syn::{
parse::{Parse, ParseBuffer, ParseStream},
token::Comma,
Expand Down Expand Up @@ -411,10 +411,8 @@ pub fn impl_wrap_pyfunction(

let text_signature_string = match text_signature.as_ref().map(|attr| &attr.value) {
Some(TextSignatureAttributeValue::Str(s)) => Some(s.value()),
Some(TextSignatureAttributeValue::Bool(LitBool { value: true, .. })) | None => {
Some(signature.text_signature(self_argument))
}
Some(TextSignatureAttributeValue::Bool(_)) => None,
None => Some(signature.text_signature(self_argument)),
Some(TextSignatureAttributeValue::Disabled(_)) => None,
};

let doc = utils::get_doc(
Expand Down
14 changes: 8 additions & 6 deletions pyo3-macros-backend/src/pyfunction/signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,12 +574,14 @@ impl<'a> FunctionSignature<'a> {
output.push_str(arg);
}

let mut first = self_argument.is_none();
let mut maybe_push_comma = |output: &mut String| {
if !first {
output.push_str(", ");
} else {
first = false;
let mut maybe_push_comma = {
let mut first = self_argument.is_none();
move |output: &mut String| {
if !first {
output.push_str(", ");
} else {
first = false;
}
}
};

Expand Down
12 changes: 6 additions & 6 deletions tests/test_text_signature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,12 +230,12 @@ fn test_auto_test_signature_method() {

#[test]
fn test_auto_test_signature_opt_out() {
#[pyfunction(text_signature = false)]
#[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 = false)]
#[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);
}
Expand All @@ -245,24 +245,24 @@ fn test_auto_test_signature_opt_out() {

#[pymethods]
impl MyClass {
#[pyo3(text_signature = false)]
#[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 = false)]
#[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 = false)]
#[pyo3(text_signature = None)]
fn staticmethod(a: i32, b: Option<i32>, c: i32) {
let _ = (a, b, c);
}

#[classmethod]
#[pyo3(text_signature = false)]
#[pyo3(text_signature = None)]
fn classmethod(cls: &PyType, a: i32, b: Option<i32>, c: i32) {
let _ = (cls, a, b, c);
}
Expand Down

0 comments on commit 426023f

Please sign in to comment.