Repository navigation
enum.Flag.__contains__ changed behavior since python 3.12 #131045
Description
Bug report
Bug description:
I noticed some strange behavior with enum.Flag an the __contains__ method in Python 3.12/3.13, as shown in the following examples.
Problem 1: Behavior changes at runtime
In the following code snippet the first print statement returns False, which is expected, since 3 is not a member of Weekday. However, the second print statement returns True, which is unexpected, since 3 is still not a member of Weekday.
import enum
class Weekday(enum.Flag):
MONDAY = 1
TUESDAY = 2
WEDNESDAY = 4
THURSDAY = 8
FRIDAY = 16
SATURDAY = 32
SUNDAY = 64
print(f"{3 in Weekday}") # => False (expected)
_ = Weekday.MONDAY | Weekday.TUESDAY
print(f"{3 in Weekday}") # => True (not expected)Problem 2: Behavior is not comparable to Python 3.11
Since Python 3.12 the behavior of Enum.__contains__ has changed, so that it is possible to compare not only with an enum-member, but also with non-enum-members (see here or here). So with Python 3.11 the code above will raise an TypeError: unsupported operand type(s) for 'in': 'int' and 'EnumType'. There you have to change the code to the following. But this in turn always produces unexpected behavior in Python 3.12/3.13.
import enum
class Weekday(enum.Flag):
MONDAY = 1
TUESDAY = 2
WEDNESDAY = 4
THURSDAY = 8
FRIDAY = 16
SATURDAY = 32
SUNDAY = 64
print(f"{Weekday(3) in Weekday}") # => Python 3.11: False (expected) Python 3.12/3.13: True (not expected)
_ = Weekday.MONDAY | Weekday.TUESDAY
print(f"{Weekday(3) in Weekday}") # => Python 3.11: False (expected) Python 3.12/3.13: True (not expected)Conclusion
I would have expected that in all cases the result is False, but since Python 3.12 it gets very strange...
CPython versions tested on:
3.12, 3.13
Operating systems tested on:
Windows
Linked PRs
Metadata
Metadata
Assignees
Labels
Projects
- StatusShow more project fieldsDone
Activity
picnixz commentedon Mar 10, 2025
1 | 2 becomes 3 and I think your surprise comes from the fact that a new member is automatically created. I think you can avoid this starting from 3.12 by specifying the boundary (namely a new member is not automatically created). I haven't checked but this should also have been documented somehow maybe indirectly.
I believe that the reason is because the result of the OR of two enum flag members should remain an enum flag member by default (3.12+) and thus we create that additional member on the fly.
With a different boundary the result would be a pure int and not an enum member. To check if this diagnosis is correct, print the type of the
_in your code.I can check on Wednesday as I'm unavailable until then (and I'm only answering from memory)
tomasr8 commentedon Mar 10, 2025
This behaviour is documented for
enum.IntFlag:Perhaps we should mention this in
enum.Flagas well?ethanfurman commentedon Mar 10, 2025
The intent is that
__contains__will returnTrueif either a member or a member value exists. In other words, ifWeekday(x)would return a valid member, thenx in Weekdayshould return True. The issue you found is that pseudo-members are not created until needed, but are then added to the Enum so thatWeekday(3) is Weekday(3)isTrue(i.e. pseudo-members are also singletons).One solution is to have
__contains__attempt a value -> member conversion so any pseudo-members are created.pythongh-131045: [Enum] fix flag containment checks when using values (…
gh-131045: [Enum] fix flag containment checks when using values (GH-1…
pythongh-131045: [Enum] fix flag containment checks when using values (…
[3.13] gh-131045: [Enum] fix flag containment checks when using values (
picnixz commentedon Mar 23, 2025
@ethanfurman What do you want to do with the backports? 3.12 is nearing its last bug fix so I wonder whether you are willing to review my PR (which had conflicts that I resolved but I'm not sure it's entirely correct for you)
ethanfurman commentedon Mar 23, 2025
Thanks for taking care of that; my PC has been having issues this last week.
[3.12] gh-131045: [Enum] fix flag containment checks when using values (
picnixz commentedon Mar 24, 2025
Thank you for the report and for the fix!