PyO3 / PyO3/pyo3

create_interface macro example from documentation does not produce usable classes when used inside pymodule

Open
#6,021 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
16.2k
Forks
1k
Avg merge
2d 6h
Merged PRs (30d)
66

Description

Bug Description

The documentation includes an example macro showing how to work around restrictions on generic using macros. However, when building modules using this macro, the classes defined inside the macro are not available to be imported.

Steps to Reproduce
  1. Create a new project with maturin init macro-reproducer
  2. Make lib.rs look like this:
use pyo3::prelude::*;

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

    struct GenericClass<T> {
        data: T,
    }

    macro_rules! create_interface {
        ($name: ident, $type: ident) => {
            #[pyclass]
            pub struct $name {
                inner: GenericClass<$type>,
            }
            #[pymethods]
            impl $name {
                #[new]
                pub fn new(data: $type) -> Self {
                    Self {
                        inner: GenericClass { data: data },
                    }
                }
            }
        };
    }

    create_interface!(IntClass, i64);
    create_interface!(FloatClass, f64);

    #[pyclass]
    pub struct StringClass {
        inner: GenericClass<String>,
    }

    #[pymethods]
    impl StringClass {
        #[new]
        pub fn new(data: String) -> Self {
            Self {
                inner: GenericClass { data: data },
            }
        }
    }
}
  1. Create a test:
def test_string_class():
    from macro_reproducer import StringClass
    s = StringClass("Hello, World!")

def test_int_class():
    from macro_reproducer import IntClass
    i = IntClass(42)

def test_float_class():
    from macro_reproducer import FloatClass
    f = FloatClass(3.14)
  1. Run test with pytest test.py
  2. IntClass and FloatClass tests fail with ImportError: cannot import name 'IntClass' from 'macro_reproducer'. Test for non-macro-coded StringClass passes
Backtrace

Your operating system and version

Ubuntu Linux 24.04

Your Python version (python --version)

Python 3.14.4

Your Rust version (rustc --version)

rustc 1.94.1 (e408947bf 2026-03-25)

Your PyO3 version

0.28.2

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

apt installed from deadsnakes, but issue is reproducible with uv installed Python too

Additional Info

No response

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the problem in the shown lib.rs and test.py files, then run pytest test.py to confirm that StringClass imports while the macro-generated classes do not. Start by tracing how PyO3's #[pymodule], #[pyclass], and #[pymethods] macros handle items generated by create_interface!. Done means IntClass and FloatClass can be imported and constructed like StringClass.

Written by the indexing model from the issue text.

Assessment

Tech stack
python, rust
Domain
api
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.