.. _enum-features:

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.
