Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enum.CONFORM behavior breaks backwards compatibility #103365

Closed
benburrill opened this issue Apr 8, 2023 · 4 comments
Closed

enum.CONFORM behavior breaks backwards compatibility #103365

benburrill opened this issue Apr 8, 2023 · 4 comments
Assignees
Labels
stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@benburrill
Copy link

benburrill commented Apr 8, 2023

Encountered yet another breaking change for enums. There are two problems here:

  • CONFORM doesn't "conform" to members that are partially unsupported
  • CONFORM does not match the previous behavior for Flag. It used to be more similar to STRICT, but STRICT also doesn't allow partially unsupported members, while Flag in 3.10 did.

Example to reproduce:

import enum
class SkipFlag(enum.Flag):
    A = 1
    B = 2
    C = 4 | B

print(SkipFlag.C in (SkipFlag.A|SkipFlag.C))

class SkipIntFlag(enum.IntFlag):
    A = 1
    B = 2
    C = 4 | B

print(SkipIntFlag.C in (SkipIntFlag.A|SkipIntFlag.C))

print(SkipIntFlag(42).value)
print(SkipFlag(42).value)

In Python 3.10.6, this code outputs:

True
True
42
Traceback (most recent call last):
...
ValueError: 42 is not a valid SkipFlag

In Python 3.11.2:

False
True
42
2

Having such members as C is useful to create a flags that are distinct from B but always implies B.

In a previous comment bug report it was stated that the previous behavior of Flag was CONFORM:

Actually, won't need a deprecation period, since the proper default for Flag is CONFORM as that matches previous behavior.

Originally posted by @ethanfurman in #96865 (comment)

Clearly this is incorrect. The previous behavior was more similar to STRICT, but there appears to be no precise equivalent in Python 3.11 to the old behavior. Using STRICT in Python 3.11, SkipFlag.A | SkipFlag.C would fail to be created completely.

Linked PRs

@benburrill benburrill added the type-bug An unexpected behavior, bug, or error label Apr 8, 2023
@sobolevn sobolevn added the stdlib Python modules in the Lib dir label Apr 8, 2023
@sunmy2019
Copy link
Member

sunmy2019 commented Apr 8, 2023

With #24215, only single-bit values will be considered in Flag when using bitwise ops like &, |, ^, ~. Multi-bit values are only considered an alias.

Thus SkipIntFlag.A | SkipIntFlag.C is actually SkipIntFlag.A | SkipIntFlag.B, since the 0b100 bit is discarded. But __contains__ will only consider its underlying values.

You will need an element with value 4 inside of SkipFlag.

This answers "why it behaves like this". Related Issues: #82431

@sunmy2019
Copy link
Member

Only single-bit values will be considered in Flag

This is documented with an inconspicuous line.

https://docs.python.org/3.10/library/enum.html#combining-members-of-flag
https://docs.python.org/3.11/howto/enum.html#flag-and-intflag-minutia

I think this should be added to the API reference here:
https://docs.python.org/3/library/enum.html#enum.Flag

@benburrill
Copy link
Author

https://docs.python.org/3.11/howto/enum.html#flag-and-intflag-minutia

It's also documented there that the default for Flag in Python 3.11 is STRICT, but it's really CONFORM

ethanfurman added a commit that referenced this issue Apr 13, 2023
STRICT boundary:

- fix bitwise operations
- make default for Flag
miss-islington pushed a commit to miss-islington/cpython that referenced this issue Apr 13, 2023
STRICT boundary:

- fix bitwise operations
- make default for Flag
(cherry picked from commit 2194071)

Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
miss-islington added a commit that referenced this issue Apr 13, 2023
STRICT boundary:

- fix bitwise operations
- make default for Flag
(cherry picked from commit 2194071)

Co-authored-by: Ethan Furman <ethan@stoneleaf.us>
carljm added a commit to carljm/cpython that referenced this issue Apr 13, 2023
* main:
  pythongh-103479: [Enum] require __new__ to be considered a data type (pythonGH-103495)
  pythongh-103365: [Enum] STRICT boundary corrections (pythonGH-103494)
  pythonGH-103488: Use return-offset, not yield-offset. (pythonGH-103502)
  pythongh-103088: Fix test_venv error message to avoid bytes/str warning (pythonGH-103500)
  pythonGH-103082: Turn on branch events for FOR_ITER instructions. (python#103507)
  pythongh-102978: Fix mock.patch function signatures for class and staticmethod decorators (python#103228)
  pythongh-103462: Ensure SelectorSocketTransport.writelines registers a writer when data is still pending (python#103463)
  pythongh-95299: Rework test_cppext.py to not invoke setup.py directly (python#103316)
@ethanfurman
Copy link
Member

@benburrill Thank you for the bug report. STRICT has been restored as the default for Flag, and the underlying issues fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

4 participants