Skip to content

Commit

Permalink
fix: Track all submodules instead of one (#106)
Browse files Browse the repository at this point in the history
Currently , submodule relation is tracked as `map: BTreeMap<String,
String>` in `register_submodules` function:


https://github.com/Jij-Inc/pyo3-stub-gen/blob/bb791fb6d20e6c019aef32437d59a9b6349ff19e/pyo3-stub-gen/src/generate/stub_info.rs#L70-L85

This resulted in wrong behaviour when a module has multiple submodules -
it contains only one `from . import submod` for the last processed
submodule.
This PR fixes this by using `BTreeMap<String, BTreeSet<String>>`
instead. It also adds `examples/mixed_sub_multiple` case to check the
intended behaviour for this case.
  • Loading branch information
konn authored Dec 10, 2024
1 parent fa79ac7 commit c93b11d
Show file tree
Hide file tree
Showing 13 changed files with 155 additions and 5 deletions.
1 change: 1 addition & 0 deletions .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jobs:
- pure
- mixed
- mixed_sub
- mixed_sub_multiple
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ members = [
"examples/pure",
"examples/mixed",
"examples/mixed_sub",
"examples/mixed_sub_multiple",
]
resolver = "2"

Expand Down
16 changes: 16 additions & 0 deletions examples/mixed_sub_multiple/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "mixed_sub_multiple"
version.workspace = true
edition.workspace = true

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
env_logger.workspace = true
pyo3-stub-gen = { path = "../../pyo3-stub-gen" }
pyo3.workspace = true

[[bin]]
name = "stub_gen"
doc = false
15 changes: 15 additions & 0 deletions examples/mixed_sub_multiple/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[build-system]
requires = ["maturin>=1.1,<2.0"]
build-backend = "maturin"

[project]
name = "mixed_sub_multiple"
requires-python = ">=3.9"

[project.optional-dependencies]
test = ["pytest", "pyright", "ruff"]

[tool.maturin]
python-source = "python"
module-name = "mixed_sub_multiple.main_mod"
features = ["pyo3/extension-module"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# This file is automatically generated by pyo3_stub_gen
# ruff: noqa: E501, F401

from . import mod_a
from . import mod_b

def greet_main() -> None:
...

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# This file is automatically generated by pyo3_stub_gen
# ruff: noqa: E501, F401


def greet_a() -> None:
...

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# This file is automatically generated by pyo3_stub_gen
# ruff: noqa: E501, F401


def greet_b() -> None:
...

Empty file.
8 changes: 8 additions & 0 deletions examples/mixed_sub_multiple/src/bin/stub_gen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
use pyo3_stub_gen::Result;

fn main() -> Result<()> {
env_logger::Builder::from_env(env_logger::Env::default().filter_or("RUST_LOG", "info")).init();
let stub = mixed_sub_multiple::stub_info()?;
stub.generate()?;
Ok(())
}
57 changes: 57 additions & 0 deletions examples/mixed_sub_multiple/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
use pyo3::prelude::*;
use pyo3_stub_gen::{define_stub_info_gatherer, derive::*};

#[gen_stub_pyfunction(module = "mixed_sub_multiple.main_mod.mod_a")]
#[pyfunction(name = "greet_a")]
pub fn greet_a() {
println!("Hello from mod_A!")
}

#[gen_stub_pyfunction(module = "mixed_sub_multiple.main_mod")]
#[pyfunction(name = "greet_main")]
pub fn greet_main() {
println!("Hello from main_mod!")
}

#[gen_stub_pyfunction(module = "mixed_sub_multiple.main_mod.mod_b")]
#[pyfunction(name = "greet_b")]
pub fn greet_b() {
println!("Hello from mod_B!")
}

#[pymodule]
fn main_mod(m: &Bound<PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(greet_main, m)?)?;
mod_a(m)?;
mod_b(m)?;
Ok(())
}

#[pymodule]
fn mod_a(parent: &Bound<PyModule>) -> PyResult<()> {
let py = parent.py();
let sub = PyModule::new_bound(py, "mod_a")?;

Check failure on line 33 in examples/mixed_sub_multiple/src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

use of deprecated associated function `pyo3::types::PyModule::new_bound`: renamed to `PyModule::new`

error: use of deprecated associated function `pyo3::types::PyModule::new_bound`: renamed to `PyModule::new` --> examples/mixed_sub_multiple/src/lib.rs:33:25 | 33 | let sub = PyModule::new_bound(py, "mod_a")?; | ^^^^^^^^^ | = note: `-D deprecated` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(deprecated)]`

Check warning on line 33 in examples/mixed_sub_multiple/src/lib.rs

View workflow job for this annotation

GitHub Actions / test

use of deprecated associated function `pyo3::types::PyModule::new_bound`: renamed to `PyModule::new`
sub.add_function(wrap_pyfunction!(greet_a, &sub)?)?;
parent.add_submodule(&sub)?;
Ok(())
}

#[pymodule]
fn mod_b(parent: &Bound<PyModule>) -> PyResult<()> {
let py = parent.py();
let sub = PyModule::new_bound(py, "mod_b")?;

Check failure on line 42 in examples/mixed_sub_multiple/src/lib.rs

View workflow job for this annotation

GitHub Actions / clippy

use of deprecated associated function `pyo3::types::PyModule::new_bound`: renamed to `PyModule::new`

error: use of deprecated associated function `pyo3::types::PyModule::new_bound`: renamed to `PyModule::new` --> examples/mixed_sub_multiple/src/lib.rs:42:25 | 42 | let sub = PyModule::new_bound(py, "mod_b")?; | ^^^^^^^^^

Check warning on line 42 in examples/mixed_sub_multiple/src/lib.rs

View workflow job for this annotation

GitHub Actions / test

use of deprecated associated function `pyo3::types::PyModule::new_bound`: renamed to `PyModule::new`
sub.add_function(wrap_pyfunction!(greet_b, &sub)?)?;
parent.add_submodule(&sub)?;
Ok(())
}

define_stub_info_gatherer!(stub_info);

/// Test of unit test for testing link problem
#[cfg(test)]
mod test {
#[test]
fn test() {
assert_eq!(2 + 2, 4);
}
}
13 changes: 13 additions & 0 deletions examples/mixed_sub_multiple/tests/test_mixed_sub_multiple.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from mixed_sub_multiple import main_mod


def test_main_mod():
main_mod.greet_main()


def test_sub_mod_a():
main_mod.mod_a.greet_a()


def test_sub_mod_b():
main_mod.mod_b.greet_b()
17 changes: 12 additions & 5 deletions pyo3-stub-gen/src/generate/stub_info.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
use crate::{generate::*, pyproject::PyProject, type_info::*};
use anyhow::{Context, Result};
use std::{collections::BTreeMap, fs, io::Write, path::*};
use std::{
collections::{BTreeMap, BTreeSet},
fs,
io::Write,
path::*,
};

#[derive(Debug, Clone, PartialEq)]
pub struct StubInfo {
Expand Down Expand Up @@ -68,18 +73,20 @@ impl StubInfoBuilder {
}

fn register_submodules(&mut self) {
let mut map = BTreeMap::new();
let mut map: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
for module in self.modules.keys() {
let path = module.split('.').collect::<Vec<_>>();
let n = path.len();
if n <= 1 {
continue;
}
map.insert(path[..n - 1].join("."), path[n - 1].to_string());
map.entry(path[..n - 1].join("."))
.or_default()
.insert(path[n - 1].to_string());
}
for (parent, child) in map {
for (parent, children) in map {
if let Some(module) = self.modules.get_mut(&parent) {
module.submodules.insert(child);
module.submodules.extend(children);
}
}
}
Expand Down

0 comments on commit c93b11d

Please sign in to comment.