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/// A function in a deeply nested submodule.
144/// The module path must match the actual runtime structure:
145/// main_mod -> deep -> nested -> module
146#[gen_stub_pyfunction(module = "mixed.main_mod.deep.nested.module")]
147#[pyfunction]
148pub fn deep_function() -> String {
149    "Hello from deep nested module!".to_string()
150}
151
152#[pymodule]
153fn main_mod(m: &Bound<PyModule>) -> PyResult<()> {
154    // Add classes and functions to main module
155    m.add_class::<A>()?;
156    m.add_class::<B>()?;
157    m.add_function(wrap_pyfunction!(create_a, m)?)?;
158    m.add_function(wrap_pyfunction!(create_b, m)?)?;
159    m.add_function(wrap_pyfunction!(greet_main, m)?)?;
160
161    // Add submodules
162    mod_a(m)?;
163    mod_b(m)?;
164    int_mod(m)?;
165    deep_nested_mod(m)?;
166    Ok(())
167}
168
169fn mod_a(parent: &Bound<PyModule>) -> PyResult<()> {
170    let py = parent.py();
171    let sub = PyModule::new(py, "mod_a")?;
172    sub.add_class::<C>()?;
173    sub.add_function(wrap_pyfunction!(greet_a, &sub)?)?;
174    sub.add_function(wrap_pyfunction!(create_c, &sub)?)?;
175    sub.add_function(wrap_pyfunction!(test_module_with_python, &sub)?)?;
176    parent.add_submodule(&sub)?;
177    Ok(())
178}
179
180fn mod_b(parent: &Bound<PyModule>) -> PyResult<()> {
181    let py = parent.py();
182    let sub = PyModule::new(py, "mod_b")?;
183    sub.add_class::<D>()?;
184    sub.add_function(wrap_pyfunction!(greet_b, &sub)?)?;
185    sub.add_function(wrap_pyfunction!(create_d, &sub)?)?;
186    parent.add_submodule(&sub)?;
187    Ok(())
188}
189
190/// A dummy module to test namespace collision with built-in 'int'
191fn int_mod(parent: &Bound<PyModule>) -> PyResult<()> {
192    let py = parent.py();
193    let sub = PyModule::new(py, "int")?;
194    sub.add_function(wrap_pyfunction!(dummy_int_fun, &sub)?)?;
195    parent.add_submodule(&sub)?;
196    Ok(())
197}
198
199/// Creates the deep.nested.module submodule hierarchy
200fn deep_nested_mod(parent: &Bound<PyModule>) -> PyResult<()> {
201    let py = parent.py();
202    let deep = PyModule::new(py, "deep")?;
203    let nested = PyModule::new(py, "nested")?;
204    let module = PyModule::new(py, "module")?;
205
206    module.add_function(wrap_pyfunction!(deep_function, &module)?)?;
207    nested.add_submodule(&module)?;
208    deep.add_submodule(&nested)?;
209    parent.add_submodule(&deep)?;
210    Ok(())
211}
212
213// Test gen_function_from_python! with module parameter
214use pyo3_stub_gen::inventory::submit;
215
216submit! {
217    gen_function_from_python! {
218        module = "mixed.main_mod.mod_b",
219        r#"
220        import typing
221
222        def test_submit_with_module(values: typing.List[int]) -> int:
223            """Test function defined with gen_function_from_python! and module parameter"""
224        "#
225    }
226}
227
228// Test cases for __all__ generation escape hatches
229
230// Test 1: Wildcard re-export from mod_a to main_mod (internal module)
231// This will add all public items from mod_a to main_mod's __all__
232// Since mod_a is an internal module, the wildcard will be resolved to specific items
233pyo3_stub_gen::reexport_module_members!("mixed.main_mod", "mixed.main_mod.mod_a");
234
235// Test 2: Specific items re-export from mod_b to main_mod
236// This will add only D and greet_b to main_mod's __all__
237pyo3_stub_gen::reexport_module_members!("mixed.main_mod", "mixed.main_mod.mod_b", "D", "greet_b");
238
239// Test 3: Verbatim entry to main_mod module
240// This adds a custom entry. For testing, we define it as a module variable.
241pyo3_stub_gen::module_variable!("mixed.main_mod", "custom_export_name", &str, "test_value");
242pyo3_stub_gen::export_verbatim!("mixed.main_mod", "custom_export_name");
243
244define_stub_info_gatherer!(stub_info);
245
246/// Test of unit test for testing link problem
247#[cfg(test)]
248mod test {
249    #[test]
250    fn test() {
251        assert_eq!(2 + 2, 4);
252    }
253}