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

Сase Study: Type Hints

Сase Study: Type Hints

Михаил Юматов (ЦИАН) @ Moscow Python Conf 2017
"У нас большой проект, много кода и разработчиков. Целый пласт возникающих багов связан с отсутствием проверки типов. Попробовали mypy и type hints, получили профит (больше документации, меньше багов), теперь покрываем type hint'ами все новые микросервисы в компании. Расскажу на примерах, что можно получить, а чего ждать не стоит".

Moscow Python Meetup
PRO

October 20, 2017
Tweet

More Decks by Moscow Python Meetup

Other Decks in Programming

Transcript

  1. М И Х А И Л Ю М А Т О В
    T Y P E H I N T S

    View Slide

  2. • Руководитель разработки в ЦИАН
    • В ЦИАНе с 2015
    • 10 лет разработки на Python
    О С Е Б Е

    View Slide

  3. • Ошибки в коде
    • AttributeError: NoneType object has no attribute …
    • TypeError: must be str, not int
    • Документация
    • Лишние тесты
    • Метаклассы и автокомплит
    П Р О Б Л Е М Ы

    View Slide

  4. def greeting(name: str) -> str:
    return 'Hello ' + name
    class Foo:
    bar: int
    def __init__(self, bar: int) -> None:
    self.bar = bar
    P E P - 4 8 4 : T Y P E H I N T S

    View Slide

  5. def greeting(name: str) -> str:
    return 'Hello ' + name
    print(greeting(777))
    print(greeting(None))
    $ mypy test.py --strict
    error: Argument 1 to "greeting" has incompatible type "int"; expected "str”
    error: Argument 1 to "greeting" has incompatible type "None"; expected "str"
    M Y P Y

    View Slide

  6. Н О В Ы Е П Р О Б Л Е М Ы

    View Slide

  7. • https://github.com/python/typeshed
    • stub files (*.pyi)
    class Enum(BaseEnum):
    @classmethod
    def has_lcname(cls, name: bytes) -> bool: ...
    @classmethod
    def get_by_lcname(cls, ccname: bytes) -> int: ...
    lcname = ... # type: bytes
    Н Е Т Т И П О В В Б И Б Л И О Т Е К А Х

    View Slide

  8. setup.py:
    setup(
    name='cian-enum',

    data_files=[
    ('stubs/cian_enum', ['cian_enum/__init__.pyi']),
    ('stubs/cian_enum', ['cian_enum/base.pyi']),
    ],
    )
    bin/mypy:
    prefix=$(python -c 'import sys; print(sys.prefix)')
    MYPYPATH=$prefix/stubs mypy "[email protected]"
    Н Е Т Т И П О В В Б И Б Л И О Т Е К А Х

    View Slide

  9. class Offer(Entity):
    id = attrs.Integer()
    geo = attrs.ValueObject(Geo)
    offer = Offer(...)
    offer.geo.region
    $ mypy test.py --strict
    error: ”ValueObject" has no attribute ”region"
    М Е Т А К Л А С С Ы

    View Slide

  10. class Offer(Entity):
    id = attrs.Integer()
    geo = attrs.ValueObject(Geo)
    offer = Offer(...)
    offer.geo.region
    entity/base.pyi
    from typing import Type, TypeVar
    T = TypeVar('T')
    def ValueObject(klass: Type[T], **kwargs) -> T: ...
    М Е Т А К Л А С С Ы : П О П Ы Т К А 1 , S T U B S

    View Slide

  11. • Это хак
    • Он работает в mypy
    • Но не работает в PyCharm
    М Е Т А К Л А С С Ы : П О П Ы Т К А 1 , S T U B S

    View Slide

  12. Было (Python):
    class AgentPhone(Entity):
    id = attrs.Integer(min_value=0, help='ID телефона')
    country_code = attrs.String(help='Код страны')
    code = attrs.String(help='Код оператора')
    number = attrs.String(help='Номер')
    confirmed = attrs.Boolean(help='Подтвержден')
    visible = attrs.Boolean(help='Опубликован')
    М Е Т А К Л А С С Ы : П О П Ы Т К А 2 , C O D E G E N

    View Slide

  13. Стало (JSON Schema):
    AgentPhone:
    type: object
    description: Телефон агента
    required: [id]
    properties:
    id: {type: integer, minimum: 0, description: "ID телефона"}
    countryCode: {type: string, description: "Код страны"}
    code: {type: string, description: "Код оператора"}
    number: {type: string, description: "Номер"}
    confirmed: {type: boolean, description: "Подтвержден"}
    visible: {type: boolean, description: "Опубликован"}
    М Е Т А К Л А С С Ы : П О П Ы Т К А 2 , C O D E G E N

    View Slide

  14. Стало (JSON Schema):
    class AgentPhone:
    id: int
    country_code: Optional[str]
    code: Optional[str]
    number: Optional[str]
    confirmed: Optional[bool]
    visible: Optional[bool]
    _meta = ...
    def __init__(...): ...
    М Е Т А К Л А С С Ы : П О П Ы Т К А 2 , C O D E G E N

    View Slide

  15. • Все легально
    • Работает в mypy
    • Работает в PyCharm
    • Работает автокомплит
    М Е Т А К Л А С С Ы : П О П Ы Т К А 2 , C O D E G E N

    View Slide

  16. В О П Р О С Ы ?

    View Slide