mixed/
lib.rs

1use pyo3::prelude::*;
2use pyo3_stub_gen::{define_stub_info_gatherer, derive::*};
3
4mod module_override_tests;
5
6// Classes that can be cross-referenced between modules (from mixed_import_type)
7#[gen_stub_pyclass]
8#[pyclass(module = "mixed.main_mod")]
9#[derive(Debug, Clone)]
10struct A {
11    x: usize,
12}
13
14#[gen_stub_pymethods]
15#[pymethods]
16impl A {
17    fn show_x(&self) {
18        println!("x = {}", self.x);
19    }
20}
21
22#[gen_stub_pyfunction(module = "mixed.main_mod")]
23#[pyfunction]
24fn create_a(x: usize) -> A {
25    A { x }
26}
27
28// Class without explicit module specification
29#[gen_stub_pyclass]
30#[pyclass]
31#[derive(Debug, Clone)]
32struct B {
33    x: usize,
34}
35
36#[gen_stub_pymethods]
37#[pymethods]
38impl B {
39    fn show_x(&self) {
40        println!("x = {}", self.x);
41    }
42}
43
44#[gen_stub_pyfunction]
45#[pyfunction]
46fn create_b(x: usize) -> B {
47    B { x }
48}
49
50// Original functions from mixed
51#[gen_stub_pyfunction(module = "mixed.main_mod.mod_a")]
52#[pyfunction(name = "greet_a")]
53pub fn greet_a() {
54    println!("Hello from mod_A!")
55}
56
57// Type alias in mod_a to test wildcard re-export
58pyo3_stub_gen::type_alias!("mixed.main_mod.mod_a", ModAAlias = i32);
59
60#[gen_stub_pyfunction(module = "mixed.main_mod")]
61#[pyfunction(name = "greet_main")]
62pub fn greet_main() {
63    println!("Hello from main_mod!")
64}
65
66#[gen_stub_pyfunction(module = "mixed.main_mod.mod_b")]
67#[pyfunction(name = "greet_b")]
68pub fn greet_b() {
69    println!("Hello from mod_B!")
70}
71
72// Class C in mod_a that references A and B (demonstrates cross-module type references)
73#[gen_stub_pyclass]
74#[pyclass(module = "mixed.main_mod.mod_a")]
75#[derive(Debug)]
76struct C {
77    a: A,
78    b: B,
79}
80
81#[gen_stub_pymethods]
82#[pymethods]
83impl C {
84    fn show_x(&self) {
85        println!("a.x:");
86        self.a.show_x();
87        println!("b.x:");
88        self.b.show_x();
89    }
90}
91
92#[gen_stub_pyfunction(module = "mixed.main_mod.mod_a")]
93#[pyfunction]
94fn create_c(a: A, b: B) -> C {
95    C { a, b }
96}
97
98// Simple class in mod_b (from mixed)
99#[gen_stub_pyclass]
100#[pyclass(module = "mixed.main_mod.mod_b")]
101#[derive(Debug)]
102struct D {
103    x: usize,
104}
105
106#[gen_stub_pymethods]
107#[pymethods]
108impl D {
109    fn show_x(&self) {
110        println!("x = {}", self.x);
111    }
112}
113
114#[gen_stub_pyfunction(module = "mixed.main_mod.mod_b")]
115#[pyfunction]
116fn create_d(x: usize) -> D {
117    D { x }
118}
119
120// Function in int submodule to test namespace collision
121#[gen_stub_pyfunction(module = "mixed.main_mod.int")]
122#[pyfunction]
123fn dummy_int_fun(x: usize) -> usize {
124    x
125}
126
127// Test function with both module and python parameters (bug reproduction case)
128// This should be placed in mod_a submodule
129#[gen_stub_pyfunction(
130    module = "mixed.main_mod.mod_a",
131    python = r#"
132    import typing
133
134    def test_module_with_python(x: typing.Generator[int, None, None]) -> int:
135        """Test function with both module and python parameters"""
136"#
137)]
138#[pyfunction]
139fn test_module_with_python(_x: &Bound<PyAny>) -> PyResult<usize> {
140    Ok(42)
141}
142
143#[pymodule]
144fn main_mod(m: &Bound<PyModule>) -> PyResult<()> {
145    // Add classes and functions to main module
146    m.add_class::<A>()?;
147    m.add_class::<B>()?;
148    m.add_function(wrap_pyfunction!(create_a, m)?)?;
149    m.add_function(wrap_pyfunction!(create_b, m)?)?;
150    m.add_function(wrap_pyfunction!(greet_main, m)?)?;
151
152    // Add submodules
153    mod_a(m)?;
154    mod_b(m)?;
155    int_mod(m)?;
156    Ok(())
157}
158
159fn mod_a(parent: &Bound<PyModule>) -> PyResult<()> {
160    let py = parent.py();
161    let sub = PyModule::new(py, "mod_a")?;
162    sub.add_class::<C>()?;
163    sub.add_function(wrap_pyfunction!(greet_a, &sub)?)?;
164    sub.add_function(wrap_pyfunction!(create_c, &sub)?)?;
165    sub.add_function(wrap_pyfunction!(test_module_with_python, &sub)?)?;
166    parent.add_submodule(&sub)?;
167    Ok(())
168}
169
170fn mod_b(parent: &Bound<PyModule>) -> PyResult<()> {
171    let py = parent.py();
172    let sub = PyModule::new(py, "mod_b")?;
173    sub.add_class::<D>()?;
174    sub.add_function(wrap_pyfunction!(greet_b, &sub)?)?;
175    sub.add_function(wrap_pyfunction!(create_d, &sub)?)?;
176    parent.add_submodule(&sub)?;
177    Ok(())
178}
179
180/// A dummy module to test namespace collision with built-in 'int'
181fn int_mod(parent: &Bound<PyModule>) -> PyResult<()> {
182    let py = parent.py();
183    let sub = PyModule::new(py, "int")?;
184    sub.add_function(wrap_pyfunction!(dummy_int_fun, &sub)?)?;
185    parent.add_submodule(&sub)?;
186    Ok(())
187}
188
189// Test gen_function_from_python! with module parameter
190use pyo3_stub_gen::inventory::submit;
191
192submit! {
193    gen_function_from_python! {
194        module = "mixed.main_mod.mod_b",
195        r#"
196        import typing
197
198        def test_submit_with_module(values: typing.List[int]) -> int:
199            """Test function defined with gen_function_from_python! and module parameter"""
200        "#
201    }
202}
203
204// Test cases for __all__ generation escape hatches
205
206// Test 1: Wildcard re-export from mod_a to main_mod (internal module)
207// This will add all public items from mod_a to main_mod's __all__
208// Since mod_a is an internal module, the wildcard will be resolved to specific items
209pyo3_stub_gen::reexport_module_members!("mixed.main_mod", "mixed.main_mod.mod_a");
210
211// Test 2: Specific items re-export from mod_b to main_mod
212// This will add only D and greet_b to main_mod's __all__
213pyo3_stub_gen::reexport_module_members!("mixed.main_mod", "mixed.main_mod.mod_b", "D", "greet_b");
214
215// Test 3: Verbatim entry to root mixed module
216// This adds a custom entry. For testing, we define it as a module variable.
217pyo3_stub_gen::module_variable!("mixed", "custom_export_name", &str, "test_value");
218pyo3_stub_gen::export_verbatim!("mixed", "custom_export_name");
219
220define_stub_info_gatherer!(stub_info);
221
222/// Test of unit test for testing link problem
223#[cfg(test)]
224mod test {
225    #[test]
226    fn test() {
227        assert_eq!(2 + 2, 4);
228    }
229}