Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Python Static Typing with MyPy

Python Static Typing with MyPy

First presented at Python-Madrid meetup

Ticketea Engineering

March 22, 2018
Tweet

More Decks by Ticketea Engineering

Other Decks in Programming

Transcript

  1. Python
    static typing
    with MyPy
    @Kartones
    Diego Muñoz

    View full-size slide

  2. 01.
    Intro
    02.
    Type Hints
    03.
    MyPy
    04.
    Advanced Topics
    Agenda
    Agenda

    View full-size slide

  3. Weak Typing
    vs
    Strong Typing
    Static Typing
    vs
    Dynamic Typing
    Intro

    View full-size slide

  4. Strong typing helps scale up codebases
    Intro

    View full-size slide

  5. Dynamic typing speeds up development cycles
    Intro

    View full-size slide

  6. But requires type-checking logic, decorators, validators...
    Intro

    View full-size slide

  7. Type hinting + type checking bring that “safety net” back
    Intro

    View full-size slide

  8. Python 3.5
    - Function/Method annotations
    - Method argument annotations
    Type Hints
    Python 3.6
    - Variable annotations
    Python 2.7
    - All of them *
    def my_method(an_integer: int = 0) -> int:
    the_result: int = an_integer + 1
    return the_result

    View full-size slide

  9. 3 Modes
    - Comment annotations
    - Type hints
    - Stub files
    Type Hints

    View full-size slide

  10. Comment Annotations
    - For Python 2
    Type Hints
    from typing import Union
    def my_method(an_integer, a_float):
    # type: (int, float) -> Union[None, float]
    if a_float == 0:
    return None
    the_result = an_integer / a_float # type: float
    return the_result

    View full-size slide

  11. Type Hints
    - For Python 3
    Type Hints
    from typing import Union
    def my_method(an_integer: int, a_float: float) -> Union[None, float]:
    if a_float == 0:
    return None
    the_result: float = an_integer / a_float
    return the_result

    View full-size slide

  12. Stub files
    - .pyi files
    - Code you cannot annotate directly (e.g. external library)
    - stubgen
    Type Hints
    from typing import Any, List
    from django.views.generic import View
    class MyLibView(object):
    http_method_names: List
    def __init__(self, **kwargs: Any) -> None: ...
    def as_view(self, cls: View, **initkwargs: Any) -> View: ...
    def sum_values(self, first: int, second: int) -> int: ...

    View full-size slide

  13. - Primitives
    Type Hints
    from typing import Union
    def my_method(an_integer: int, a_float: float) -> Union[None, float]:
    if a_float == 0:
    return None
    the_result: float = an_integer / a_float
    return the_result

    View full-size slide

  14. - Unknown type
    Type Hints
    from typing import Any
    class MyDjangoView(View):
    def post(self, request: WSGIRequest, **kwargs: Any) -> JsonResponse:
    pass

    View full-size slide

  15. - Lists and Tuples
    Type Hints
    from typing import List, Tuple
    def numeric_tuple_to_list(source_tuple: Tuple) -> List:
    return [item for item in source_tuple]
    def numeric_tuple_to_list(source_tuple: Tuple[int]) -> List[int]:
    return [item for item in source_tuple]

    View full-size slide

  16. - Dictionaries
    Type Hints
    from typing import Dict
    def my_method(input_data: Dict) -> None:
    for key, value in input_data.items():
    print(f"{key}:{value}")
    def my_method_2(input_data: Dict[str, int]) -> None:
    for key, value in input_data.items():
    print(f"{key}:{value}")

    View full-size slide

  17. - Multiple types
    Type Hints
    from typing import Union
    def my_method(an_integer: int, a_float: float) -> Union[None, float]:
    if a_float == 0:
    return None
    the_result: float = an_integer / a_float
    return the_result

    View full-size slide

  18. - “Nullable type” (type or None)
    Type Hints
    from typing import Optional
    def my_method(an_integer: int, a_float: float) -> Optional[float]:
    if a_float == 0:
    return None
    the_result: float = an_integer / a_float
    return the_result

    View full-size slide

  19. Annotations not enforced by Python
    Left for type checkers to use (gradual typing)
    Type Hints

    View full-size slide

  20. Static type checker tool
    MyPy

    View full-size slide

  21. How to run
    - From command line
    MyPy
    mypy myproj
    myproj/models.py:46: error: Function is missing a type annotation for one or
    more arguments
    myproj/models.py:54: error: Unsupported operand types for >= ("int" and "str")
    myproj/tests/models_test.py:253: error: Argument 2 to "my_method" of "MyClass"
    has incompatible type "int"; expected "str"

    View full-size slide

  22. How to run
    - As a “linter” test
    MyPy
    import subprocess
    import sys
    def test_mypy_compliance() -> None:
    mypy_binary = subprocess.check_output("which mypy",
    shell=True,
    stderr=sys.stderr)
    .decode("ascii")
    .replace("\n", "")
    result = subprocess.call("{} apu".format(mypy_binary),
    shell=True,
    stdout=sys.stdout,
    stderr=sys.stderr)
    assert result == 0

    View full-size slide

  23. How to run
    - As an IDE plugin
    MyPy

    View full-size slide

  24. Advanced
    Topics

    View full-size slide

  25. Python 3
    - Inform that a variable is coerced to a certain type
    Advanced Topics
    from typing import cast
    # let’s imagine my_input comes from an external module
    print(5 + cast(int, my_input))

    View full-size slide

  26. Python 3
    - Supports Inheritance
    Advanced Topics
    class Vehicle:
    pass
    class Car(Vehicle):
    pass
    def start_engine(vehicle: Vehicle) -> None:
    print(vehicle.__class__)
    mycar = Car()
    start_engine(mycar)

    View full-size slide

  27. Python 3
    - Types can also be specified
    Advanced Topics
    from typing import Type
    def validate_type(a_vehicle_class: Type) -> bool:
    return a_vehicle_class in [Car, Truck]

    View full-size slide

  28. Python 3
    - Iterators
    Advanced Topics
    from typing import Iterator
    def find_items() -> Iterator[MyClass]:
    # ...
    yield item

    View full-size slide

  29. Python 3
    - Function/Method handlers
    Advanced Topics
    from typing import Callable
    def my_method1(func: Callable, value: int) -> int:
    return func(value)
    def my_method2(func: Callable[[int], int], value: int) -> int:
    return func(value)

    View full-size slide

  30. Python 3
    - Type aliases
    Advanced Topics
    from typing import List
    Vector = List[float]

    View full-size slide

  31. Python 3
    - Forward references are problematic
    - Solution:
    - Python < 3.7: Quotes
    - Python >= 3.7: PEP 563: Postponed Evaluation of Annotations
    Advanced Topics
    class ClassA:
    def a_method(self, target: "ClassB") -> "ClassB":
    target.b_method()
    return target
    class ClassB:
    def b_method(self) -> None:
    pass

    View full-size slide

  32. Python 3
    - Import cycles are problematic
    - Solution: Type checking flag + Quotes
    Advanced Topics
    from typing import TYPE_CHECKING
    if TYPE_CHECKING:
    from b import ClassB
    def some_method(target: "ClassB") -> None:
    target.b_method()
    from a import some_method
    class ClassB:
    def b_method(self) ->None:
    pass
    def a_method(self) -> None:
    some_method(self)

    View full-size slide

  33. Dropbox’s PyAnnotate:
    Automatically annotate your code
    Advanced Topics

    View full-size slide

  34. Needs changes to your tests → use Pytest-annotate
    Advanced Topics

    View full-size slide

  35. Python 2-style comment annotations :(
    Advanced Topics

    View full-size slide

  36. Instagram’s MonkeyType:
    Automatically annotate your code
    Advanced Topics

    View full-size slide

  37. Custom Python profiler → no need to change code
    Advanced Topics

    View full-size slide

  38. Python 3-style type hints :)
    Advanced Topics

    View full-size slide

  39. Type hints:
    https://www.python.org/dev/peps/pep-0483/
    https://www.python.org/dev/peps/pep-0484/
    MyPy:
    http://mypy-lang.org/
    PyAnnotate:
    https://github.com/dropbox/pyannotate
    https://github.com/kensho-technologies/pytest-annotate
    MonkeyType:
    https://github.com/Instagram/MonkeyType
    Advanced Topics

    View full-size slide

  40. THE END
    Thanks!
    @Kartones
    Slides: https://speakerdeck.com/ticketeaeng

    View full-size slide