From a43ccd235bf073e6f69582a8e5bc7a96e0ded8b0 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Thu, 4 Jan 2024 19:56:17 +0900 Subject: [PATCH 1/7] stub_info in lib crate --- pyo3-stub-gen-testing/Cargo.toml | 1 + pyo3-stub-gen-testing/src/bin/stub_gen.rs | 6 ++---- pyo3-stub-gen-testing/src/lib.rs | 7 +++++++ 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pyo3-stub-gen-testing/Cargo.toml b/pyo3-stub-gen-testing/Cargo.toml index ab82c4e..e307358 100644 --- a/pyo3-stub-gen-testing/Cargo.toml +++ b/pyo3-stub-gen-testing/Cargo.toml @@ -8,6 +8,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] inventory.workspace = true +anyhow.workspace = true pyo3-stub-gen = { workspace = true, optional = true } pyo3.workspace = true diff --git a/pyo3-stub-gen-testing/src/bin/stub_gen.rs b/pyo3-stub-gen-testing/src/bin/stub_gen.rs index 3e30407..d8ba35d 100644 --- a/pyo3-stub-gen-testing/src/bin/stub_gen.rs +++ b/pyo3-stub-gen-testing/src/bin/stub_gen.rs @@ -1,6 +1,4 @@ -use pyo3_stub_gen::*; - fn main() { - let modules = generate::gather().unwrap(); - dbg!(modules); + let stub = pyo3_stub_gen_testing::stub_info().unwrap(); + dbg!(stub); } diff --git a/pyo3-stub-gen-testing/src/lib.rs b/pyo3-stub-gen-testing/src/lib.rs index fcdea7a..4130fb2 100644 --- a/pyo3-stub-gen-testing/src/lib.rs +++ b/pyo3-stub-gen-testing/src/lib.rs @@ -1,3 +1,5 @@ +use std::collections::BTreeMap; + use pyo3::prelude::*; #[cfg(feature = "stub_gen")] @@ -22,6 +24,11 @@ fn pyo3_stub_gen_testing(_py: Python, m: &PyModule) -> PyResult<()> { Ok(()) } +#[cfg(feature = "stub_gen")] +pub fn stub_info() -> anyhow::Result> { + pyo3_stub_gen::generate::gather() +} + #[cfg(test)] mod test { #[test] From 98072e516cfefd8c99a9169701f30bf078ebb755 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 5 Jan 2024 11:24:23 +0900 Subject: [PATCH 2/7] StubInfo struct --- pyo3-stub-gen-testing/src/bin/stub_gen.rs | 2 +- pyo3-stub-gen-testing/src/lib.rs | 9 +- pyo3-stub-gen/src/generate.rs | 226 +++++++++++----------- 3 files changed, 123 insertions(+), 114 deletions(-) diff --git a/pyo3-stub-gen-testing/src/bin/stub_gen.rs b/pyo3-stub-gen-testing/src/bin/stub_gen.rs index d8ba35d..c3df794 100644 --- a/pyo3-stub-gen-testing/src/bin/stub_gen.rs +++ b/pyo3-stub-gen-testing/src/bin/stub_gen.rs @@ -1,4 +1,4 @@ fn main() { - let stub = pyo3_stub_gen_testing::stub_info().unwrap(); + let stub = pyo3_stub_gen_testing::stub_info(); dbg!(stub); } diff --git a/pyo3-stub-gen-testing/src/lib.rs b/pyo3-stub-gen-testing/src/lib.rs index 4130fb2..5f3e416 100644 --- a/pyo3-stub-gen-testing/src/lib.rs +++ b/pyo3-stub-gen-testing/src/lib.rs @@ -1,9 +1,7 @@ -use std::collections::BTreeMap; - use pyo3::prelude::*; #[cfg(feature = "stub_gen")] -use pyo3_stub_gen::derive::*; +use pyo3_stub_gen::{derive::*, generate::StubInfo}; /// Returns the sum of two numbers as a string. /// @@ -25,8 +23,9 @@ fn pyo3_stub_gen_testing(_py: Python, m: &PyModule) -> PyResult<()> { } #[cfg(feature = "stub_gen")] -pub fn stub_info() -> anyhow::Result> { - pyo3_stub_gen::generate::gather() +pub fn stub_info() -> StubInfo { + use std::env; + StubInfo::gather(env!("CARGO_PKG_NAME")) } #[cfg(test)] diff --git a/pyo3-stub-gen/src/generate.rs b/pyo3-stub-gen/src/generate.rs index a4c6104..d2cde90 100644 --- a/pyo3-stub-gen/src/generate.rs +++ b/pyo3-stub-gen/src/generate.rs @@ -8,7 +8,7 @@ use pyo3::inspect::types::TypeInfo; use std::{ any::TypeId, collections::{BTreeMap, BTreeSet}, - env, fmt, fs, + fmt, fs, io::Write, path::*, }; @@ -294,122 +294,132 @@ impl fmt::Display for Module { } } -/// Gather metadata generated by proc-macros -pub fn gather() -> Result> { - let mut modules: BTreeMap = BTreeMap::new(); - - for info in inventory::iter:: { - let module_name = info.module.map(str::to_owned).unwrap_or(pkg_name()); - let module = modules.entry(module_name).or_default(); - - module.class.insert( - (info.struct_id)(), - ClassDef { - name: info.pyclass_name, - new: None, - doc: info.doc, - methods: Vec::new(), - members: info - .members - .iter() - .map(|info| MemberDef { - name: info.name, - r#type: (info.r#type)(), - }) - .collect(), - }, - ); - } +#[derive(Debug, Clone, PartialEq)] +pub struct StubInfo(BTreeMap); + +impl StubInfo { + pub fn gather(pkg_name: &str) -> Self { + let mut modules: BTreeMap = BTreeMap::new(); + + for info in inventory::iter:: { + let module_name = info + .module + .map(str::to_owned) + .unwrap_or(pkg_name.to_string()); + let module = modules.entry(module_name).or_default(); + + module.class.insert( + (info.struct_id)(), + ClassDef { + name: info.pyclass_name, + new: None, + doc: info.doc, + methods: Vec::new(), + members: info + .members + .iter() + .map(|info| MemberDef { + name: info.name, + r#type: (info.r#type)(), + }) + .collect(), + }, + ); + } - for info in inventory::iter:: { - let module_name = info.module.map(str::to_owned).unwrap_or(pkg_name()); - let module = modules.entry(module_name).or_default(); - module.enum_.insert( - (info.enum_id)(), - EnumDef { - name: info.pyclass_name, - doc: info.doc, - variants: info.variants, - }, - ); - } + for info in inventory::iter:: { + let module_name = info + .module + .map(str::to_owned) + .unwrap_or(pkg_name.to_string()); + let module = modules.entry(module_name).or_default(); + module.enum_.insert( + (info.enum_id)(), + EnumDef { + name: info.pyclass_name, + doc: info.doc, + variants: info.variants, + }, + ); + } - 'methods_info: for info in inventory::iter:: { - let struct_id = (info.struct_id)(); - for module in modules.values_mut() { - if let Some(entry) = module.class.get_mut(&struct_id) { - for getter in info.getters { - entry.members.push(MemberDef { - name: getter.name, - r#type: (getter.r#type)(), - }); - } - for method in info.methods { - entry.methods.push(MethodDef { - name: method.name, - args: method.args.iter().map(Arg::from_info).collect(), - signature: method.signature, - r#return: (method.r#return)().into(), - doc: method.doc, - is_class: method.is_class, - is_static: method.is_static, - }) + 'methods_info: for info in inventory::iter:: { + let struct_id = (info.struct_id)(); + for module in modules.values_mut() { + if let Some(entry) = module.class.get_mut(&struct_id) { + for getter in info.getters { + entry.members.push(MemberDef { + name: getter.name, + r#type: (getter.r#type)(), + }); + } + for method in info.methods { + entry.methods.push(MethodDef { + name: method.name, + args: method.args.iter().map(Arg::from_info).collect(), + signature: method.signature, + r#return: (method.r#return)().into(), + doc: method.doc, + is_class: method.is_class, + is_static: method.is_static, + }) + } + if let Some(new) = &info.new { + entry.new = Some(NewDef::from_info(new)); + } + continue 'methods_info; } - if let Some(new) = &info.new { - entry.new = Some(NewDef::from_info(new)); - } - continue 'methods_info; } + unreachable!("Missing struct_id = {:?}", struct_id); } - unreachable!("Missing struct_id = {:?}", struct_id); - } - for info in inventory::iter:: { - let module = modules - .entry(info.module.map(str::to_string).unwrap_or(pkg_name())) - .or_default(); - module.function.insert( - info.name, - FunctionDef { - name: info.name, - args: info.args.iter().map(Arg::from_info).collect(), - r#return: (info.r#return)().into(), - doc: info.doc, - signature: info.signature, - }, - ); - } - - let default = modules.entry(pkg_name()).or_default(); - for info in inventory::iter:: { - default.error.insert(info.name); - } - - Ok(modules) -} + for info in inventory::iter:: { + let module = modules + .entry( + info.module + .map(str::to_string) + .unwrap_or(pkg_name.to_string()), + ) + .or_default(); + module.function.insert( + info.name, + FunctionDef { + name: info.name, + args: info.args.iter().map(Arg::from_info).collect(), + r#return: (info.r#return)().into(), + doc: info.doc, + signature: info.signature, + }, + ); + } -fn pkg_name() -> String { - env::var("CARGO_PKG_NAME").unwrap() -} + let default = modules.entry(pkg_name.to_string()).or_default(); + for info in inventory::iter:: { + default.error.insert(info.name); + } -pub fn generate(python_root: &Path) -> Result<()> { - for (name, module) in gather()?.iter() { - let path: Vec<&str> = name.split('.').collect(); - let dest = if path.len() > 1 { - python_root.join(format!("{}.pyi", path[1..].join("/"))) - } else { - python_root.join("__init__.pyi") - }; + Self(modules) + } - if let Some(dir) = dest.parent() { - fs::create_dir_all(dir)?; + pub fn generate(&self, python_root: &Path) -> Result<()> { + for (name, module) in self.0.iter() { + let path: Vec<&str> = name.split('.').collect(); + let dest = if path.len() > 1 { + python_root.join(format!("{}.pyi", path[1..].join("/"))) + } else { + python_root.join("__init__.pyi") + }; + + if let Some(dir) = dest.parent() { + fs::create_dir_all(dir)?; + } + let mut f = fs::File::create(dest)?; + writeln!(f, "# This file is automatically generated by gen_stub.rs\n")?; + writeln!(f, "from typing import final, Any, List, Dict")?; + writeln!(f, "from enum import Enum, auto")?; + writeln!(f)?; + write!(f, "{}", module)?; } - let mut f = fs::File::create(dest)?; - writeln!(f, "# This file is automatically generated by gen_stub.rs\n")?; - writeln!(f, "from typing import final, Any, List, Dict")?; - writeln!(f, "from enum import Enum, auto")?; - writeln!(f)?; - write!(f, "{}", module)?; + Ok(()) } - Ok(()) } From a7f9c90bb0db35a2ab0babc36738a804468a1cd6 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 5 Jan 2024 11:57:29 +0900 Subject: [PATCH 3/7] from_info --- pyo3-stub-gen/src/generate.rs | 121 ++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 49 deletions(-) diff --git a/pyo3-stub-gen/src/generate.rs b/pyo3-stub-gen/src/generate.rs index d2cde90..f2c3139 100644 --- a/pyo3-stub-gen/src/generate.rs +++ b/pyo3-stub-gen/src/generate.rs @@ -69,6 +69,20 @@ struct MethodDef { is_class: bool, } +impl MethodDef { + fn from_info(info: &MethodInfo) -> Self { + Self { + name: info.name, + args: info.args.iter().map(Arg::from_info).collect(), + signature: info.signature, + r#return: (info.r#return)().into(), + doc: info.doc, + is_static: info.is_static, + is_class: info.is_class, + } + } +} + impl fmt::Display for MethodDef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let indent = indent(); @@ -120,6 +134,15 @@ struct MemberDef { r#type: TypeInfo, } +impl MemberDef { + fn from_info(info: &MemberInfo) -> Self { + Self { + name: info.name, + r#type: (info.r#type)(), + } + } +} + impl fmt::Display for MemberDef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let indent = indent(); @@ -171,6 +194,18 @@ struct ClassDef { methods: Vec, } +impl ClassDef { + fn from_info(info: &PyClassInfo) -> Self { + Self { + name: info.pyclass_name, + new: None, + doc: info.doc, + members: info.members.iter().map(MemberDef::from_info).collect(), + methods: Vec::new(), + } + } +} + impl fmt::Display for ClassDef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "@final")?; @@ -208,6 +243,16 @@ struct EnumDef { variants: &'static [&'static str], } +impl EnumDef { + fn from_info(info: &PyEnumInfo) -> Self { + Self { + name: info.pyclass_name, + doc: info.doc, + variants: info.variants, + } + } +} + impl fmt::Display for EnumDef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "@final")?; @@ -238,6 +283,18 @@ struct FunctionDef { doc: &'static str, } +impl FunctionDef { + fn from_info(info: &PyFunctionInfo) -> Self { + Self { + name: info.name, + args: info.args.iter().map(Arg::from_info).collect(), + r#return: (info.r#return)().into(), + doc: info.doc, + signature: info.signature, + } + } +} + impl fmt::Display for FunctionDef { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "def {}(", self.name)?; @@ -298,49 +355,30 @@ impl fmt::Display for Module { pub struct StubInfo(BTreeMap); impl StubInfo { - pub fn gather(pkg_name: &str) -> Self { + pub fn gather(default_module_name: &str) -> Self { let mut modules: BTreeMap = BTreeMap::new(); for info in inventory::iter:: { let module_name = info .module .map(str::to_owned) - .unwrap_or(pkg_name.to_string()); + .unwrap_or(default_module_name.to_string()); let module = modules.entry(module_name).or_default(); - module.class.insert( - (info.struct_id)(), - ClassDef { - name: info.pyclass_name, - new: None, - doc: info.doc, - methods: Vec::new(), - members: info - .members - .iter() - .map(|info| MemberDef { - name: info.name, - r#type: (info.r#type)(), - }) - .collect(), - }, - ); + module + .class + .insert((info.struct_id)(), ClassDef::from_info(info)); } for info in inventory::iter:: { let module_name = info .module .map(str::to_owned) - .unwrap_or(pkg_name.to_string()); + .unwrap_or(default_module_name.to_string()); let module = modules.entry(module_name).or_default(); - module.enum_.insert( - (info.enum_id)(), - EnumDef { - name: info.pyclass_name, - doc: info.doc, - variants: info.variants, - }, - ); + module + .enum_ + .insert((info.enum_id)(), EnumDef::from_info(info)); } 'methods_info: for info in inventory::iter:: { @@ -354,15 +392,7 @@ impl StubInfo { }); } for method in info.methods { - entry.methods.push(MethodDef { - name: method.name, - args: method.args.iter().map(Arg::from_info).collect(), - signature: method.signature, - r#return: (method.r#return)().into(), - doc: method.doc, - is_class: method.is_class, - is_static: method.is_static, - }) + entry.methods.push(MethodDef::from_info(method)) } if let Some(new) = &info.new { entry.new = Some(NewDef::from_info(new)); @@ -378,22 +408,15 @@ impl StubInfo { .entry( info.module .map(str::to_string) - .unwrap_or(pkg_name.to_string()), + .unwrap_or(default_module_name.to_string()), ) .or_default(); - module.function.insert( - info.name, - FunctionDef { - name: info.name, - args: info.args.iter().map(Arg::from_info).collect(), - r#return: (info.r#return)().into(), - doc: info.doc, - signature: info.signature, - }, - ); + module + .function + .insert(info.name, FunctionDef::from_info(info)); } - let default = modules.entry(pkg_name.to_string()).or_default(); + let default = modules.entry(default_module_name.to_string()).or_default(); for info in inventory::iter:: { default.error.insert(info.name); } From 3d8745e50df4423c5591502b44547f6921b53f6f Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 5 Jan 2024 15:18:26 +0900 Subject: [PATCH 4/7] StubInfo::{generate_single_stub_file, generate_stub_files} --- pyo3-stub-gen-testing/src/bin/stub_gen.rs | 3 +- pyo3-stub-gen/src/generate.rs | 45 ++++++++++++++++++----- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/pyo3-stub-gen-testing/src/bin/stub_gen.rs b/pyo3-stub-gen-testing/src/bin/stub_gen.rs index c3df794..de5bfb0 100644 --- a/pyo3-stub-gen-testing/src/bin/stub_gen.rs +++ b/pyo3-stub-gen-testing/src/bin/stub_gen.rs @@ -1,4 +1,5 @@ fn main() { let stub = pyo3_stub_gen_testing::stub_info(); - dbg!(stub); + stub.generate_single_stub_file(env!("CARGO_MANIFEST_DIR")) + .unwrap(); } diff --git a/pyo3-stub-gen/src/generate.rs b/pyo3-stub-gen/src/generate.rs index f2c3139..e706d85 100644 --- a/pyo3-stub-gen/src/generate.rs +++ b/pyo3-stub-gen/src/generate.rs @@ -2,7 +2,7 @@ use crate::type_info::*; -use anyhow::Result; +use anyhow::{anyhow, bail, Result}; use itertools::Itertools; use pyo3::inspect::types::TypeInfo; use std::{ @@ -335,6 +335,12 @@ pub struct Module { impl fmt::Display for Module { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "# This file is automatically generated by pyo3_stub_gen")?; + writeln!(f)?; + writeln!(f, "from typing import final, Any, List, Dict")?; + writeln!(f, "from enum import Enum, auto")?; + writeln!(f)?; + for class in self.class.values().sorted_by_key(|class| class.name) { write!(f, "{}", class)?; } @@ -352,9 +358,18 @@ impl fmt::Display for Module { } #[derive(Debug, Clone, PartialEq)] -pub struct StubInfo(BTreeMap); +pub struct StubInfo { + modules: BTreeMap, + default_module_name: String, +} impl StubInfo { + pub fn default_module(&self) -> Result<&Module> { + self.modules + .get(&self.default_module_name) + .ok_or_else(|| anyhow!("Missing default module: {}", self.default_module_name)) + } + pub fn gather(default_module_name: &str) -> Self { let mut modules: BTreeMap = BTreeMap::new(); @@ -421,11 +436,27 @@ impl StubInfo { default.error.insert(info.name); } - Self(modules) + Self { + modules, + default_module_name: default_module_name.to_string(), + } + } + + pub fn generate_single_stub_file(&self, out_dir: impl AsRef) -> Result<()> { + let out_dir = out_dir.as_ref(); + if !out_dir.is_dir() { + bail!("{} is not a directory", out_dir.display()); + } + + let mut f = fs::File::create(out_dir.join(format!("{}.pyi", self.default_module_name)))?; + let module = self.default_module()?; + write!(f, "{}", module)?; + Ok(()) } - pub fn generate(&self, python_root: &Path) -> Result<()> { - for (name, module) in self.0.iter() { + pub fn generate_stub_files(&self, python_root: impl AsRef) -> Result<()> { + let python_root = python_root.as_ref(); + for (name, module) in self.modules.iter() { let path: Vec<&str> = name.split('.').collect(); let dest = if path.len() > 1 { python_root.join(format!("{}.pyi", path[1..].join("/"))) @@ -437,10 +468,6 @@ impl StubInfo { fs::create_dir_all(dir)?; } let mut f = fs::File::create(dest)?; - writeln!(f, "# This file is automatically generated by gen_stub.rs\n")?; - writeln!(f, "from typing import final, Any, List, Dict")?; - writeln!(f, "from enum import Enum, auto")?; - writeln!(f)?; write!(f, "{}", module)?; } Ok(()) From bfd9ee6b7bf900810fc11a0559a58df7f08d8a07 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 5 Jan 2024 15:47:07 +0900 Subject: [PATCH 5/7] StubInfo::from_pyproject_toml --- Cargo.toml | 2 ++ pyo3-stub-gen-testing/src/bin/stub_gen.rs | 10 ++++++---- pyo3-stub-gen-testing/src/lib.rs | 9 +++++---- pyo3-stub-gen/Cargo.toml | 4 +++- pyo3-stub-gen/src/generate.rs | 5 +++++ pyo3-stub-gen/src/lib.rs | 6 +++++- pyo3-stub-gen/src/pyproject.rs | 22 ++++++++++++++++++++++ 7 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 pyo3-stub-gen/src/pyproject.rs diff --git a/Cargo.toml b/Cargo.toml index 5ea8c31..0150ec8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,4 +18,6 @@ prettyplease = "0.2.16" proc-macro2 = "1.0.74" pyo3 = { version = "0.20.1", features = ["experimental-inspect"] } quote = "1.0.35" +serde = { version = "1.0.194", features = ["derive"] } syn = "2.0.46" +toml = "0.8.8" diff --git a/pyo3-stub-gen-testing/src/bin/stub_gen.rs b/pyo3-stub-gen-testing/src/bin/stub_gen.rs index de5bfb0..0a545ea 100644 --- a/pyo3-stub-gen-testing/src/bin/stub_gen.rs +++ b/pyo3-stub-gen-testing/src/bin/stub_gen.rs @@ -1,5 +1,7 @@ -fn main() { - let stub = pyo3_stub_gen_testing::stub_info(); - stub.generate_single_stub_file(env!("CARGO_MANIFEST_DIR")) - .unwrap(); +use pyo3_stub_gen::Result; + +fn main() -> Result<()> { + let stub = pyo3_stub_gen_testing::stub_info()?; + stub.generate_single_stub_file(env!("CARGO_MANIFEST_DIR"))?; + Ok(()) } diff --git a/pyo3-stub-gen-testing/src/lib.rs b/pyo3-stub-gen-testing/src/lib.rs index 5f3e416..6688d32 100644 --- a/pyo3-stub-gen-testing/src/lib.rs +++ b/pyo3-stub-gen-testing/src/lib.rs @@ -1,7 +1,7 @@ use pyo3::prelude::*; #[cfg(feature = "stub_gen")] -use pyo3_stub_gen::{derive::*, generate::StubInfo}; +use pyo3_stub_gen::derive::*; /// Returns the sum of two numbers as a string. /// @@ -23,9 +23,10 @@ fn pyo3_stub_gen_testing(_py: Python, m: &PyModule) -> PyResult<()> { } #[cfg(feature = "stub_gen")] -pub fn stub_info() -> StubInfo { - use std::env; - StubInfo::gather(env!("CARGO_PKG_NAME")) +pub fn stub_info() -> pyo3_stub_gen::Result { + use std::{env, path::*}; + let manifest_dir: &Path = env!("CARGO_MANIFEST_DIR").as_ref(); + pyo3_stub_gen::StubInfo::from_pyproject_toml(manifest_dir.join("pyproject.toml")) } #[cfg(test)] diff --git a/pyo3-stub-gen/Cargo.toml b/pyo3-stub-gen/Cargo.toml index 4f4b261..5c869be 100644 --- a/pyo3-stub-gen/Cargo.toml +++ b/pyo3-stub-gen/Cargo.toml @@ -7,6 +7,8 @@ edition = "2021" pyo3-stub-gen-derive.workspace = true anyhow.workspace = true -itertools.workspace = true inventory.workspace = true +itertools.workspace = true pyo3.workspace = true +serde.workspace = true +toml.workspace = true diff --git a/pyo3-stub-gen/src/generate.rs b/pyo3-stub-gen/src/generate.rs index e706d85..e996092 100644 --- a/pyo3-stub-gen/src/generate.rs +++ b/pyo3-stub-gen/src/generate.rs @@ -370,6 +370,11 @@ impl StubInfo { .ok_or_else(|| anyhow!("Missing default module: {}", self.default_module_name)) } + pub fn from_pyproject_toml(path: impl AsRef) -> Result { + let pyproject = crate::pyproject::parse_toml(path)?; + Ok(Self::gather(&pyproject.project.name)) + } + pub fn gather(default_module_name: &str) -> Self { let mut modules: BTreeMap = BTreeMap::new(); diff --git a/pyo3-stub-gen/src/lib.rs b/pyo3-stub-gen/src/lib.rs index 97ac2fd..fa5e41c 100644 --- a/pyo3-stub-gen/src/lib.rs +++ b/pyo3-stub-gen/src/lib.rs @@ -1,4 +1,8 @@ pub use pyo3_stub_gen_derive as derive; -pub mod generate; +mod generate; +mod pyproject; pub mod type_info; + +pub type Result = anyhow::Result; +pub use generate::StubInfo; diff --git a/pyo3-stub-gen/src/pyproject.rs b/pyo3-stub-gen/src/pyproject.rs new file mode 100644 index 0000000..5fa8c44 --- /dev/null +++ b/pyo3-stub-gen/src/pyproject.rs @@ -0,0 +1,22 @@ +use anyhow::{bail, Result}; +use serde::{Deserialize, Serialize}; +use std::{fs, path::*}; + +pub fn parse_toml(path: impl AsRef) -> Result { + let path = path.as_ref(); + if path.file_name() != Some("pyproject.toml".as_ref()) { + bail!("{} is not a pyproject.toml", path.display()) + } + let out = toml::de::from_str(&fs::read_to_string(path)?)?; + Ok(out) +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct PyProject { + pub project: Project, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct Project { + pub name: String, +} From db098a1fa77257f346dff59624725616a3842933 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 5 Jan 2024 15:54:03 +0900 Subject: [PATCH 6/7] Add generated stub file --- .github/workflows/python.yml | 3 +++ .github/workflows/rust.yml | 3 +++ pyo3-stub-gen-testing/pyo3_stub_gen_testing.pyi | 17 +++++++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 pyo3-stub-gen-testing/pyo3_stub_gen_testing.pyi diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index 76781ef..3b71944 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -35,3 +35,6 @@ jobs: - name: Python Test run: pytest + + - name: Type check + run: pyright diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 39f2c66..0c68415 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -114,3 +114,6 @@ jobs: with: command: run args: --bin stub_gen --features stub_gen + + - name: Check if stub file is up to date + run: git diff --exit-code diff --git a/pyo3-stub-gen-testing/pyo3_stub_gen_testing.pyi b/pyo3-stub-gen-testing/pyo3_stub_gen_testing.pyi new file mode 100644 index 0000000..a554aa7 --- /dev/null +++ b/pyo3-stub-gen-testing/pyo3_stub_gen_testing.pyi @@ -0,0 +1,17 @@ +# This file is automatically generated by pyo3_stub_gen + +from typing import final, Any, List, Dict +from enum import Enum, auto + +def sum_as_string(a,b) -> str: + r""" + Returns the sum of two numbers as a string. + + Test of running doc-test + + ```rust + assert_eq!(2 + 2, 4); + ``` + """ + ... + From 02d2836129cccd462532ed143b6aea69e78c69b0 Mon Sep 17 00:00:00 2001 From: Toshiki Teramura Date: Fri, 5 Jan 2024 16:02:56 +0900 Subject: [PATCH 7/7] Re-export inventory crate as pyo3_stub_gen::inventory --- pyo3-stub-gen-derive/src/gen_stub.rs | 8 ++++---- pyo3-stub-gen-testing/Cargo.toml | 2 -- pyo3-stub-gen/src/lib.rs | 1 + 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pyo3-stub-gen-derive/src/gen_stub.rs b/pyo3-stub-gen-derive/src/gen_stub.rs index 06d069a..08edd21 100644 --- a/pyo3-stub-gen-derive/src/gen_stub.rs +++ b/pyo3-stub-gen-derive/src/gen_stub.rs @@ -91,7 +91,7 @@ pub fn pyclass(item: TokenStream2) -> Result { let inner = PyClassInfo::try_from(parse2::(item.clone())?)?; Ok(quote! { #item - inventory::submit! { + pyo3_stub_gen::inventory::submit! { #inner } }) @@ -101,7 +101,7 @@ pub fn pyclass_enum(item: TokenStream2) -> Result { let inner = PyEnumInfo::try_from(parse2::(item.clone())?)?; Ok(quote! { #item - inventory::submit! { + pyo3_stub_gen::inventory::submit! { #inner } }) @@ -111,7 +111,7 @@ pub fn pymethods(item: TokenStream2) -> Result { let inner = PyMethodsInfo::try_from(parse2::(item.clone())?)?; Ok(quote! { #item - inventory::submit! { + pyo3_stub_gen::inventory::submit! { #inner } }) @@ -122,7 +122,7 @@ pub fn pyfunction(attr: TokenStream2, item: TokenStream2) -> Result