Sitelet https://github.com/python/cpython/issues/107538
Skip to content

Inverted enum.Flag #107538

Description

@GrafLearnt

Bug report

Checklist

  • I am confident this is a bug in CPython, not a bug in a third-party project
  • I have searched the CPython issue tracker, and am confident this bug has not been reported before

A clear and concise description of the bug

Adding inverted option to enum.Flag brakes logic of enum.
I assume it caused by ambiguous implementation of bitwise NOT in Flag which processes ~value option different inside class scope and outside class scope unlike bitwise OR

import enum

class X(enum.Flag):
  a = enum.auto()
  b = enum.auto()
  c = a | b


assert list(X) == [X.a, X.b]
assert ~X.a == X.b
assert list(~X.a) == [X.b]


class Y(enum.Flag):
  a = enum.auto()
  b = enum.auto()
  c = a | b
  d = ~a  # this line brakes the code


assert list(Y) == [Y.a, Y.b]
assert ~Y.a == Y.b  # AssertionError
assert list(~Y.a) == [Y.b]  # ValueError: -2 is not a positive integer

Traceback:

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/enum.py", line 1482, in __iter__
  yield from self._iter_member_(self._value_)
File "/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/enum.py", line 1359, in _iter_member_by_value_
  for val in _iter_bits_lsb(value & cls._flag_mask_):
File "/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/enum.py", line 122, in _iter_bits_lsb
  raise ValueError('%r is not a positive integer' % original)
ValueError: -2 is not a positive integer

Your environment

  • CPython versions tested on: Clang 14.0.3 (clang-1403.0.22.14.1)
  • Operating system and architecture: 22.5.0 Darwin Kernel Version 22.5.0: Thu Jun 8 22:22:20 PDT 2023; root:xnu-8796.121.3~7/RELEASE_ARM64_T6000 arm64

Linked PRs

Activity

added
stdlibStandard Library Python modules in the Lib/ directory
on Aug 1, 2023

Eclips4 commented on Aug 1, 2023

@Eclips4
Member

Cannot reproduce on current main. Seems that this case fixed by #105542

ethanfurman commented on Aug 1, 2023

@ethanfurman
Member

It's not failing, and the first three work correctly, but Y.d should be an alias for Y.b, not -2.

GrafLearnt commented on Aug 1, 2023

@GrafLearnt
Author

@ethanfurman may be you are right but I find following quite confusing:
~a changed while a remaining the same

class X(enum.Flag):
    a = enum.auto()
    b = enum.auto()
    c = enum.auto()
>>> list(X)
[<X.a: 1>, <X.b: 2>, <X.c: 4>]
>>> ~X.a
<X.b|c: 6>
>>> list(~X.a)
[<X.b: 2>, <X.c: 4>]
class Y(enum.Flag):
    a = enum.auto()
    b = enum.auto()
    c = enum.auto()
    d = ~a
>>> list(Y)
[<Y.a: 1>, <Y.b: 2>, <Y.c: 4>]
>>> ~Y.a
<Y.d: -2>
>>> list(~Y.a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/enum.py", line 1482, in __iter__
    yield from self._iter_member_(self._value_)
  File "/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/enum.py", line 1359, in _iter_member_by_value_
    for val in _iter_bits_lsb(value & cls._flag_mask_):
  File "/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/enum.py", line 122, in _iter_bits_lsb
    raise ValueError('%r is not a positive integer' % original)
ValueError: -2 is not a positive integer

ethanfurman commented on Aug 1, 2023

@ethanfurman
Member

@GrafLearnt, in your latest example, d should be 6 (i.e. b|c). Thanks for the reminder, this will be a more involved fix than I was originally thinking.

GrafLearnt commented on Aug 1, 2023

@GrafLearnt
Author

@ethanfurman d = b | c will work perfectly fine until someone add new member without adding it to d

ethanfurman commented on Aug 1, 2023

@ethanfurman
Member

Well, presumably, if they write d = b|c that's what they want; while d = ~a means every member that's not a.

GrafLearnt commented on Aug 1, 2023

@GrafLearnt
Author

@ethanfurman
I love it:

class X(enum.Flag):
    a = enum.auto()
    b = enum.auto()
    c = enum.auto()
>>> ~X.a
<X.b|c: 6>

But this doesn't look correct to me:

class Y(enum.Flag):
    a = enum.auto()
    b = enum.auto()
    c = enum.auto()
    d = ~a
>>> ~Y.a
<Y.d: -2>

Kindly ask to clarify if I am allowed to define inverted.
It brakes iteration of ~Y.a
Also I get following:

>>> ~Y.b
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/enum.py", line 1542, in __invert__
    self._inverted_ = self.__class__(self._flag_mask_ ^ self._value_)
                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/enum.py", line 711, in __call__
    return cls.__new__(cls, value)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/enum.py", line 1136, in __new__
    raise exc
  File "/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/enum.py", line 1113, in __new__
    result = cls._missing_(value)
             ^^^^^^^^^^^^^^^^^^^^
  File "/opt/homebrew/Cellar/python@3.11/3.11.4_1/Frameworks/Python.framework/Versions/3.11/lib/python3.11/enum.py", line 1399, in _missing_
    raise ValueError(
ValueError: <flag 'Y'> invalid value -3
    given 0b1 01
  allowed 0b1 11

ethanfurman commented on Aug 1, 2023

@ethanfurman
Member

Defining ~a as a member is what is not working correctly.

I believe that last error is fixed in 3.11.5.

GrafLearnt commented on Aug 1, 2023

@GrafLearnt
Author

@ethanfurman is defining inverted restricted or will be fixed?

Works fine in Python 3.10.12:

>>> ~Y.b
<Y.c|a: 5>

ethanfurman commented on Aug 1, 2023

@ethanfurman
Member

Will be fixed.

added a commit that references this issue on Jul 10, 2025
added a commit that references this issue on Jul 11, 2025
added a commit that references this issue on Jul 12, 2025
added a commit that references this issue on Jul 13, 2025
added a commit that references this issue on Aug 4, 2025
added a commit that references this issue on Aug 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

stdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions