pyo3 icon indicating copy to clipboard operation
pyo3 copied to clipboard

"Variant or associated item not found in `Enum`" when a variant is disabled through a feature

Open jeberger opened this issue 1 year ago • 3 comments

Bug Description

When using Cargo features to conditionally enable enum variants, #[pyclass] tries to wrap disabled variants, which causes the compilation to fail.

Steps to Reproduce

Try to compile this code without enabling optional_feature:

#[pyclass]
enum Enum {
    AlwaysAvailable,
    #[cfg (feature = "optional_feature")]
    OptionalVariant,
}

This fails with error:

error[E0599]: no variant or associated item named `OptionalVariant` found for enum `Enum` in the current scope
   --> src\lib.rs:5:5
    |
  2 |      enum Enum {
    |  ____------------
    | |    |
    | |    variant or associated item `OptionalVariant` not found for this enum
  3 | |       AlwaysAvailable,
  4 | |       #[cfg (feature = "optional_feature")]
  5 | |       OptionalVariant,
    | |      -^^^^^^^^^^^^^^^ variant or associated item not found in `Enum`
    | |______|
    |

For more information about this error, try `rustc --explain E0599`.

(the code works fine when optional_feature is enabled)

Backtrace

No response

Your operating system and version

Windows 10 & ArchLinux

Your Python version (python --version)

Python 3.11.7

Your Rust version (rustc --version)

rustc 1.80.0 (051478957 2024-07-21)

Your PyO3 version

0.20.3

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

Installer + virtualenv

Additional Info

No response

jeberger avatar Aug 02 '24 07:08 jeberger

Thanks for the report - looks like we're missing handling for #[cfg] inside our enum code. There are other places in the proc-macros where we handle cfgs, so it should be possible to do similar here. PR welcome!

davidhewitt avatar Aug 02 '24 08:08 davidhewitt

Hi, I'm a long time fan of PyO3 and I'm interested about learning some more about how it works. I've come up with a fix for this bug in the case of simple enums: https://github.com/PyO3/pyo3/compare/main...jeff-k:pyo3:enum-feature-variants

I see in the pyimpl.rs and module.rs modules that the solution is to propagate the cfg attributes to the generated code, so that's what I've done.

Here's a test case that could go in tests/test_field_cfg.rs:

#[pyclass(eq, eq_int)]
#[derive(PartialEq)]
enum CfgSimpleEnum {
    #[cfg(any())]
    pub DisabledVariant,
    #[cfg(not(any()))]
    pub EnabledVariant,
}

#[test]
fn test_cfg_simple_enum() {
    Python::with_gil(|py| {
        let simple = py.get_type::<CfgSimpleEnum>();
        pyo3::py_run!(py, simple, r#"
            assert hasattr(simple, "EnabledVariant")
            assert not hasattr(simple, "DisabledVariant")
        "#);
    })
}

This should fix this specific bug, but motivates a similar fix for complex enums. I'd be happy to be assigned this task.

Thanks for the amazing crate.

jeff-k avatar Sep 01 '24 13:09 jeff-k

@jeff-k sounds perfect and thank you for both the appreciation and contribution. If you're willing to open a PR, I'll gladly review. 🙏

davidhewitt avatar Sep 01 '24 13:09 davidhewitt

Fixed in #4509

davidhewitt avatar Nov 19 '24 22:11 davidhewitt