We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Stdlib Mastery step 49 of 55
Flag: a permission mask that prints, iterates and refuses bad bits
Replace integer constants and flags & PERM_READ with an enum.Flag.
class Permission(Flag):
EXECUTE = auto() # 1
WRITE = auto() # 2
READ = auto() # 4
RW = READ | WRITE # named composite
ALL = READ | WRITE | EXECUTE
def to_octal(permission: Permission) -> int: ...
def from_octal(bits: int) -> Permission: ... # ValueError outside 0..7
def solve(octals: list[int], probes: list[int]) -> dict[str, object]: ...
solve returns:
| key | value |
|---|---|
"decoded" |
for each octal, sorted(member.name or "" for member in permission), or "ValueError" |
"roundtrip" |
for each octal, to_octal(from_octal(bits)), or "ValueError" |
"membership" |
for each probe, Permission.READ in from_octal(bits); False when the probe is out of range |
"canonical" |
[m.name for m in Permission] |
| "rw_is_composite" | (Permission.READ | Permission.WRITE) is Permission.RW |
| "all_octal" | to_octal(Permission.ALL) |
What the integer version costs you. print(flags) says 6; a debugger
says 6; a log line says 6. Nothing rejects from_octal(9999). There is no
way to list the permissions or ask which bits are set. And flags & PERM_READ
evaluates to 4 rather than to a boolean, so if flags & PERM_READ and
if flags & PERM_READ == PERM_READ differ in ways people get wrong.
Three behaviours the tests pin down.
auto() in a Flag yields successive powers of two — the one place it
means something different from a plain Enum.
Membership is in, not &: Permission.READ in flags is a boolean and reads
as English. Iterating a value yields its constituent single-bit members, so
sorted(m.name for m in Permission(5)) is ["EXECUTE", "READ"] — “explain
this mask” in one line.
Iterating the class yields only canonical members: RW and ALL are
composites of existing flags, so the enum machinery treats them as aliases and
skips them. Permission(0) iterates to nothing at all.
Validate explicitly. Flag defaults to boundary=STRICT, so
Permission(8) raises anyway — but the message comes from the enum machinery.
An explicit range check produces an error naming your domain, and it does
not depend on a default that differs by base class (IntFlag defaults to
KEEP, which preserves unknown bits so values round-tripping through a C API
survive).
Why not IntFlag. Its members are ints, which is convenient at a
database or C boundary, and arithmetic on them degrades to plain int —
Perm.R | Perm.W is a Perm, but Perm.R + 1 is an int and the domain is
gone. Use Flag and convert with two named functions at the boundary.
Worth knowing but not tested: @verify(UNIQUE) and @verify(CONTINUOUS)
(3.11) catch accidental aliases and gaps at class-creation time. UNIQUE is
in tension with named composites, which the machinery regards as aliases.
Stuck?
Python reference solution
Sign in to attempt this problem and reveal the reference solution.