Skip to content
← Back to explainers

Python explainer

What mypy Checks and How You Are Graded

About 3 min to read. Allow extra time to work through examples.

The site accepts a Python solution only when its tests pass and mypy in strict mode reports no errors. See what mypy checks, and how to read its errors.

This article uses Python. You can make it your practice mode to browse related content across the site.

Every Python exercise on this site checks your solution twice. First it runs the tests. Then it runs a type checker called mypy. The site accepts your solution only when both pass.

Here is a function that passes its test and still fails:

def greet(name):
    return "Hello, " + name

greet("Ada") returns "Hello, Ada", so a test for it passes. mypy, in strict mode, still reports an error:

L1:1 [no-untyped-def]
Function is missing a type annotation

Add the annotations, and both checks pass:

def greet(name: str) -> str:
    return "Hello, " + name

What mypy does

A type checker reads your code without running it, and checks that your code uses each value the way its type allows. If a function says it takes a str, mypy reports any call that passes an int. It finds this kind of bug before the code runs, including in branches your tests never reach.

mypy is one of several type checkers for Python, and the one this site uses. Strict mode, mypy --strict, turns on extra checks. The first one you will meet requires every function to declare a type for each parameter and for its return value.

Run and Submit do different things

  • Run runs the visible test cases and shows which ones pass. It does not run mypy.
  • Submit runs every test case, including hidden ones, and then runs mypy --strict on your file. The result says "Accepted" only when the tests pass and mypy reports no errors.

If the tests pass and mypy does not, the result says "Not accepted. The tests passed but mypy --strict did not." Fix the errors listed under it, and submit again.

Your code runs on your own computer, not on the site's servers. The crackedai-connect runtime, which you install once, runs the tests and mypy and sends the results back to the page.

Read a mypy error

Each mypy error shows three things:

  • its line and column, such as L2:12 for line 2, column 12;
  • an error code in square brackets, such as [union-attr];
  • a sentence that says what is wrong.
def shout(text: str | None) -> str:
    return text.upper()
L2:12 [union-attr]
Item "None" of "str | None" has no attribute "upper"

str | None means the value is either a string or None. mypy is saying that text might be None, and None has no .upper() method. Check for None first:

def shout(text: str | None) -> str:
    if text is None:
        return ""
    return text.upper()

After the if, mypy knows that text is a str. A check like this, which rules out a type, is called narrowing.

The error code is the quickest way to look an error up. mypy's list of error codes explains each one with an example.

Check your understanding Your solution passes every test, but the result says "Not accepted". What do you look at first? Show answer

Look at the mypy errors listed under the result. The tests passed, so the logic is probably right. In strict mode, the usual cause is a missing annotation, or code that uses a value that might be None without checking it first. Fix each error, then submit again.

Going further

Submit runs mypy --strict --warn-unreachable --extra-checks --ignore-missing-imports. It checks your code against the oldest Python version the exercise supports. A later article in this track lists what --strict turns on. The next exercise has you fix the mypy errors in code whose visible tests already pass.

step 1 of 13

Getting Started with Tests and Type Checks

Up next Fix Your First mypy Errors