-
-
Notifications
You must be signed in to change notification settings - Fork 31.7k
enum.Flag should be more set-like #82431
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
Comments
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 Thank you for considering. |
|
|
Part of this issue (#1) was intended to be addressed by #22221 which added an However that PR seems problematic on several counts:
I've put post-merge comments on the PR. I think it would be safer to have an explicitly named |
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. |
I don't agree. The "zero" bit does not exist, so having contains return True on |
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'. |
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. |
It's completely undocumented, but today I noticed that Flag.__contains__() is actually a subset operation. def __contains__(self, other):
...
return other._value_ & self._value_ == other._value_ It's an unfortunate departure from the 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). |
Again, I disagree. 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. |
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 |
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. |
The code sample: class Color(IntFlag):
BLACK = 0
RED = 1
GREEN = 2
BLUE = 4
PURPLE = RED | BLUE
WHITE = RED | GREEN | BLUE Here's the summary of the changes:
>>> list(Color.WHITE)
[<Color.RED: 1>, <Color.GREEN: 2>, <Color.BLUE: 4>]
>>> (Color.RED | Color.GREEN).name
'RED|GREEN'
>>> Color.RED | Color.BLUE
<Color.PURPLE: 5>
>>> Color(7) # or Color(-1)
<Color.WHITE: 7>
>>> Color.BLACK in Color.WHITE
False otherwise, if all bits of one flag are in the other flag, True is returned: >>> Color.PURPLE in Color.WHITE
True There is a new boundary mechanism that controls how out-of-range / invalid bits are handled: STRICT --> raises an exception when presented with invalid values 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). |
Thank you to everyone involved. :-) To answer the first three points that started this issue:
|
test_enum fails when Python is installed: PPC64LE Fedora Rawhide Clang Installed 3.x: 0:01:37 load avg: 8.99 [232/426/1] test_enum failed -- running: test_tokenize (1 min 37 sec), test_unparse (31.7 sec), test_concurrent_futures (37.5 sec)
Failed to call load_tests:
Traceback (most recent call last):
File "/home/buildbot/buildarea/3.x.cstratak-fedora-rawhide-ppc64le.clang-installed/build/target/lib/python3.10/unittest/loader.py", line 130, in loadTestsFromModule
return load_tests(self, tests, pattern)
File "/home/buildbot/buildarea/3.x.cstratak-fedora-rawhide-ppc64le.clang-installed/build/target/lib/python3.10/test/test_enum.py", line 20, in load_tests
tests.addTests(doctest.DocFileSuite(
File "/home/buildbot/buildarea/3.x.cstratak-fedora-rawhide-ppc64le.clang-installed/build/target/lib/python3.10/doctest.py", line 2511, in DocFileSuite
suite.addTest(DocFileTest(path, **kw))
File "/home/buildbot/buildarea/3.x.cstratak-fedora-rawhide-ppc64le.clang-installed/build/target/lib/python3.10/doctest.py", line 2433, in DocFileTest
doc, path = _load_testfile(path, package, module_relative,
File "/home/buildbot/buildarea/3.x.cstratak-fedora-rawhide-ppc64le.clang-installed/build/target/lib/python3.10/doctest.py", line 231, in _load_testfile
file_contents = loader.get_data(filename)
File "<frozen importlib._bootstrap_external>", line 1023, in get_data
FileNotFoundError: [Errno 2] No such file or directory: '/home/buildbot/buildarea/3.x.cstratak-fedora-rawhide-ppc64le.clang-installed/build/target/lib/python3.10/test/../../Doc/library/enum.rst'
test test_enum crashed -- Traceback (most recent call last):
File "/home/buildbot/buildarea/3.x.cstratak-fedora-rawhide-ppc64le.clang-installed/build/target/lib/python3.10/test/libregrtest/runtest.py", line 272, in _runtest_inner
refleak = _runtest_inner2(ns, test_name)
File "/home/buildbot/buildarea/3.x.cstratak-fedora-rawhide-ppc64le.clang-installed/build/target/lib/python3.10/test/libregrtest/runtest.py", line 236, in _runtest_inner2
test_runner()
File "/home/buildbot/buildarea/3.x.cstratak-fedora-rawhide-ppc64le.clang-installed/build/target/lib/python3.10/test/libregrtest/runtest.py", line 210, in _test_module
raise Exception("errors while loading tests")
Exception: errors while loading tests Tests are loaded by Lib/test/test_enum.py with: def load_tests(loader, tests, ignore):
tests.addTests(doctest.DocTestSuite(enum))
tests.addTests(doctest.DocFileSuite(
'../../Doc/library/enum.rst',
optionflags=doctest.ELLIPSIS|doctest.NORMALIZE_WHITESPACE,
))
return tests |
PPC64LE Fedora Rawhide Clang Installed 3.x is back to green, thanks for the fix ;-) |
I've reported https://bugs.python.org/issue44242 because I believe there is a regression in this change. |
Flag
andIntFlag
members iterable #22221Note: 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:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: