|
2 | 2 |
|
3 | 3 | The `#[pyfunction]` attribute also accepts parameters to control how the generated Python function accepts arguments. Just like in Python, arguments can be positional-only, keyword-only, or accept either. `*args` lists and `**kwargs` dicts can also be accepted. These parameters also work for `#[pymethods]` which will be introduced in the [Python Classes](../class.md) section of the guide. |
4 | 4 |
|
5 | | -Like Python, by default PyO3 accepts all arguments as either positional or keyword arguments. Most arguments are required by default, except for trailing `Option<_>` arguments, which are [implicitly given a default of `None`](#trailing-optional-arguments). This behaviour can be configured by the `#[pyo3(signature = (...))]` option which allows writing a signature in Python syntax. |
| 5 | +Like Python, by default PyO3 accepts all arguments as either positional or keyword arguments. All arguments are required by default. This behaviour can be configured by the `#[pyo3(signature = (...))]` option which allows writing a signature in Python syntax. |
6 | 6 |
|
7 | 7 | This section of the guide goes into detail about use of the `#[pyo3(signature = (...))]` option and its related option `#[pyo3(text_signature = "...")]` |
8 | 8 |
|
@@ -118,82 +118,6 @@ num=-1 |
118 | 118 | > } |
119 | 119 | > ``` |
120 | 120 |
|
121 | | -## Trailing optional arguments |
122 | | -
|
123 | | -<div class="warning"> |
124 | | -
|
125 | | -⚠️ Warning: This behaviour is being phased out 🛠️ |
126 | | -
|
127 | | -The special casing of trailing optional arguments is deprecated. In a future `pyo3` version, arguments of type `Option<..>` will share the same behaviour as other arguments, they are required unless a default is set using `#[pyo3(signature = (...))]`. |
128 | | -
|
129 | | -This is done to better align the Python and Rust definition of such functions and make it more intuitive to rewrite them from Python in Rust. Specifically `def some_fn(a: int, b: Optional[int]): ...` will not automatically default `b` to `none`, but requires an explicit default if desired, where as in current `pyo3` it is handled the other way around. |
130 | | -
|
131 | | -During the migration window a `#[pyo3(signature = (...))]` will be required to silence the deprecation warning. After support for trailing optional arguments is fully removed, the signature attribute can be removed if all arguments should be required. |
132 | | -</div> |
133 | | -
|
134 | | -
|
135 | | -As a convenience, functions without a `#[pyo3(signature = (...))]` option will treat trailing `Option<T>` arguments as having a default of `None`. In the example below, PyO3 will create `increment` with a signature of `increment(x, amount=None)`. |
136 | | -
|
137 | | -```rust |
138 | | -#![allow(deprecated)] |
139 | | -use pyo3::prelude::*; |
140 | | -
|
141 | | -/// Returns a copy of `x` increased by `amount`. |
142 | | -/// |
143 | | -/// If `amount` is unspecified or `None`, equivalent to `x + 1`. |
144 | | -#[pyfunction] |
145 | | -fn increment(x: u64, amount: Option<u64>) -> u64 { |
146 | | - x + amount.unwrap_or(1) |
147 | | -} |
148 | | -# |
149 | | -# fn main() -> PyResult<()> { |
150 | | -# Python::with_gil(|py| { |
151 | | -# let fun = pyo3::wrap_pyfunction!(increment, py)?; |
152 | | -# |
153 | | -# let inspect = PyModule::import(py, "inspect")?.getattr("signature")?; |
154 | | -# let sig: String = inspect |
155 | | -# .call1((fun,))? |
156 | | -# .call_method0("__str__")? |
157 | | -# .extract()?; |
158 | | -# |
159 | | -# #[cfg(Py_3_8)] // on 3.7 the signature doesn't render b, upstream bug? |
160 | | -# assert_eq!(sig, "(x, amount=None)"); |
161 | | -# |
162 | | -# Ok(()) |
163 | | -# }) |
164 | | -# } |
165 | | -``` |
166 | | -
|
167 | | -To make trailing `Option<T>` arguments required, but still accept `None`, add a `#[pyo3(signature = (...))]` annotation. For the example above, this would be `#[pyo3(signature = (x, amount))]`: |
168 | | - |
169 | | -```rust |
170 | | -# use pyo3::prelude::*; |
171 | | -#[pyfunction] |
172 | | -#[pyo3(signature = (x, amount))] |
173 | | -fn increment(x: u64, amount: Option<u64>) -> u64 { |
174 | | - x + amount.unwrap_or(1) |
175 | | -} |
176 | | -# |
177 | | -# fn main() -> PyResult<()> { |
178 | | -# Python::with_gil(|py| { |
179 | | -# let fun = pyo3::wrap_pyfunction!(increment, py)?; |
180 | | -# |
181 | | -# let inspect = PyModule::import(py, "inspect")?.getattr("signature")?; |
182 | | -# let sig: String = inspect |
183 | | -# .call1((fun,))? |
184 | | -# .call_method0("__str__")? |
185 | | -# .extract()?; |
186 | | -# |
187 | | -# #[cfg(Py_3_8)] // on 3.7 the signature doesn't render b, upstream bug? |
188 | | -# assert_eq!(sig, "(x, amount)"); |
189 | | -# |
190 | | -# Ok(()) |
191 | | -# }) |
192 | | -# } |
193 | | -``` |
194 | | - |
195 | | -To help avoid confusion, PyO3 requires `#[pyo3(signature = (...))]` when an `Option<T>` argument is surrounded by arguments which aren't `Option<T>`. |
196 | | - |
197 | 121 | ## Making the function signature available to Python |
198 | 122 |
|
199 | 123 | 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. |
|
0 commit comments