Repository navigation
enum.Flag should be more set-like #82431
Copy link
Copy link
Closed
Labels
3.10 (EOL)end of lifeend of lifestdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancementA feature request or enhancement
Description
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
assignee = 'https://github.com/ethanfurman'
closed_at = <Date 2021-01-26.22:15:42.201>
created_at = <Date 2019-09-22.11:14:02.624>
labels = ['type-feature', 'library', '3.10']
title = 'enum.Flag should be more set-like'
updated_at = <Date 2021-05-26.19:32:15.310>
user = 'https://github.com/belm0'bugs.python.org fields:
activity = <Date 2021-05-26.19:32:15.310>
actor = 'hroncok'
assignee = 'ethan.furman'
closed = True
closed_date = <Date 2021-01-26.22:15:42.201>
closer = 'vstinner'
components = ['Library (Lib)']
creation = <Date 2019-09-22.11:14:02.624>
creator = 'John Belmonte'
dependencies = []
files = []
hgrepos = []
issue_num = 38250
keywords = ['patch']
message_count = 19.0
messages = ['352967', '365051', '365072', '378436', '378439', '378455', '378456', '378461', '378552', '378553', '378555', '378606', '385062', '385672', '385677', '385701', '385735', '385737', '394458']
nosy_count = 7.0
nosy_names = ['jbelmonte', 'ethan.furman', 'veky', 'hroncok', 'Manjusaka', 'John Belmonte', 'hauntsaninja']
pr_nums = ['22221', '22734', '24215', '24342', '25820']
priority = 'normal'
resolution = 'fixed'
stage = 'resolved'
status = 'closed'
superseder = None
type = 'enhancement'
url = 'https://bugs.python.org/issue38250'
versions = ['Python 3.10']Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
3.10 (EOL)end of lifeend of lifestdlibStandard Library Python modules in the Lib/ directoryStandard Library Python modules in the Lib/ directorytype-featureA feature request or enhancementA feature request or enhancement
Activity
belm0 commentedon Sep 22, 2019
I would like Flag class instances to have more set-like abilities:
I may be told "implement it yourself as instance methods", or that #3 has an idiom (a & b is b). Ideally though, every Flag user should be able to rely on these being implemented consistently.
When trying to implement #1 without devolving into bit fiddling, naturally one might try to use the class iterator. Unfortunately the semantics of that enumeration include 0, aliases, and compound values. I've used Flag in several situations and projects, and so far there hasn't been a case where that was the desired semantics. Interesting though, if #1 were implemented in the standard library, then we could enumerate all bits of the Flag via iteration of
~MyFlag(0)... though that's obscuring things behind another idiom.Thank you for considering.
vedgar commentedon Mar 26, 2020
for flag in flags.set:
(though set might imply unorderedness:)
(Of course, if you do make it O(1), I have no objection.)
Zheaoli commentedon Mar 26, 2020
not sure I gett the Point
not sure
absolutely yes
belm0 commentedon Oct 11, 2020
Part of this issue (#1) was intended to be addressed by #22221 which added an
__iter__implementation to Flag and IntFlag. (The PR did not reference this issue, and was already merged last month.)However that PR seems problematic on several counts:
__iter__diverges from the existing__contains__. The latter includes 0 and compound valuesI've put post-merge comments on the PR.
I think it would be safer to have an explicitly named
bits()iterator on flag instances, rather than use__iter__().vedgar commentedon Oct 11, 2020
Just a comment, (1) is analogous to str. iter('abc') gives only 'a', 'b' and 'c', while contains accepts '', 'ab', 'bc', and 'abc' too. At least in my mind, it's a pretty strong analogy.
belm0 commentedon Oct 11, 2020
I don't agree. The "zero" bit does not exist, so having contains return True on
Foo(0) in xis misaligned with the iterator. And having contains return True for specific compound values just because they happen to be explicitly defined, while returning False for others, is arbitrary. contains seems to be of very little use, and moreover a trap for the unwary. Assuming we have to live with that until Python 4, it's better to make an explicit iterator likebits()so that the API doesn't contradict itself.vedgar commentedon Oct 11, 2020
Of course, if it returns True only on _some_ bits combinations it doesn't make sense. I thought every element of a Boolean span would be _in_ the Foo.
But about "zero bit", I still think it's perfectly analogous to '' in 'abc'.
belm0 commentedon Oct 11, 2020
I think #22221 should be reverted (considering the design issue, performance issue, and bugs), and lets have a proper design and review.
While just reading the code, I found an existing bug in Flag. And the new __iter__ uses the buggy internal function, and so itself has bugs.
#22221 (comment)
belm0 commentedon Oct 13, 2020
It's completely undocumented, but today I noticed that Flag.__contains__() is actually a subset operation.
It's an unfortunate departure from the
settype, which usesinfor membership test and issubset() /<=for subset test.For set operations, the Flag individual bits should be considered the members of a set (not Flag compound values, which are themselves equivalent to a set).
vedgar commentedon Oct 13, 2020
Again, I disagree.
strused to work like this in Py2.0 (or somewhere around then), only 'x' was in 'xyz', not 'xy'. Then Guido came to his senses. :-)This is not set theory, this is mereology. You don't differentiate between a digit and a one-digit number, a char and a one-char string, and in the same way you shouldn't differentiate between a bit and a one-bit flag.
belm0 commentedon Oct 13, 2020
I agree that a bit and one-bit flag are the same.
I don't understand the comparison, because str 'a in b' tests if 'a' is a subsequence of 'b'. It is not a subset operation ('xz' in 'xyz' is false).
I can understand the argument that Flag has a subset operator (currently __contains__), and given that one-bit flags can be used freely with the subset operator, there is no reason to add a bit membership operator.
However, since flag values are arguably a set of enabled bits, I think the use of
infor subset is a confusing departure from thesetAPI.vedgar commentedon Oct 14, 2020
Flag values are _not_ a set of enabled bits.
At least, it is a weird kind of set where a is the same as {a}. That's why I mentioned mereology... there is no reasonable "membership", just "inclusion". I understand that str is not a perfect analogy, but sets are even more wrong.
ethanfurman commentedon Jan 14, 2021
The code sample:
Here's the summary of the changes:
+ only canonical flags are returned during iteration
>>> list(Color.WHITE) [<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]negating a flag or flag set returns a new flag/flag set with the
corresponding positive integer value
names of pseudo-flags are constructed from their members' names>>> (Color.RED | Color.GREEN).name 'RED|GREEN'>>> Color.RED | Color.BLUE <Color.PURPLE: 5> >>> Color(7) # or Color(-1) <Color.WHITE: 7>are never considered to be contained:
>>> Color.BLACK in Color.WHITE Falseotherwise, if all bits of one flag are in the other flag, True is returned:
>>> Color.PURPLE in Color.WHITE TrueThere is a new boundary mechanism that controls how out-of-range / invalid bits are handled:
STRICT,CONFORM,EJECT', andKEEP':STRICT --> raises an exception when presented with invalid values
CONFORM --> discards any invalid bits
EJECT --> lose Flag status and become a normal int with the given value
KEEP --> keep the extra bits
- keeps Flag status and extra bits
- they don't show up in iteration
- they do show up in repr() and str()
The default for Flag is STRICT, the default for IntFlag is DISCARD, and the default for _convert_ is KEEP (see ssl.Options for an example of when KEEP is needed).
ethanfurman commentedon Jan 25, 2021
New changeset 7aaeb2a by Ethan Furman in branch 'master':
bpo-38250: [Enum] single-bit flags are canonical (GH-24215)
7aaeb2a
ethanfurman commentedon Jan 25, 2021
Thank you to everyone involved. :-)
To answer the first three points that started this issue:
iteration -> each single-bit flag in the entire flag, or a
combinations of flags, is returned one at a time -- not the
empty set, not other multi-bit values
length is implemented ->
len(Color.BLUE | Color.RED) == 2subset is implemented as containment checking:
Color.BLUE in (Color.RED | Color.BLUE) is Truevstinner commentedon Jan 26, 2021
test_enum fails when Python is installed:
PPC64LE Fedora Rawhide Clang Installed 3.x:
https://buildbot.python.org/all/#builders/312/builds/597
Tests are loaded by Lib/test/test_enum.py with:
ethanfurman commentedon Jan 26, 2021
New changeset 01faf45 by Ethan Furman in branch 'master':
bpo-38250: [Enum] only include .rst test if file available (GH-24342)
01faf45
vstinner commentedon Jan 26, 2021
PPC64LE Fedora Rawhide Clang Installed 3.x is back to green, thanks for the fix ;-)
https://buildbot.python.org/all/#/builders/312/builds/600
hroncok commentedon May 26, 2021
I've reported https://bugs.python.org/issue44242 because I believe there is a regression in this change.