Making the most out of Mypy

def is_longer(s: str, i: int) -> bool:
    return len(s) > i
is_longer(None, 2)
is_longer("hello", None)
strict_optional=True
Argument 1 to "is_longer" has incompatible type "None"; expected "str"
Argument 2 to "is_longer" has incompatible type "None"; expected "int"
is_longer(2, "foo")
Argument 1 to "is_longer" has incompatible type "int"; expected "str"
Argument 2 to "is_longer" has incompatible type "str"; expected "int"
from typing import Optional

def is_longer(
  s: Optional[str], 
  i: Optional[int]
) -> bool:
    if s is None or i is None:
        return False
    else:
        return len(s) > i

"Make illegal states unrepresentable"

from typing import Optional

class Person:
    name: str
    dog_name: Optional[str]
    dog_age: Optional[int]

Person("Phil", None, None)
Person("Bob", "Fido", 3)
Person("Bob", "Fido", None)
Person("Bob", None, 3)
from typing import Optional, Tuple

class Person:
    name: str
    dog: Optional[Tuple[str, int]]
class Dog:
    name: str
    age: int

class Person:
    name: str
    dog: Optional[Dog]
Person("Phil", None)
Person("Bob", Dog("Fido", 3))
Person("Bob", Dog("Fido", None))
Person("Bob", Dog(None, 3))
Person(False, "foo")
class EmailPassword:
    email: str
    password: str

class Phone:
    phone: int

class UserAuth:
    email_password: Optional[EmailPassword]
    phone: Optional[Phone]
UserAuth(
  EmailPassword("foo@gmail.com", "secret123"),
  None
)

UserAuth(None, Phone(1234567890))
UserAuth(None, None)
from typing import Union

UserAuth = Union[Phone, EmailPassword]
def auth_description(auth: UserAuth) -> str:
    if isinstance(auth, Phone):
        return f"Phone number: {auth.phone}"
    else:
        return f"Email: {auth.email}"
class User:
    id: str
    created_at: datetime
    auth: UserAuth

Conclusion

  • Use a stricter Mypy config
  • What invariants exist in your code that are implicit?
  • What implicit relationships exist between properties?

Thank you!

Making the most out of mypy

By Phil Nachum

Making the most out of mypy

  • 394