Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#[pymodule(module="MODULE")] does not set the __module__ attribute #4870

Open
amorenoz opened this issue Jan 24, 2025 · 1 comment
Open

#[pymodule(module="MODULE")] does not set the __module__ attribute #4870

amorenoz opened this issue Jan 24, 2025 · 1 comment
Labels

Comments

@amorenoz
Copy link

amorenoz commented Jan 24, 2025

Bug Description

#[pyclass(module = "module")]
struct Foo {}

Correctly sets the __module__ attribute of the class.

The same behavior for other (sub) pymodules is expected.
The attribute is needed for tools such as https://github.com/mkdocstrings/griffe to properly find the submodule. See PyO3/maturin#1365.

What seems to happen is the module value of the submodule is used to determine the module value of the (inlined) pyclasses.

Steps to Reproduce

  1. Create a module with a submodule. Use module="..."
/// Module documentation: A Python module implemented in Rust.
#[pymodule]
mod mymodule {
    use pyo3::prelude::*;

    /// Sub-Module documentation!!!
    #[pymodule(module="mymodule.mymodule.submodule")]
    mod submodule {
    	use super::*;

        #[pyclass]
        struct Foo{};

        /// Hack: workaround for https://github.com/PyO3/pyo3/issues/759   
        fn init(m: &Bound<'_, PyModule>) -> PyResult<()> {
            Python::with_gil(|py| {
                py.import("sys")?
                    .getattr("modules")?
                    .set_item("mymodule.submodule", m)
            })
        }
   }
}
  1. Check the __module__ attribute`
>>> from mymodule import submodule
>>> submodule.__module__
Traceback (most recent call last):
  File "<python-input-2>", line 1, in <module>
    submodule.__module__
AttributeError: module 'submodule' has no attribute '__module__'

Backtrace

Your operating system and version

Fedora 41

Your Python version (python --version)

Python 3.13.1

Your Rust version (rustc --version)

rustc 1.81.0 (eeb90cda1 2024-09-04)

Your PyO3 version

pyo3 v0.23.3 (https://github.com/PyO3/pyo3#5c363b5d)

How did you install python? Did you use a virtualenv?

dnf install python

Additional Info

As a workaround, __module__ attribute could be set manually:

        fn init(m: &Bound<'_, PyModule>) -> PyResult<()> {
            Python::with_gil(|py| {
                py.import("sys")?
                    .getattr("modules")?
                    .set_item("mymodule.submodule", m)
            })?;
            m.setattr("__module__", "mymodule.mymodule.submodule") // <-- Add this
        }
@amorenoz amorenoz added the bug label Jan 24, 2025
@amorenoz
Copy link
Author

Sorry if this is the intended behavior, I might just be confused here.

Again, I just want griffe / mkdocs to generate documenation for me.

Interestingly, it seems that if a struct is embedded inside a module (not exported with #[pymodule_export], it's __module__ is, by default, the value you put into it's module's module="..." + the name of the module itself.

This is a bit confusing so I'll show code:

#[pymodule]
mod mymodule {
    use pyo3::prelude::*;

    #[pymodule(module="mymodule.mymodule")]
    mod submodule {
    	use super::*;

        #[pyclass]
        /// FooBar is also important
        struct FooBar {}

        /// Hack: workaround for https://github.com/PyO3/pyo3/issues/759
        #[pymodule_init]
        fn init(m: &Bound<'_, PyModule>) -> PyResult<()> {
            Python::with_gil(|py| {
                py.import("sys")?
                    .getattr("modules")?
                    .set_item("mymodule.submodule", m)
            })
        }
     }
}

results in:

>>> from mymodule import submodule
>>> submodule.FooBar.__module__
'mymodule.mymodule.submodule'
>>> 

Which is correct.

Now, for griffe to work, I need the module's __module__ to be mymodule.mymodule.submodule (as I posted here).

However, if I change it:

   #[pymodule(module="mymodule.mymodule.submodule")]
    mod submodule { ... }

griffe will still not work because:

>>> from mymodule import submodule
>>> submodule.__module__
Traceback (most recent call last):
  File "<python-input-1>", line 1, in <module>
    submodule.__module__
AttributeError: module 'submodule' has no attribute '__module__'

But, the issue is now that:

>>> submodule.FooBar.__module__
'mymodule.mymodule.submodule.submodule'

I can fix griffe by manually setting the __module__ attribute but if this "bug" (I am not sure it one is now :-D) gets fixed I would have to manually set #[pyclass(module="mymodule.mymodule.submodule]" on FooBar.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant