Skip to content

← Everyday Standard Library Tools step 51 of 55

Medium Primitives

Decode a Three-Bit Permission Mask

The starter stores permissions as plain integer constants. Replace them with an enum.Flag, convert between flags and integers, and report what each mask contains.

from_octal(5)  # <Permission.EXECUTE|READ: 5>
to_octal(Permission.RW)  # 6
from_octal(8)  # raises ValueError

What to write

Declare the class with the single bits first, in this order, using auto(), and then the two named combinations:

class Permission(Flag):
    EXECUTE = auto()      # 1
    WRITE = auto()        # 2
    READ = auto()         # 4
    RW = READ | WRITE     # 6
    ALL = READ | WRITE | EXECUTE  # 7
  • to_octal(permission: Permission) -> int returns the number, permission.value.
  • from_octal(bits: int) -> Permission returns the flag for an integer from 0 to 7. Raise ValueError for any other integer. Check the range yourself before you call Permission(bits), because Permission(-1) returns ALL instead of raising.

The octal side of each function is a plain integer, not a string of octal digits. Each valid integer is one three-bit group, like one digit of a Unix file mode.

Then write solve(octals, probes). It returns:

Key Value
decoded For each value in octals, the names of its single bits in alphabetical order, or "ValueError" for an invalid value
roundtrip For each value in octals, the value converted to a flag and back to an integer, or "ValueError"
membership For each value in probes, whether it includes READ, using Permission.READ in permission. An invalid value gives False
canonical The names from looping over the class, which are ["EXECUTE", "WRITE", "READ"]
rw_is_composite Whether combining READ and WRITE gives exactly the member Permission.RW, which must be True
all_octal to_octal(Permission.ALL), which must be 7

For octals=[5, 0, 8] and probes=[5, 2, -1], decoded is [["EXECUTE", "READ"], [], "ValueError"], roundtrip is [5, 0, "ValueError"] and membership is [True, False, False].

On a Flag, mypy types a member's name as str | None. Write member.name or "" when you collect the names to sort.

Edge cases

  • Keep the input order and any duplicates in decoded, roundtrip and membership.
  • Empty input lists give empty lists for those three keys. The other three keys are still in the report.
  • 0 is valid. It has no bits and does not include READ.
  • Inputs are always integers. You do not need to handle strings or other types.