M14) Shipping label traceback

A parcel service charges shipping per kilogram. Parcels within the Netherlands ("NL") cost 120 cents per kilogram, parcels to other EU countries ("EU") cost 250 cents, and all other destinations cost 400 cents. The program in Listing 1, saved as shipping.py, prints the text for a shipping label.

Listing 1: A program that prints a shipping label for a 2 kg parcel to the United Kingdom (Question 1).
def rate_per_kg(zone: str) -> int:
    if zone == "NL":
        return 120
    elif zone == "EU":
        return 250
    elif zone == "World":
        return 400


def shipping_cost(weight: int, zone: str) -> int:
    return weight * rate_per_kg(zone)


def label_text(weight: int, zone: str) -> str:
    cost = shipping_cost(weight, zone)
    return f"{zone}: {cost} cents"


print(label_text(2, "UK"))

Running the program produces the traceback in Listing 2.

Listing 2: The traceback of Listing 1 (Question 1).
Traceback (most recent call last):
  File "shipping.py", line 19, in <module>
    print(label_text(2, "UK"))
          ^^^^^^^^^^^^^^^^^^^
  File "shipping.py", line 15, in label_text
    cost = shipping_cost(weight, zone)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "shipping.py", line 11, in shipping_cost
    return weight * rate_per_kg(zone)
           ~~~~~~~^~~~~~~~~~~~~~~~~~~
TypeError: unsupported operand type(s) for *: 'int' and 'NoneType'

Where is the defect, and why?

  1. In rate_per_kg. Its last branch tests zone == "World", so any other destination reaches the end of the function, which returns None.
  2. On line 11 of shipping_cost. Python cannot multiply a number by a function call, so the rate must be stored in a variable first.
  3. On line 19. "UK" is not a valid zone, so the call must pass "World" or "EU" instead.
  4. In shipping_cost. It must catch the TypeError with try-except and return 0.