Switch BadPixels{,FF} base class to enum.IntFlag
Description
Casually scrolling through Python docs, I saw enum.IntFlag
(available since Python 3.6) which could make all our flag-related code nicer.
The two advantages are:
-
Int
: the resulting enum constants we get are also subclasses of int. This means that if we for instance want to put them into numpy arrays, we can assign them directly, dropping.value
. -
Flag
: enables bitwise operations between enum constants resulting in enum constants. Again, this means we can drop.value
(which would throw away the "semantic" value of the enum class).
I've gone over cal_tools
and dropped all .value
suffixes.
Given that work is ongoing with notebooks, I'd suggest they can be updated later as we refactor anyway.
Side note: what to call a merge request which is just refactoring / code style stuff? This is hardly a feature or a fix.
How Has This Been Tested?
Existing code using .value
is not impacted.
Let's compare the two base classes:
from enum import Enum, IntFlag
import numpy as np
class OldBadPixels(Enum):
OFFSET_OUT_OF_THRESHOLD = 0b000000000000000000001 # bit 1
NOISE_OUT_OF_THRESHOLD = 0b000000000000000000010 # bit 2
...
class NewBadPixels(IntFlag):
OFFSET_OUT_OF_THRESHOLD = 0b000000000000000000001 # bit 1
NOISE_OUT_OF_THRESHOLD = 0b000000000000000000010 # bit 2
...
We can use the IntFlag
-based ones directly as numbers:
a1 = np.zeros(1, dtype=np.uint32)
a1[0] |= NewBadPixels.NON_LIN_RESPONSE_REGION
a1[0] |= NewBadPixels.CI_LINEAR_DEVIATION
Or use .value
(so existing code doing this is not affected):
a2 = np.zeros(1, dtype=np.uint32)
a2[0] |= NewBadPixels.NON_LIN_RESPONSE_REGION.value
a2[0] |= NewBadPixels.CI_LINEAR_DEVIATION.value
So far, a1 == a2
.
For completeness, note that the old Enum
-based flags are not numbers:
b1 = np.zeros(1, dtype=np.uint32)
b1[0] |= OldBadPixels.NON_LIN_RESPONSE_REGION
b1[0] |= OldBadPixels.CI_LINEAR_DEVIATION
Traceback (most recent call last):
File "test-440.py", line 68, in <module>
b1[0] |= OldBadPixels.NON_LIN_RESPONSE_REGION
TypeError: unsupported operand type(s) for |: 'int' and 'OldBadPixels'
And as expected, the .value
has not changed at all:
b2 = np.zeros(1, dtype=np.uint32)
b2[0] |= OldBadPixels.NON_LIN_RESPONSE_REGION.value
b2[0] |= OldBadPixels.CI_LINEAR_DEVIATION.value
So a1 == a2 == b2
.
As advertised, we can do bitwise operations directly on members and get BadPixels
bad instead of untypely numbers:
> print(NewBadPixels.DATA_STD_IS_ZERO | NewBadPixels.INTERPOLATED)
NewBadPixels.INTERPOLATED|DATA_STD_IS_ZERO
Bonus: we can recover a BadPixels
instance:
> print(NewBadPixels(int(a1[0])))
NewBadPixels.NON_LIN_RESPONSE_REGION|CI_LINEAR_DEVIATION
And we can still use is
between the enumerable things:
> print(NewBadPixels(int(a1[0])) is NewBadPixels.NON_LIN_RESPONSE_REGION | NewBadPixels.CI_LINEAR_DEVIATION)
True
Types of changes
Code improvement (non-breaking).
Checklist:
- My code follows the code style of this project.