The Set of Enum Features

The development of the new Python enums took the form of a series of patches. While we put a lot of effort into supporting the old Enums (without promoting them), it is still possible that someone has a case where they cannot use the Python enums as they are now. To avoid people setting the environment flag to disable this completely, we implemented a way to select each combination of enum functions step by step with a specific set of flags.

The Possible Enum Flags

This is the table of all flags used to control the creation of Python enums.

Flag Name

Value

ENOPT_OLD_ENUM

0x00

(False) No longer possible since PySide 6.6

ENOPT_NEW_ENUM

0x01

(True) The default for PySide 6.4, full implementation

ENOPT_INHERIT_INT

0x02

Turn all Enum into IntEnum and Flag into IntFlag

ENOPT_GLOBAL_SHORTCUT

0x04

Re-add shortcuts for global enums

ENOPT_SCOPED_SHORTCUT

0x08

Re-add shortcuts for scoped enums

ENOPT_NO_FAKESHORTCUT

0x10

Don’t fake rename (forgiveness mode)

ENOPT_NO_FAKERENAMES

0x20

Don’t fake shortcuts (forgiveness mode)

ENOPT_NO_ZERODEFAULT

0x40

Don’t use zero default (forgiveness mode)

ENOPT_NO_MISSING

0x80

Don’t allow missing values in Enum

Such a set of flags can be defined either by the environment variable PYSIDE6_OPTION_PYTHON_ENUM or set by the Python variable sys.pyside6_option_python_enum before PySide6 is imported. The environment variable also supports arbitrary integer expressions by using ast.literal_eval.

ENOPT_OLD_ENUM (0x00)

This option completely disables the new enum implementation. Even though this is a valid option, we want to avoid it if possible. The goal is to eventually remove the old implementation. To make this possible, we have made the individual features of the enum implementation accessible as flags. This way, if users report problems, we may be able to provide a temporary solution before extending enum support accordingly.

ENOPT_NEW_ENUM (0x01)

In a perfect world, no one would choose anything other than this default setting. Unfortunately, reality is not always like that. That is why there are the following flags.

The most likely flags needed

If there are errors, they are likely to be the following: Either implicit assumptions are there that require IntEnum, or global enums are used that unfortunately cannot be replaced with tricks.

ENOPT_INHERIT_INT (0x02)

When this flag is set, all enum.Enum/enum.Flag classes are converted to enum.IntEnum/enum.IntFlag. This solves the most likely compatibility problem when switching to Python enums. The old Shiboken enums always inherit from int, but most Python enums do not.

It was a decision of Python developers not to let enums inherit from int by default, since no order should be implied. In most cases, inheritance from int can be avoided, either by using the value property or better by uplifting: instead of using AnEnum.AnInstance.value in a function that expects an int argument, you can also convert the integer to an enumeration instance after the call by AnEnum(int_arg) and use that in comparisons.

However, there are cases where this is not possible, and explicit support in PySide is simply not available. In those cases, you can use this flag as a workaround until we have implemented alternatives.

ENOPT_GLOBAL_SHORTCUT (0x04)

At the beginning of the Python enum implementation, we continued to support the shortcut behavior of Shiboken enums: the enum constants were mirrored into the enclosing scope. This was later emulated in the course of forgiveness mode. For enum classes in a PySide class this works fine, but for enum classes directly on the module level there is no good way to implement forgiveness.

It is unlikely that errors are hidden for global enums, because they should already produce an error during import. But for cases without access to the source code, you can help yourself with this flag.

A flag value of 0x6 is likely to solve the majority of problems.

Flags for completeness

The following flags complement the description of Python Enums. They essentially serve the better understanding of the implementation and make it fully transparent and customizable.

ENOPT_SCOPED_SHORTCUT (0x08)

For completeness, we also supported mirroring scoped enums, although this has since been replaced by forgiveness mode. If you want to try this, please also use the ENOPT_NO_FAKESHORTCUT flag (0x10), otherwise the effect of this flag will remain invisible.

ENOPT_NO_FAKERENAMES (0x10)

Forgiveness mode emulates renaming Enum.Flag classes back to Shiboken QFlags structures, which have slightly different names. So when such a defunct name is used, the system replaces it internally with the new enum.Flag structure. Unless special boundary problems are provoked, this replacement should work.

To see the effect of this renaming, you can turn it off with this flag.

ENOPT_NO_ZERODEFAULT (0x40)

As part of the forgiveness mode, Python enums can be created by a parameterless call, although Python enums actually force a parameter when called.

The effect can be examined if this flag is set to disable it.

ENOPT_NO_MISSING (0x80)

There are a few cases where Shiboken enums use missing values. In enum.Flag structures, this is allowed anyway because we have set the FlagBoundary.KEEP flag (see enum.py).

Normal enum.Enum structures don’t have this provision, but the enum module allows to pass a _missing_ function for customization.

Our way of dealing with this situation is to create a new fake enum.Enum class with the same name and a nameless instance, and pretend with an attribute setting that it has the same type. The additional instances created in this way are recorded in a class dict _sbk_missing_ in order to preserve their identity.

You will see the effect of not defining a _missing_ function if you set this flag.