Skip to content

Commit 4fbc371

Browse files
committed
Stub: allow custom imports
These imports are written in a small DSL like: ```rust #[pymodule(stubs = { from datetime import datetime as dt, time; from uuid import UUID; })] ``` Then parsed, sent as an AST inside the introspection data (following the same AST format as the type hints) and serialized by the introspection crate that merges these imports with the auto generated ones The `#[pymodule]` parameter is named `stub` because we might include some other features in the future like protocols
1 parent 42a35d9 commit 4fbc371

File tree

16 files changed

+665
-68
lines changed

16 files changed

+665
-68
lines changed

newsfragments/5877.added.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Introspection: allow to set custom stub imports in `#[pymodule]` macro

pyo3-introspection/src/introspection.rs

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::model::{
2-
Argument, Arguments, Attribute, Class, Constant, Expr, Function, Module, Operator,
3-
VariableLengthArgument,
2+
Argument, Arguments, Attribute, Class, Constant, Expr, Function, ImportAlias, Module, Operator,
3+
Statement, VariableLengthArgument,
44
};
55
use anyhow::{anyhow, bail, ensure, Context, Result};
66
use goblin::elf::section_header::SHN_XINDEX;
@@ -52,6 +52,7 @@ fn parse_chunks(chunks: &[Chunk], main_module_name: &str) -> Result<Module> {
5252
members,
5353
doc,
5454
incomplete,
55+
stubs,
5556
} = chunk
5657
{
5758
if name == main_module_name {
@@ -65,6 +66,7 @@ fn parse_chunks(chunks: &[Chunk], main_module_name: &str) -> Result<Module> {
6566
name,
6667
members,
6768
*incomplete,
69+
stubs,
6870
doc.as_deref(),
6971
&chunks_by_id,
7072
&chunks_by_parent,
@@ -82,6 +84,7 @@ fn convert_module(
8284
name: &str,
8385
members: &[String],
8486
mut incomplete: bool,
87+
stubs: &[ChunkStatement],
8588
docstring: Option<&str>,
8689
chunks_by_id: &HashMap<&str, &Chunk>,
8790
chunks_by_parent: &HashMap<&str, Vec<&Chunk>>,
@@ -114,6 +117,35 @@ fn convert_module(
114117
functions,
115118
attributes,
116119
incomplete,
120+
stubs: stubs
121+
.iter()
122+
.map(|statement| match statement {
123+
ChunkStatement::ImportFrom {
124+
module,
125+
names,
126+
level,
127+
} => Statement::ImportFrom {
128+
module: module.clone(),
129+
names: names
130+
.iter()
131+
.map(|alias| ImportAlias {
132+
name: alias.name.clone(),
133+
asname: alias.asname.clone(),
134+
})
135+
.collect(),
136+
level: *level,
137+
},
138+
ChunkStatement::Import { names } => Statement::Import {
139+
names: names
140+
.iter()
141+
.map(|alias| ImportAlias {
142+
name: alias.name.clone(),
143+
asname: alias.asname.clone(),
144+
})
145+
.collect(),
146+
},
147+
})
148+
.collect(),
117149
docstring: docstring.map(Into::into),
118150
})
119151
}
@@ -139,12 +171,14 @@ fn convert_members<'a>(
139171
members,
140172
incomplete,
141173
doc,
174+
stubs,
142175
} => {
143176
modules.push(convert_module(
144177
id,
145178
name,
146179
members,
147180
*incomplete,
181+
stubs,
148182
doc.as_deref(),
149183
chunks_by_id,
150184
chunks_by_parent,
@@ -672,6 +706,8 @@ enum Chunk {
672706
#[serde(default)]
673707
doc: Option<String>,
674708
incomplete: bool,
709+
#[serde(default)]
710+
stubs: Vec<ChunkStatement>,
675711
},
676712
Class {
677713
id: String,
@@ -739,6 +775,25 @@ struct ChunkArgument {
739775
annotation: Option<ChunkExpr>,
740776
}
741777

778+
#[derive(Deserialize)]
779+
#[serde(tag = "type", rename_all = "lowercase")]
780+
enum ChunkStatement {
781+
ImportFrom {
782+
module: String,
783+
names: Vec<ChunkAlias>,
784+
level: usize,
785+
},
786+
Import {
787+
names: Vec<ChunkAlias>,
788+
},
789+
}
790+
791+
#[derive(Deserialize)]
792+
struct ChunkAlias {
793+
name: String,
794+
asname: Option<String>,
795+
}
796+
742797
#[derive(Deserialize)]
743798
#[serde(tag = "type", rename_all = "lowercase")]
744799
enum ChunkExpr {

pyo3-introspection/src/model.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub struct Module {
66
pub functions: Vec<Function>,
77
pub attributes: Vec<Attribute>,
88
pub incomplete: bool,
9+
pub stubs: Vec<Statement>,
910
pub docstring: Option<String>,
1011
}
1112

@@ -74,6 +75,30 @@ pub struct VariableLengthArgument {
7475
pub annotation: Option<Expr>,
7576
}
7677

78+
/// A python statement
79+
///
80+
/// This is the `stmt` production of the [Python `ast` module grammar](https://docs.python.org/3/library/ast.html#abstract-grammar)
81+
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
82+
pub enum Statement {
83+
/// `from {module} import {names}`
84+
ImportFrom {
85+
module: String,
86+
names: Vec<ImportAlias>,
87+
level: usize,
88+
},
89+
/// `import {names}`
90+
Import { names: Vec<ImportAlias> },
91+
}
92+
93+
/// A python import alias `{name} as {asname}`
94+
///
95+
/// This is the `alias` production of the [Python `ast` module grammar](https://docs.python.org/3/library/ast.html#abstract-grammar)
96+
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
97+
pub struct ImportAlias {
98+
pub name: String,
99+
pub asname: Option<String>,
100+
}
101+
77102
/// A python expression
78103
///
79104
/// This is the `expr` production of the [Python `ast` module grammar](https://docs.python.org/3/library/ast.html#abstract-grammar)

0 commit comments

Comments
 (0)