pyo3_stub_gen/lib.rs
1//! This crate creates stub files in following three steps using [inventory] crate:
2//!
3//! Define type information in Rust code (or by proc-macro)
4//! ---------------------------------------------------------
5//! The first step is to define Python type information in Rust code. [type_info] module provides several structs, for example:
6//!
7//! - [type_info::PyFunctionInfo] stores information of Python function, i.e. the name of the function, arguments and its types, return type, etc.
8//! - [type_info::PyClassInfo] stores information for Python class definition, i.e. the name of the class, members and its types, methods, etc.
9//!
10//! For better understanding of what happens in the background, let's define these information manually:
11//!
12//! ```
13//! use pyo3::*;
14//! use pyo3_stub_gen::type_info::*;
15//!
16//! // Usual PyO3 class definition
17//! #[pyclass(module = "my_module", name = "MyClass")]
18//! struct MyClass {
19//! #[pyo3(get)]
20//! name: String,
21//! #[pyo3(get)]
22//! description: Option<String>,
23//! }
24//!
25//! // Submit type information for stub file generation to inventory
26//! inventory::submit!{
27//! // Send information about Python class
28//! PyClassInfo {
29//! // Type ID of Rust struct (used to gathering phase discussed later)
30//! struct_id: std::any::TypeId::of::<MyClass>,
31//!
32//! // Python module name. Since stub file is generated per modules,
33//! // this helps where the class definition should be placed.
34//! module: Some("my_module"),
35//!
36//! // Python class name
37//! pyclass_name: "MyClass",
38//!
39//! getters: &[
40//! MemberInfo {
41//! name: "name",
42//! r#type: <String as ::pyo3_stub_gen::PyStubType>::type_output,
43//! doc: "Name docstring",
44//! default: None,
45//! },
46//! MemberInfo {
47//! name: "description",
48//! r#type: <Option<String> as ::pyo3_stub_gen::PyStubType>::type_output,
49//! doc: "Description docstring",
50//! default: None,
51//! },
52//! ],
53//!
54//! setters: &[],
55//!
56//! doc: "Docstring used in Python",
57//!
58//! // Base classes
59//! bases: &[],
60//! }
61//! }
62//! ```
63//!
64//! Roughly speaking, the above corresponds a following stub file `my_module.pyi`:
65//!
66//! ```python
67//! class MyClass:
68//! """
69//! Docstring used in Python
70//! """
71//! name: str
72//! """Name docstring"""
73//! description: Optional[str]
74//! """Description docstring"""
75//! ```
76//!
77//! We want to generate this [type_info::PyClassInfo] section automatically from `MyClass` Rust struct definition.
78//! This is done by using `#[gen_stub_pyclass]` proc-macro:
79//!
80//! ```
81//! use pyo3::*;
82//! use pyo3_stub_gen::{type_info::*, derive::gen_stub_pyclass};
83//!
84//! // Usual PyO3 class definition
85//! #[gen_stub_pyclass]
86//! #[pyclass(module = "my_module", name = "MyClass")]
87//! struct MyClass {
88//! #[pyo3(get)]
89//! name: String,
90//! #[pyo3(get)]
91//! description: Option<String>,
92//! }
93//! ```
94//!
95//! Since proc-macro is a converter from Rust code to Rust code, the output must be a Rust code.
96//! However, we need to gather these [type_info::PyClassInfo] definitions to generate stub files,
97//! and the above [inventory::submit] is for it.
98//!
99//! Gather type information into [StubInfo]
100//! ----------------------------------------
101//! [inventory] crate provides a mechanism to gather [inventory::submit]ted information when the library is loaded.
102//! To access these information through [inventory::iter], we need to define a gather function in the crate.
103//! Typically, this is done by following:
104//!
105//! ```rust
106//! use pyo3_stub_gen::{StubInfo, Result};
107//!
108//! pub fn stub_info() -> Result<StubInfo> {
109//! let manifest_dir: &::std::path::Path = env!("CARGO_MANIFEST_DIR").as_ref();
110//! StubInfo::from_pyproject_toml(manifest_dir.join("pyproject.toml"))
111//! }
112//! ```
113//!
114//! There is a helper macro to define it easily:
115//!
116//! ```rust
117//! pyo3_stub_gen::define_stub_info_gatherer!(sub_info);
118//! ```
119//!
120//! Generate stub file from [StubInfo]
121//! -----------------------------------
122//! [StubInfo] translates [type_info::PyClassInfo] and other information into a form helpful for generating stub files while gathering.
123//!
124//! [generate] module provides structs implementing [std::fmt::Display] to generate corresponding parts of stub file.
125//! For example, [generate::MethodDef] generates Python class method definition as follows:
126//!
127//! ```rust
128//! use pyo3_stub_gen::{TypeInfo, generate::*};
129//!
130//! let method = MethodDef {
131//! name: "foo",
132//! args: vec![Arg { name: "x", r#type: TypeInfo::builtin("int"), signature: None, }],
133//! r#return: TypeInfo::builtin("int"),
134//! doc: "This is a foo method.",
135//! r#type: MethodType::Instance,
136//! };
137//!
138//! assert_eq!(
139//! method.to_string().trim(),
140//! r#"
141//! def foo(self, x:builtins.int) -> builtins.int:
142//! r"""
143//! This is a foo method.
144//! """
145//! "#.trim()
146//! );
147//! ```
148//!
149//! [generate::ClassDef] generates Python class definition using [generate::MethodDef] and others, and other `*Def` structs works as well.
150//!
151//! [generate::Module] consists of `*Def` structs and yields an entire stub file `*.pyi` for a single Python (sub-)module, i.e. a shared library build by PyO3.
152//! [generate::Module]s are created as a part of [StubInfo], which merges [type_info::PyClassInfo]s and others submitted to [inventory] separately.
153//! [StubInfo] is instantiated with [pyproject::PyProject] to get where to generate the stub file,
154//! and [StubInfo::generate] generates the stub files for every modules.
155//!
156
157pub use inventory;
158pub use pyo3_stub_gen_derive as derive; // re-export to use in generated code
159
160pub mod exception;
161pub mod generate;
162pub mod pyproject;
163mod stub_type;
164pub mod type_info;
165pub mod util;
166
167pub use generate::StubInfo;
168pub use stub_type::{PyStubType, TypeInfo};
169
170pub type Result<T> = anyhow::Result<T>;
171
172/// Create a function to initialize [StubInfo] from `pyproject.toml` in `CARGO_MANIFEST_DIR`.
173///
174/// If `pyproject.toml` is in another place, you need to create a function to call [StubInfo::from_pyproject_toml] manually.
175/// This must be placed in your PyO3 library crate, i.e. same crate where [inventory::submit]ted,
176/// not in `gen_stub` executables due to [inventory] mechanism.
177///
178#[macro_export]
179macro_rules! define_stub_info_gatherer {
180 ($function_name:ident) => {
181 /// Auto-generated function to gather information to generate stub files
182 pub fn $function_name() -> $crate::Result<$crate::StubInfo> {
183 let manifest_dir: &::std::path::Path = env!("CARGO_MANIFEST_DIR").as_ref();
184 $crate::StubInfo::from_pyproject_toml(manifest_dir.join("pyproject.toml"))
185 }
186 };
187}
188
189#[macro_export]
190macro_rules! module_variable {
191 ($module:expr, $name:expr, $ty:ty) => {
192 $crate::inventory::submit! {
193 $crate::type_info::PyVariableInfo{
194 name: $name,
195 module: $module,
196 r#type: <$ty as $crate::PyStubType>::type_output,
197 }
198 }
199 };
200}
201
202#[doc = include_str!("../../README.md")]
203mod readme {}