We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← The Type System as a Design Tool step 5 of 24
Literal fit modes and the Final default
df.merge(how="innner") becomes a red squiggle in your editor instead of a
runtime ValueError in production — if you are lucky, and if somebody typed the
parameter as a Literal.
Literal puts exact values into the type system. Literal["contain"] is a
type inhabited by exactly one string. A union of them is a closed set of valid
arguments that the checker can verify at every call site, and that
assert_never can prove you handled exhaustively.
Legal members: int, str, bytes, bool, enum members, and None.
Illegal: floats (Literal[3.14] is a valid-type error), arbitrary
expressions, and mutable containers. The float exclusion is deliberate —
floating-point equality is not a sound basis for type identity.
The rule that surprises everyone: assignment does not preserve literalness.
mode = "contain" # inferred: str, NOT Literal['contain']
resize(img, tgt, mode) # error: str is not Literal['contain', 'cover', 'stretch']
The escape hatch is Final:
DEFAULT_FIT: Final = "contain" # revealed type: Literal['contain']?
DEFAULT_FIT2: Final[str] = "contain" # revealed type: str -- the annotation wins
An explicit type on a Final kills literal inference. TIMEOUT: Final = 30
reveals Literal[30]?; TIMEOUT: Final[int] = 30 reveals int. This problem
depends on that: DEFAULT_FIT is declared bare so it can serve as the default
value of a Literal-typed parameter.
The task
type Fit = Literal["contain", "cover", "stretch"]
def resize(
image: tuple[int, int],
target: tuple[int, int],
fit: Fit = DEFAULT_FIT,
) -> tuple[int, int]:
image and target are (width, height). Return the new (width, height):
-
contain— scale bymin(target_w / w, target_h / h), so the whole image fits inside the target box, preserving aspect ratio. -
cover— scale bymax(...), so the image covers the target box, preserving aspect ratio, overflowing on one axis. -
stretch— returntargetunchanged, aspect ratio be damned.
Round each scaled dimension with round(), then clamp it to a minimum of 1:
a 1000x1 banner scaled into a 10x10 box would otherwise come back zero pixels
tall, and a zero-dimension image is a crash somewhere further down the pipeline.
Close the match with assert_never(fit).
Return a tuple[int, int]. A list will fail — in this mode the container type
is part of the contract.
What the tests check
Each mode’s arithmetic; that omitting fit entirely uses DEFAULT_FIT (the
Final literal-inference rule, exercised for real); upscaling as well as
downscaling; the identity case; and the degenerate aspect ratio that hits the
clamp.
Types
Literal is what makes assert_never possible here: the checker knows the
parameter has exactly three inhabitants, so a match covering all three leaves
Never in the default arm.
One divergence to be aware of, because it will bite you when you move between
checkers: mypy does not narrow x in ("contain", "cover") to a Literal.
pyright does. If you need to validate an incoming str down to a Fit, write
an explicit loop or a chain of == comparisons; the membership test that looks
obviously equivalent is not, under mypy.
Loading visualization…
Stuck?
Python reference solution
Sign in to attempt this problem and reveal the reference solution.