Static Type Checking with mypy
Python does not require type annotations to run a program.
This project still uses annotations because they document what values functions accept and return:
def count_words(text: str) -> int:
...
The annotation says that text should be a string and the result should be an
integer.
Type hints do not check themselves
Python normally does not reject a call just because it conflicts with an annotation. Type hints are metadata that tools can inspect.
A static type checker reads the code without running it and reports operations that are inconsistent with the annotations. This project uses mypy for that job.
Tests and type checking answer different questions:
- pytest runs code and checks observed behavior,
- mypy analyzes possible value types before the code runs.
Both are useful, and neither replaces the other.
Add mypy as a development dependency
mypy is needed while developing the project, not while using text-toolkit as
a library. Add it to the dev dependency group:
uv add --group dev mypy
After cloning this repository, uv sync installs mypy with the other
development dependencies.
Configure mypy
The project keeps its mypy settings in pyproject.toml:
[tool.mypy]
python_version = "3.12"
files = ["src", "tests"]
strict = true
These options mean:
python_versionchecks code using Python 3.12 language rules,filesdefines the directories checked when no paths are passed,strictenables mypy's optional strictness checks.
Strict mode works well here because this is a small project whose functions and tests already have annotations. Adding strict mode to a larger existing project may require a gradual approach.
The exact checks included in strict mode can change between mypy releases. The project therefore runs mypy after dependency updates instead of assuming that a previous result will always stay valid.
Run the type checker
Run:
uv run mypy
The files setting means that no paths are needed in the command. A successful
check reports output similar to:
Success: no issues found in 4 source files
What an error looks like
Consider code that assigns the integer returned by count_words to a variable
declared as a string:
word_count: str = count_words("clean Python project")
mypy reports an incompatible assignment because the annotation and return type do not agree. The program does not need to run for mypy to find that mismatch.
Read the file path, line number, and error code in the message. Fix the type or the code rather than silencing the warning without understanding it.
What mypy does not prove
A successful mypy check does not mean that the program is correct.
mypy does not replace:
- tests for runtime behavior,
- Ruff checks for linting and formatting,
- code review,
- clear program design.
Type checking is one layer of feedback in the project workflow.
Rule of thumb
Use type hints to describe interfaces.
Use mypy to check whether the code follows those descriptions.
Use pytest to check what the code actually does when it runs.