Marcel Blijleven

Published on

Add type annotations to kwargs

You’ve probably been using type annotations in Python for quite some time now, but did you know you can also add them to **kwargs?

Technically it’s been possible since Python 3.5, but it wasn’t very useful. You could simply add a regular type annotation to **kwargs, like so:

def naive_approach(**kwargs: int) -> None:
    foo = kwargs.get("foo")  # foo is of type int | None

But this would mean that any value inside the kwargs dict is an int.

TypedDict and Unpack

This is where TypedDict and Unpack can be helpful. These were introduced in Python 3.8 and Python 3.11 respectively. Unpack was modified in 3.12 to support PEP 692.

Both TypedDict and Unpack have also been made available in earlier versions through typing_extensions, but it’s recommended to use at least version 3.11 because of issues in Python 3.10 and earlier versions.

A TypedDict lets you add type hints to dicts with a fixed set of keys. These keys can also be marked as ’not required'.

Unpack conceptually marks a value as ‘unpacked’, combining this with a TypedDict lets you add meaningful type hinting to **kwargs.

from typing import NotRequired, TypedDict, Unpack


class Kwargs(TypedDict):
    bootstrap_servers: NotRequired[str]
    request_timeout_ms: NotRequired[int]


def create_consumer(client_id: str, **kwargs: Unpack[Kwargs]) -> None:
    ...

This will give you code completion hints both inside the function body and when calling the function from anywhere in your code.

A screenshot of a code editor that displays the behaviour of unpacking a TypedDict

This currently works in both (neo)vim and VSCode, it doesn’t work in PyCharm as of this writing. There’s an open ticket here.

(P.S. don’t mind the different color scheme in my editor :))

Tags: