diff --git a/src/buffer.rs b/src/buffer.rs index a8c85ad5..672ab32c 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,3 +1,5 @@ +#![cfg(not(Py_LIMITED_API))] +#![cfg_attr(docsrs, doc(cfg(not(Py_LIMITED_API))))] // Copyright (c) 2017 Daniel Grunwald // // Permission is hereby granted, free of charge, to any person obtaining a copy of this diff --git a/src/conversion.rs b/src/conversion.rs index e247aa51..0463d670 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -1,6 +1,6 @@ // Copyright (c) 2017-present PyO3 Project and Contributors -//! Conversions between various states of Rust and Python types and their wrappers. +//! Defines conversions between Rust and Python types. use crate::err::{self, PyDowncastError, PyResult}; use crate::type_object::PyTypeInfo; use crate::types::PyTuple; diff --git a/src/conversions/mod.rs b/src/conversions/mod.rs index 42182379..78825a06 100644 --- a/src/conversions/mod.rs +++ b/src/conversions/mod.rs @@ -7,3 +7,4 @@ pub mod num_bigint; pub mod num_complex; mod osstr; mod path; +pub mod serde; diff --git a/src/serde.rs b/src/conversions/serde.rs similarity index 57% rename from src/serde.rs rename to src/conversions/serde.rs index 0d527c19..e6571e2f 100644 --- a/src/serde.rs +++ b/src/conversions/serde.rs @@ -1,3 +1,23 @@ +#![cfg_attr(docsrs, doc(cfg(feature = "serde")))] +#![cfg(feature = "serde")] + +//! Enables (de)serialization of [`Py`]`` objects via [serde](https://docs.rs/serde). +//! +//! # Setup +//! +//! To use this feature, add this to your **`Cargo.toml`**: +//! +//! ```toml +//! [dependencies] +//! serde = "1.0" +// workaround for `extended_key_value_attributes`: https://github.com/rust-lang/rust/issues/82768#issuecomment-803935643 +#![cfg_attr(docsrs, cfg_attr(docsrs, doc = concat!("pyo3 = { version = \"", env!("CARGO_PKG_VERSION"), "\", features = [\"serde\"] }")))] +#![cfg_attr( + not(docsrs), + doc = "pyo3 = { version = \"*\", features = [\"serde\"] }" +)] +//! ``` + use crate::{Py, PyAny, PyClass, Python}; use serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer}; diff --git a/src/ffi/pystate.rs b/src/ffi/pystate.rs index 8007fe19..e2f0d9c6 100644 --- a/src/ffi/pystate.rs +++ b/src/ffi/pystate.rs @@ -23,15 +23,15 @@ extern "C" { pub fn PyInterpreterState_Delete(arg1: *mut PyInterpreterState); #[cfg(all(Py_3_9, not(PyPy)))] - #[cfg_attr(docsrs, doc(all(Py_3_9, not(PyPy))))] + #[cfg_attr(docsrs, doc(cfg(all(Py_3_9, not(PyPy)))))] pub fn PyInterpreterState_Get() -> *mut PyInterpreterState; #[cfg(all(Py_3_8, not(PyPy)))] - #[cfg_attr(docsrs, doc(all(Py_3_8, not(PyPy))))] + #[cfg_attr(docsrs, doc(cfg(all(Py_3_8, not(PyPy)))))] pub fn PyInterpreterState_GetDict(arg1: *mut PyInterpreterState) -> *mut PyObject; #[cfg(all(Py_3_7, not(PyPy)))] - #[cfg_attr(docsrs, doc(all(Py_3_7, not(PyPy))))] + #[cfg_attr(docsrs, doc(cfg(all(Py_3_7, not(PyPy)))))] pub fn PyInterpreterState_GetID(arg1: *mut PyInterpreterState) -> i64; #[cfg(not(PyPy))] diff --git a/src/impl_.rs b/src/impl_.rs index a4bd78ff..46f5d771 100644 --- a/src/impl_.rs +++ b/src/impl_.rs @@ -1,6 +1,8 @@ -//! Internals of PyO3 which are accessed by code expanded from PyO3's procedural macros. Usage of -//! any of these APIs in downstream code is implicitly acknowledging that these APIs may change at -//! any time without documentation in the CHANGELOG and without breaking semver guarantees. +//! Internals of PyO3 which are accessed by code expanded from PyO3's procedural macros. +//! +//! Usage of any of these APIs in downstream code is implicitly acknowledging that these +//! APIs may may change at any time without documentation in the CHANGELOG and without +//! breaking semver guarantees. pub mod deprecations; pub mod freelist; diff --git a/src/lib.rs b/src/lib.rs index c67d8f40..7b91ed9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,15 @@ #![cfg_attr(feature = "nightly", feature(specialization))] #![cfg_attr(docsrs, feature(doc_cfg))] - +#![deny( + invalid_doc_attributes, + rustdoc::broken_intra_doc_links, + rustdoc::bare_urls +)] //! Rust bindings to the Python interpreter. //! //! PyO3 can be used to write native Python modules or run Python code and modules from Rust. //! -//! See [the guide](https://pyo3.rs/) for a detailed introduction. +//! See [the guide] for a detailed introduction. //! //! # PyO3's object types //! @@ -13,42 +17,38 @@ //! //! ## The Python<'py> object //! -//! Holding the [global interpreter lock](https://docs.python.org/3/glossary.html#term-global-interpreter-lock) -//! (GIL) is modeled with the [`Python<'py>`](crate::Python) token. -//! All APIs that require that the GIL is held require this token as proof -//! that you really are holding the GIL. It can be explicitly acquired and -//! is also implicitly acquired by PyO3 as it wraps Rust functions and structs -//! into Python functions and objects. +//! Holding the [global interpreter lock] (GIL) is modeled with the [`Python<'py>`] token. All APIs +//! that require that the GIL is held require this token as proof that you really are holding the +//! GIL. It can be explicitly acquired and is also implicitly acquired by PyO3 as it wraps Rust +//! functions and structs into Python functions and objects. //! //! ## The GIL-dependent types //! -//! For example `&`[`PyAny`](crate::types::PyAny). -//! These are only ever seen as references, with a lifetime that is only valid for as long -//! as the GIL is held, which is why using them doesn't require a [`Python<'py>`](crate::Python) token. -//! The underlying Python object, if mutable, can be mutated through any reference. +//! For example `&`[`PyAny`]. These are only ever seen as references, with a lifetime that is only +//! valid for as long as the GIL is held, which is why using them doesn't require a [`Python<'py>`] +//! token. The underlying Python object, if mutable, can be mutated through any reference. //! -//! See the [guide](https://pyo3.rs/latest/types.html) for an explanation of the different Python object types. +//! See the [guide][types] for an explanation of the different Python object types. //! //! ## The GIL-independent types //! -//! When wrapped in [`Py`]`<...>`, like with [`Py`]`<`[`PyAny`](crate::types::PyAny)`>` or [`Py`]``, Python objects -//! no longer have a limited lifetime which makes them easier to store in structs and pass between functions. -//! However, you cannot do much with them without a -//! [`Python<'py>`](crate::Python) token, for which you’d need to reacquire the GIL. +//! When wrapped in [`Py`]`<...>`, like with [`Py`]`<`[`PyAny`]`>` or [`Py`]``, Python +//! objects no longer have a limited lifetime which makes them easier to store in structs and pass +//! between functions. However, you cannot do much with them without a [`Python<'py>`] token, for +//! which you’d need to reacquire the GIL. //! //! ## PyErr //! //! The vast majority of operations in this library will return [`PyResult<...>`](PyResult). //! This is an alias for the type `Result<..., PyErr>`. //! -//! A `PyErr` represents a Python exception. A `PyErr` returned to Python code will be raised as a Python exception. -//! Errors from `PyO3` itself are also exposed as Python exceptions. +//! A `PyErr` represents a Python exception. A `PyErr` returned to Python code will be raised as a +//! Python exception. Errors from `PyO3` itself are also exposed as Python exceptions. //! //! # Feature flags //! -//! PyO3 uses [feature flags](https://doc.rust-lang.org/cargo/reference/features.html) -//! to enable you to opt-in to additional functionality. For a detailed description, see -//! the [Features Reference chapter of the guide](https://pyo3.rs/latest/features.html#features-reference). +//! PyO3 uses [feature flags] to enable you to opt-in to additional functionality.For a detailed +//! description, see the [Features chapter of the guide]. //! //! ## Default feature flags //! @@ -57,63 +57,44 @@ //! //! ## Optional feature flags //! -//! The following features are optional: -//! - `abi3`: Restricts PyO3's API to a subset of the full Python API which is guaranteed -//! by [PEP 384](https://www.python.org/dev/peps/pep-0384/) to be forward-compatible with future Python versions. +//! The following features customize PyO3's behavior: +//! +//! - `abi3`: Restricts PyO3's API to a subset of the full Python API which is guaranteed by +//! [PEP 384] to be forward-compatible with future Python versions. +//! - `auto-initialize`: Changes [`Python::with_gil`] and [`Python::acquire_gil`] to automatically +//! initialize the Python interpreter if needed. +//! - `extension-module`: This will tell the linker to keep the Python symbols unresolved, so that +//! your module can also be used with statically linked Python interpreters. Use this feature when +//! building an extension module. +//! - `multiple-pymethods`: Enables the use of multiple [`#[pymethods]`] blocks per [`#[pyclass]`]. +//! This adds a dependency on the [inventory][inventory] crate, which is not supported on all +//! platforms. +//! +//! The following features enable interactions with other crates in the Rust ecosystem: // -//! - `auto-initialize`: Changes [`Python::with_gil`](crate::Python::with_gil) and -//! [`Python::acquire_gil`](crate::Python::acquire_gil) to automatically initialize the -//! Python interpreter if needed. -// -//! - `extension-module`: This will tell the linker to keep the Python symbols unresolved, -//! so that your module can also be used with statically linked Python interpreters. -//! Use this feature when building an extension module. -// -//! - [`hashbrown`](./hashbrown/index.html): Enables conversions between Python objects and -//! [hashbrown](https://docs.rs/hashbrown)'s -//! [`HashMap`](https://docs.rs/hashbrown/latest/hashbrown/struct.HashMap.html) and -//! [`HashSet`](https://docs.rs/hashbrown/latest/hashbrown/struct.HashSet.html) types. -// -//! - [`indexmap`](./indexmap/index.html): Enables conversions between Python dictionary and -//! [indexmap](https://docs.rs/indexmap)'s -//! [`IndexMap`](https://docs.rs/indexmap/latest/indexmap/map/struct.IndexMap.html). -// -//! - `multiple-pymethods`: Enables the use of multiple -//! [`#[pymethods]`](crate::proc_macro::pymethods) blocks per -//! [`#[pyclass]`](crate::proc_macro::pyclass). This adds a dependency on the -//! [`inventory`](https://docs.rs/inventory) crate, which is not supported on all platforms. -// -//! - [`num-bigint`](./num_bigint/index.html): Enables conversions between Python objects and -//! [num-bigint](https://docs.rs/num-bigint)'s -//! [`BigInt`](https://docs.rs/num-bigint/latest/num_bigint/struct.BigInt.html) and -//! [`BigUint`](https://docs.rs/num-bigint/latest/num_bigint/struct.BigUint.html) types. -// -//! - [`num-complex`](./num_complex/index.html): Enables conversions between Python objects and -//! [num-complex](https://docs.rs/num-complex)'s -//! [`Complex`](https://docs.rs/num-complex/latest/num_complex/struct.Complex.html) type. -// -//! - `serde`: Allows implementing [serde](https://docs.rs/serde)'s -//! [`Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html) and -//! [`Deserialize`](https://docs.rs/serde/latest/serde/trait.Deserialize.html) traits for -//! [`Py`]`` for all `T` that implement -//! [`Serialize`](https://docs.rs/serde/latest/serde/trait.Serialize.html) and -//! [`Deserialize`](https://docs.rs/serde/latest/serde/trait.Deserialize.html). +//! - [`hashbrown`]: Enables conversions between Python objects and [hashbrown]'s [`HashMap`] and +//! [`HashSet`] types. +//! - [`indexmap`]: Enables conversions between Python dictionary and [indexmap]'s [`IndexMap`]. +//! - [`num-bigint`]: Enables conversions between Python objects and [num-bigint]'s [`BigInt`] and +//! [`BigUint`] types. +//! - [`num-complex`]: Enables conversions between Python objects and [num-complex]'s [`Complex`] +//! type. +//! - [`serde`]: Allows implementing [serde]'s [`Serialize`] and [`Deserialize`] traits for +//! [`Py`]`` for all `T` that implement [`Serialize`] and [`Deserialize`]. //! //! ## Unstable features //! -//! - `nightly`: Gates some optimizations that rely on -//! [`#![feature(specialization)]`](https://github.com/rust-lang/rfcs/blob/master/text/1210-impl-specialization.md), -//! for which you'd also need nightly Rust. You should not use this feature. +//! - `nightly`: Gates some optimizations that rely on [`#![feature(specialization)]`], for which +//! you'd also need nightly Rust. You should not use this feature. // //! ## `rustc` environment flags //! //! PyO3 uses `rustc`'s `--cfg` flags to enable or disable code used for different Python versions. -//! If you want to do this for your own crate, you can do so with the [`pyo3-build-config`](https://docs.rs/pyo3-build-config) crate. +//! If you want to do this for your own crate, you can do so with the [`pyo3-build-config`] crate. //! -//! - `Py_3_6`, `Py_3_7`, `Py_3_8`, `Py_3_9`, `Py_3_10`: Marks code that is only enabled when compiling for a given minimum Python version. -// +//! - `Py_3_6`, `Py_3_7`, `Py_3_8`, `Py_3_9`, `Py_3_10`: Marks code that is only enabled when +//! compiling for a given minimum Python version. //! - `Py_LIMITED_API`: Marks code enabled when the `abi3` feature flag is enabled. -// //! - `PyPy` - Marks code enabled when compiling for PyPy. //! //! # Minimum supported Rust and Python versions @@ -125,10 +106,9 @@ //! # Example: Building a native Python module //! //! PyO3 can be used to generate a native Python module. The easiest way to try this out for the -//! first time is to use [`maturin`](https://github.com/PyO3/maturin). `maturin` is a tool for -//! building and publishing Rust-based Python packages with minimal configuration. The following -//! steps set up some files for an example Python module, install `maturin`, and then show how build -//! and import the Python module. +//! first time is to use [`maturin`]. `maturin` is a tool for building and publishing Rust-based +//! Python packages with minimal configuration. The following steps set up some files for an example +//! Python module, install `maturin`, and then show how build and import the Python module. //! //! First, create a new folder (let's call it `string_sum`) containing the following two files: //! @@ -198,10 +178,9 @@ //! '25' //! ``` //! -//! As well as with `maturin`, it is possible to build using -//! [`setuptools-rust`](https://github.com/PyO3/setuptools-rust) or -//! [manually](https://pyo3.rs/latest/building_and_distribution.html#manual-builds). Both offer more -//! flexibility than `maturin` but require further configuration. +//! As well as with `maturin`, it is possible to build using [setuptools-rust] or +//! [manually][manual_builds]. Both offer more flexibility than `maturin` but require further +//! configuration. //! //! # Example: Using Python from Rust //! @@ -247,21 +226,54 @@ //! } //! ``` //! -//! The guide has [a section](https://pyo3.rs/latest/python_from_rust.html) with lots of examples -//! about this topic. +//! The guide has [a section][calling_rust] with lots of examples about this topic. //! //! # Other Examples //! //! The PyO3 [README](https://github.com/PyO3/pyo3#readme) contains quick-start examples for both -//! using [Rust from Python](https://github.com/PyO3/pyo3#using-rust-from-python) and -//! [Python from Rust](https://github.com/PyO3/pyo3#using-python-from-rust). +//! using [Rust from Python] and [Python from Rust]. //! -//! The PyO3 repository's [examples subdirectory](https://github.com/PyO3/pyo3/tree/main/examples) +//! The PyO3 repository's [examples subdirectory] //! contains some basic packages to demonstrate usage of PyO3. //! //! There are many projects using PyO3 - see a list of some at -//! - +//! . +//! +//! [inventory]: https://docs.rs/inventory +//! [`HashMap`]: https://docs.rs/hashbrown/latest/hashbrown/struct.HashMap.html +//! [`HashSet`]: https://docs.rs/hashbrown/latest/hashbrown/struct.HashSet.html +//! [`IndexMap`]: https://docs.rs/indexmap/latest/indexmap/map/struct.IndexMap.html +//! [`BigInt`]: https://docs.rs/num-bigint/latest/num_bigint/struct.BigInt.html +//! [`BigUint`]: https://docs.rs/num-bigint/latest/num_bigint/struct.BigUint.html +//! [`Complex`]: https://docs.rs/num-complex/latest/num_complex/struct.Complex.html +//! [`Deserialize`]: https://docs.rs/serde/latest/serde/trait.Deserialize.html +//! [`Serialize`]: https://docs.rs/serde/latest/serde/trait.Serialize.html +//! [`hashbrown`]: ./hashbrown/index.html +//! [`indexmap`]: <./indexmap/index.html> +//! [`maturin`]: https://github.com/PyO3/maturin "Build and publish crates with pyo3, rust-cpython and cffi bindings as well as rust binaries as python packages" +//! [`num-bigint`]: ./num_bigint/index.html +//! [`num-complex`]: ./num_complex/index.html +//! [`pyo3-build-config`]: https://docs.rs/pyo3-build-config +//! [`serde`]: <./serde/index.html> +//! [calling_rust]: https://pyo3.rs/latest/python_from_rust.html "Calling Python from Rust - PyO3 user guide" +//! [examples subdirectory]: https://github.com/PyO3/pyo3/tree/main/examples +//! [feature flags]: https://doc.rust-lang.org/cargo/reference/features.html "Features - The Cargo Book" +//! [global interpreter lock]: https://docs.python.org/3/glossary.html#term-global-interpreter-lock +//! [hashbrown]: https://docs.rs/hashbrown +//! [indexmap]: https://docs.rs/indexmap +//! [manual_builds]: https://pyo3.rs/latest/building_and_distribution.html#manual-builds "Manual builds - Building and Distribution - PyO3 user guide" +//! [num-bigint]: https://docs.rs/num-bigint +//! [num-complex]: https://docs.rs/num-complex +//! [serde]: https://docs.rs/serde +//! [setuptools-rust]: https://github.com/PyO3/setuptools-rust "Setuptools plugin for Rust extensions" +//! [the guide]: https://pyo3.rs "PyO3 user guide" +//! [types]: https://pyo3.rs/latest/types.html "GIL lifetimes, mutability and Python object types" +//! [PEP 384]: https://www.python.org/dev/peps/pep-0384 "PEP 384 -- Defining a Stable ABI" +//! [Python from Rust]: https://github.com/PyO3/pyo3#using-python-from-rust +//! [Rust from Python]: https://github.com/PyO3/pyo3#using-rust-from-python +//! [`#![feature(specialization)]`]: +//! [Features chapter of the guide]: "Features Reference - PyO3 user guide" +//! [`#[pyclass]`]: macro@crate::pyclass pub use crate::class::*; pub use crate::conversion::{ AsPyPointer, FromPyObject, FromPyPointer, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, @@ -278,7 +290,6 @@ pub use crate::pyclass::PyClass; pub use crate::pyclass_init::PyClassInitializer; pub use crate::python::{Python, PythonVersionInfo}; pub use crate::type_object::PyTypeInfo; -// Since PyAny is as important as PyObject, we expose it to the top level. pub use crate::types::PyAny; #[cfg(feature = "macros")] @@ -290,16 +301,13 @@ pub use { }; #[cfg(all(feature = "macros", feature = "multiple-pymethods"))] +#[doc(hidden)] pub use inventory; // Re-exported for `#[pyclass]` and `#[pymethods]` with `multiple-pymethods`. #[macro_use] mod internal_tricks; -// The CPython stable ABI does not include PyBuffer. -#[cfg(not(Py_LIMITED_API))] -#[cfg_attr(docsrs, doc(cfg(not(Py_LIMITED_API))))] pub mod buffer; - #[doc(hidden)] pub mod callback; pub mod class; @@ -312,13 +320,10 @@ mod err; pub mod exceptions; pub mod ffi; mod gil; +#[doc(hidden)] pub mod impl_; mod instance; - -#[cfg(not(Py_LIMITED_API))] -#[cfg_attr(docsrs, doc(cfg(not(Py_LIMITED_API))))] pub mod marshal; - pub mod once_cell; pub mod panic; pub mod prelude; @@ -327,166 +332,27 @@ pub mod pyclass; pub mod pyclass_init; pub mod pyclass_slots; mod python; - pub mod type_object; pub mod types; pub use crate::conversions::*; -#[cfg_attr(docsrs, doc(cfg(feature = "serde")))] -#[cfg(feature = "serde")] -pub mod serde; - -/// The proc macros, all of which are part of the prelude. -/// -/// Import these with `use pyo3::prelude::*;` -#[cfg_attr(docsrs, doc(cfg(feature = "macros")))] +#[doc(hidden)] +#[deprecated( + since = "0.15.0", + note = "please import this with `use pyo3::...` or from the prelude instead" +)] #[cfg(feature = "macros")] pub mod proc_macro { pub use pyo3_macros::{pyclass, pyfunction, pymethods, pymodule, pyproto}; } -/// Returns a function that takes a [Python] instance and returns a Python function. -/// -/// Use this together with `#[pyfunction]` and [types::PyModule::add_wrapped]. -#[macro_export] -macro_rules! wrap_pyfunction { - ($function_name: ident) => {{ - &|py| $crate::paste::expr! { [<__pyo3_get_function_ $function_name>] }(py) - }}; - - ($function_name: ident, $arg: expr) => { - $crate::wrap_pyfunction!($function_name)( - <$crate::derive_utils::PyFunctionArguments as ::std::convert::From<_>>::from($arg), - ) - }; -} - -/// Returns a function that takes a [Python] instance and returns a Python module. -/// -/// Use this together with `#[pymodule]` and [types::PyModule::add_wrapped]. -#[macro_export] -macro_rules! wrap_pymodule { - ($module_name:ident) => {{ - $crate::paste::expr! { - &|py| unsafe { $crate::PyObject::from_owned_ptr(py, []()) } - } - }}; -} - -/// A convenient macro to execute a Python code snippet, with some local variables set. -/// -/// # Panics -/// This macro internally calls [`Python::run`](struct.Python.html#method.run) and panics -/// if it returns `Err`, after printing the error to stdout. -/// -/// # Examples -/// ``` -/// use pyo3::{prelude::*, py_run, types::PyList}; -/// Python::with_gil(|py| { -/// let list = PyList::new(py, &[1, 2, 3]); -/// py_run!(py, list, "assert list == [1, 2, 3]"); -/// }); -/// ``` -/// -/// You can use this macro to test pyfunctions or pyclasses quickly. -/// -/// ``` -/// use pyo3::{prelude::*, py_run, PyCell}; -/// #[pyclass] -/// #[derive(Debug)] -/// struct Time { -/// hour: u32, -/// minute: u32, -/// second: u32, -/// } -/// #[pymethods] -/// impl Time { -/// fn repl_japanese(&self) -> String { -/// format!("{}時{}分{}秒", self.hour, self.minute, self.second) -/// } -/// #[getter] -/// fn hour(&self) -> u32 { -/// self.hour -/// } -/// fn as_tuple(&self) -> (u32, u32, u32) { -/// (self.hour, self.minute, self.second) -/// } -/// } -/// Python::with_gil(|py| { -/// let time = PyCell::new(py, Time {hour: 8, minute: 43, second: 16}).unwrap(); -/// let time_as_tuple = (8, 43, 16); -/// py_run!(py, time time_as_tuple, r#" -/// assert time.hour == 8 -/// assert time.repl_japanese() == "8時43分16秒" -/// assert time.as_tuple() == time_as_tuple -/// "#); -/// }); -/// ``` -/// -/// If you need to prepare the `locals` dict by yourself, you can pass it as `*locals`. -/// -/// ``` -/// use pyo3::prelude::*; -/// use pyo3::types::IntoPyDict; -/// #[pyclass] -/// struct MyClass {} -/// #[pymethods] -/// impl MyClass { -/// #[new] -/// fn new() -> Self { MyClass {} } -/// } -/// Python::with_gil(|py| { -/// let locals = [("C", py.get_type::())].into_py_dict(py); -/// pyo3::py_run!(py, *locals, "c = C()"); -/// }); -/// ``` -/// -/// **Note** -/// Since this macro is intended to use for testing, it **causes panic** when -/// [Python::run] returns `Err` internally. -/// If you need to handle failures, please use [Python::run] directly. -/// -#[macro_export] +#[cfg_attr(docsrs, doc(cfg(feature = "macros")))] #[cfg(feature = "macros")] -macro_rules! py_run { - ($py:expr, $($val:ident)+, $code:literal) => {{ - $crate::py_run_impl!($py, $($val)+, $crate::indoc::indoc!($code)) - }}; - ($py:expr, $($val:ident)+, $code:expr) => {{ - $crate::py_run_impl!($py, $($val)+, &$crate::unindent::unindent($code)) - }}; - ($py:expr, *$dict:expr, $code:literal) => {{ - $crate::py_run_impl!($py, *$dict, $crate::indoc::indoc!($code)) - }}; - ($py:expr, *$dict:expr, $code:expr) => {{ - $crate::py_run_impl!($py, *$dict, &$crate::unindent::unindent($code)) - }}; -} +pub use pyo3_macros::{pyclass, pyfunction, pymethods, pymodule, pyproto}; -#[macro_export] -#[doc(hidden)] -#[cfg(feature = "macros")] -macro_rules! py_run_impl { - ($py:expr, $($val:ident)+, $code:expr) => {{ - use $crate::types::IntoPyDict; - use $crate::ToPyObject; - let d = [$((stringify!($val), $val.to_object($py)),)+].into_py_dict($py); - $crate::py_run_impl!($py, *d, $code) - }}; - ($py:expr, *$dict:expr, $code:expr) => {{ - use ::std::option::Option::*; - if let ::std::result::Result::Err(e) = $py.run($code, None, Some($dict)) { - e.print($py); - // So when this c api function the last line called printed the error to stderr, - // the output is only written into a buffer which is never flushed because we - // panic before flushing. This is where this hack comes into place - $py.run("import sys; sys.stderr.flush()", None, None) - .unwrap(); - ::std::panic!("{}", $code) - } - }}; -} +#[macro_use] +mod macros; /// Test readme and user guide #[cfg(doctest)] diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 00000000..5653f927 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,147 @@ +/// Wraps a Rust function annotated with [`#[pyfunction]`](crate::pyfunction), turning it into a +/// [`PyCFunction`](crate::types::PyCFunction). +/// +/// This can be used with [`PyModule::add_function`](crate::types::PyModule::add_function) to add +/// free functions to a [`PyModule`](crate::types::PyModule) - see its documention for more information. +#[macro_export] +macro_rules! wrap_pyfunction { + ($function_name: ident) => {{ + &|py| $crate::paste::expr! { [<__pyo3_get_function_ $function_name>] }(py) + }}; + + ($function_name: ident, $arg: expr) => { + $crate::wrap_pyfunction!($function_name)( + <$crate::derive_utils::PyFunctionArguments as ::std::convert::From<_>>::from($arg), + ) + }; +} + +/// Returns a function that takes a [`Python`](crate::python::Python) instance and returns a Python module. +/// +/// Use this together with [`#[pymodule]`](crate::pymodule) and [crate::types::PyModule::add_wrapped]. +#[macro_export] +macro_rules! wrap_pymodule { + ($module_name:ident) => {{ + $crate::paste::expr! { + &|py| unsafe { $crate::PyObject::from_owned_ptr(py, []()) } + } + }}; +} + +/// A convenient macro to execute a Python code snippet, with some local variables set. +/// +/// # Panics +/// +/// This macro internally calls [`Python::run`](crate::Python::run) and panics +/// if it returns `Err`, after printing the error to stdout. +/// +/// If you need to handle failures, please use [`Python::run`](crate::python::Python::run) instead. +/// +/// # Examples +/// ``` +/// use pyo3::{prelude::*, py_run, types::PyList}; +/// +/// Python::with_gil(|py| { +/// let list = PyList::new(py, &[1, 2, 3]); +/// py_run!(py, list, "assert list == [1, 2, 3]"); +/// }); +/// ``` +/// +/// You can use this macro to test pyfunctions or pyclasses quickly. +/// +/// ``` +/// use pyo3::{prelude::*, py_run, PyCell}; +/// +/// #[pyclass] +/// #[derive(Debug)] +/// struct Time { +/// hour: u32, +/// minute: u32, +/// second: u32, +/// } +/// +/// #[pymethods] +/// impl Time { +/// fn repl_japanese(&self) -> String { +/// format!("{}時{}分{}秒", self.hour, self.minute, self.second) +/// } +/// #[getter] +/// fn hour(&self) -> u32 { +/// self.hour +/// } +/// fn as_tuple(&self) -> (u32, u32, u32) { +/// (self.hour, self.minute, self.second) +/// } +/// } +/// +/// Python::with_gil(|py| { +/// let time = PyCell::new(py, Time {hour: 8, minute: 43, second: 16}).unwrap(); +/// let time_as_tuple = (8, 43, 16); +/// py_run!(py, time time_as_tuple, r#" +/// assert time.hour == 8 +/// assert time.repl_japanese() == "8時43分16秒" +/// assert time.as_tuple() == time_as_tuple +/// "#); +/// }); +/// ``` +/// +/// If you need to prepare the `locals` dict by yourself, you can pass it as `*locals`. +/// +/// ``` +/// use pyo3::prelude::*; +/// use pyo3::types::IntoPyDict; +/// +/// #[pyclass] +/// struct MyClass; +/// +/// #[pymethods] +/// impl MyClass { +/// #[new] +/// fn new() -> Self { MyClass {} } +/// } +/// +/// Python::with_gil(|py| { +/// let locals = [("C", py.get_type::())].into_py_dict(py); +/// pyo3::py_run!(py, *locals, "c = C()"); +/// }); +/// ``` +#[macro_export] +#[cfg(feature = "macros")] +macro_rules! py_run { + ($py:expr, $($val:ident)+, $code:literal) => {{ + $crate::py_run_impl!($py, $($val)+, $crate::indoc::indoc!($code)) + }}; + ($py:expr, $($val:ident)+, $code:expr) => {{ + $crate::py_run_impl!($py, $($val)+, &$crate::unindent::unindent($code)) + }}; + ($py:expr, *$dict:expr, $code:literal) => {{ + $crate::py_run_impl!($py, *$dict, $crate::indoc::indoc!($code)) + }}; + ($py:expr, *$dict:expr, $code:expr) => {{ + $crate::py_run_impl!($py, *$dict, &$crate::unindent::unindent($code)) + }}; +} + +#[macro_export] +#[doc(hidden)] +#[cfg(feature = "macros")] +macro_rules! py_run_impl { + ($py:expr, $($val:ident)+, $code:expr) => {{ + use $crate::types::IntoPyDict; + use $crate::ToPyObject; + let d = [$((stringify!($val), $val.to_object($py)),)+].into_py_dict($py); + $crate::py_run_impl!($py, *d, $code) + }}; + ($py:expr, *$dict:expr, $code:expr) => {{ + use ::std::option::Option::*; + if let ::std::result::Result::Err(e) = $py.run($code, None, Some($dict)) { + e.print($py); + // So when this c api function the last line called printed the error to stderr, + // the output is only written into a buffer which is never flushed because we + // panic before flushing. This is where this hack comes into place + $py.run("import sys; sys.stderr.flush()", None, None) + .unwrap(); + ::std::panic!("{}", $code) + } + }}; +} diff --git a/src/marshal.rs b/src/marshal.rs index fd1cc0a8..95786f9d 100644 --- a/src/marshal.rs +++ b/src/marshal.rs @@ -1,6 +1,6 @@ #![cfg(not(Py_LIMITED_API))] -//! Support for the Python `marshal` format. Not supported in limited API -//! builds. +#![cfg_attr(docsrs, doc(cfg(not(Py_LIMITED_API))))] +//! Support for the Python `marshal` format. use crate::ffi; use crate::types::{PyAny, PyBytes}; diff --git a/src/prelude.rs b/src/prelude.rs index 04eae682..90e23992 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,8 +1,8 @@ // Copyright (c) 2017-present PyO3 Project and Contributors -//! A collection of items you most likely want to have in scope when working with pyo3 +//! PyO3's prelude. //! -//! The purpose of this module is to alleviate imports of many common pyo3 traits +//! The purpose of this module is to alleviate imports of many commonly used items of the PyO3 crate //! by adding a glob import to the top of pyo3 heavy modules: //! //! ``` @@ -16,9 +16,10 @@ pub use crate::instance::{Py, PyObject}; pub use crate::pycell::{PyCell, PyRef, PyRefMut}; pub use crate::pyclass_init::PyClassInitializer; pub use crate::python::Python; -pub use crate::{FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject}; -// PyModule is only part of the prelude because we need it for the pymodule function pub use crate::types::{PyAny, PyModule}; pub use crate::wrap_pyfunction; +pub use crate::{FromPyObject, IntoPy, IntoPyPointer, PyTryFrom, PyTryInto, ToPyObject}; + +#[cfg_attr(docsrs, doc(cfg(feature = "macros")))] #[cfg(feature = "macros")] -pub use {crate::proc_macro::*, pyo3_macros::FromPyObject}; +pub use pyo3_macros::{pyclass, pyfunction, pymethods, pymodule, pyproto, FromPyObject}; diff --git a/src/pycell.rs b/src/pycell.rs index e3f12e05..c8855a35 100644 --- a/src/pycell.rs +++ b/src/pycell.rs @@ -1,4 +1,7 @@ -//! Includes `PyCell` implementation. +//! PyO3's interior mutability primitive. +//! +//! For more information see the [guide](https://pyo3.rs/latest/class.html?highlight=pycell#pycell-and-interior-mutability "PyCell and interior mutability") + use crate::exceptions::PyRuntimeError; use crate::pyclass::PyClass; use crate::pyclass_init::PyClassInitializer; diff --git a/src/pyclass_init.rs b/src/pyclass_init.rs index b00e97ff..a858fa30 100644 --- a/src/pyclass_init.rs +++ b/src/pyclass_init.rs @@ -1,4 +1,4 @@ -//! Initialization utilities for `#[pyclass]`. +//! Contains initialization utilities for `#[pyclass]`. use crate::class::impl_::PyClassThreadChecker; use crate::pyclass_slots::{PyClassDict, PyClassWeakRef}; use crate::{callback::IntoPyCallbackOutput, class::impl_::PyClassBaseType}; diff --git a/src/pyclass_slots.rs b/src/pyclass_slots.rs index 8135c2e3..43426aa4 100644 --- a/src/pyclass_slots.rs +++ b/src/pyclass_slots.rs @@ -1,4 +1,5 @@ -//! This module contains additional fields for `#[pyclass]`. +//! Contains additional fields for `#[pyclass]`. +//! //! Mainly used by PyO3's proc-macro code. use crate::{ffi, Python}; diff --git a/tests/test_proc_macro_hygiene.rs b/tests/test_proc_macro_hygiene.rs index f123353b..a24dd32a 100644 --- a/tests/test_proc_macro_hygiene.rs +++ b/tests/test_proc_macro_hygiene.rs @@ -21,14 +21,14 @@ enum Derive4 { B { f: i32 }, } // enum case -#[::pyo3::proc_macro::pyclass] +#[::pyo3::pyclass] #[derive(::std::clone::Clone)] pub struct Foo; -#[::pyo3::proc_macro::pyclass] +#[::pyo3::pyclass] pub struct Foo2; -#[::pyo3::proc_macro::pyclass( +#[::pyo3::pyclass( name = "ActuallyBar", freelist = 8, weakref, @@ -47,7 +47,7 @@ pub struct Bar { c: ::std::option::Option<::pyo3::Py>, } -#[::pyo3::proc_macro::pymethods] +#[::pyo3::pymethods] impl Bar { #[args(x = "1", "*", _z = "2")] fn test(&self, _y: &Bar, _z: i32) {} @@ -84,7 +84,7 @@ impl Bar { } } -#[::pyo3::proc_macro::pyproto] +#[::pyo3::pyproto] impl ::pyo3::class::gc::PyGCProtocol for Bar { fn __traverse__( &self, @@ -102,7 +102,7 @@ impl ::pyo3::class::gc::PyGCProtocol for Bar { } #[cfg(not(Py_LIMITED_API))] -#[::pyo3::proc_macro::pyproto] +#[::pyo3::pyproto] impl ::pyo3::class::PyBufferProtocol for Bar { fn bf_getbuffer( _s: ::pyo3::PyRefMut, @@ -114,12 +114,12 @@ impl ::pyo3::class::PyBufferProtocol for Bar { fn bf_releasebuffer(_s: ::pyo3::PyRefMut, _v: *mut ::pyo3::ffi::Py_buffer) {} } -#[::pyo3::proc_macro::pyfunction] +#[::pyo3::pyfunction] fn do_something(x: i32) -> ::pyo3::PyResult { ::std::result::Result::Ok(x) } -#[::pyo3::proc_macro::pymodule] +#[::pyo3::pymodule] fn my_module(_py: ::pyo3::Python, m: &::pyo3::types::PyModule) -> ::pyo3::PyResult<()> { m.add_function(::pyo3::wrap_pyfunction!(do_something, m)?)?; ::std::result::Result::Ok(())