image


Картинка предоcтавлена автором boto3-type-annotations, Allie Fitter


Сейчас мало кто пишет большие проекты на Python без аннотации типов. Это и просто, и позволяет отловить кучу ошибок еще на этапе написания кода, да и работает очень шустро. Но стоит добавить в зависимости boto3, и mypy начинает пестрить сообщениями о том, что аннотаций типов для boto3 не существует в природе.


Не страшно, существует же официальный генератор аннотаций для boto3 botostubs. Только он официально не выпускался, не обновляется и с mypy не работает.


Есть и замечательный boto3-type-annotations, но тоже немного заброшен, и поддержки mypy всё еще нет.


Как же проверять типы для boto3, а бонусом получить автодополнение кода?


Проверка типов в mypy


Предположим, мы пользуемся самой свежей на данный момент версией boto3 1.11.9 для сервисов EC2, DynamoDB и S3. Да и Python у нас 3.6.9 или даже новее. Чтобы получить и проверку типов, и автодополнение, нам нужно установить совместимую версию boto3-stubs 1.11.9.x с указанием сервисов.


# поддерживаются все boto3 сервисы, так что замените на свои
pip install boto3-stubs[s3,ec2,dynamodb]==1.11.9.0

# если вы не используете pip, pipfile или poetry,
# или же устанавливаете whl-пакеты, то нужно еще и сгенерировать
# индекс установленных сервисов
python -m mypy_boto3

И… всё! Уже можно запускать mypy и увидеть, что он, наконец, нашел стабы для boto3, а возможно даже нашел ошибки в проекте.


Там же было еще что-то про автодополнение!


И это тоже есть, но уже чуть сложнее.


В VSCode не поддерживаются полностью аннотации для перегруженных функций, так что типы в некоторых случаях придется указывать явно. А именно для boto3.client, boto3_session.client,boto3.resource, boto3_session.resource, client.get_waiter и client.get_paginator.


В PyCharm авто-определение типов для перегруженных функций может медленно работать и поедать оперативку, если вы установили больше 15 сервисов, так что явные типы пригодятся и там.


import boto3

from mypy_boto3 import dynamodb

# Явное указание типа, чтобы автодополнение работало везде
client: dynamodb.DynamoDBClient = boto3.client("dynamodb")

# IDE подскажет имена и типы агрументов и возвращаемого значения
client.query("my_table")

Пример использования


import boto3

from mypy_boto3 import s3

# PyCharm и mypy работают и без явного указания типа, но лучше указывать
client: s3.S3Client = boto3.client("s3")

# IDE подсказывает и имена, и типы, и всё-всё-всё
client.create_bucket(Bucket="bucket")

# посмотрим, сможет ли mypy отловить ошибки в этом коде

# (mypy) error: Missing positional argument "Key" in call to "get_object" of "S3Client"
client.get_object(Bucket="bucket")

# (mypy) error: Argument "Key" to "get_object" of "S3Client" has incompatible type "None"; expected "str"
client.get_object(Bucket="bucket", Key=None)

resource: s3.S3ServiceResource = boto3.Session(region_name="us-west-1").resource("s3")

# Для ресурсов авто-дополенение и проверка типов тоже работает
bucket = resource.Bucket("bucket")

# (mypy) error: Unexpected keyword argument "key" for "upload_file" of "Bucket"; did you mean "Key"?
bucket.upload_file(Filename="my.txt", key="my-txt")

# waiter'ы  и paginator'ы тоже аннотированы
# и не забываем про явные типы на всякий случай
waiter: s3.BucketExistsWaiter = client.get_waiter("bucket_exists")
paginator: s3.ListMultipartUploadsPaginator = client.get_paginator(
    "list_multipart_uploads"
)

Как оно работает


Аннотации создаются автоматически небольшой библиотекой mypy-boto3-builder и выходят спустя час-два после релиза новой версии boto3. Если вы используете старую версию boto3, то можете сгенерировать пакеты руками по инструкции.


Гененриуется это всё из json-файлов с описанием сервисов из botocore с небольшими патчами, так как boto3 изменяет методы некоторых сервисов.


Помогите советами


Напоследок, несколько вопросов для вас, чтобы размять мозги и при этом помочь проекту.


Для генерации индекса аннотаций для сервисов используется post-install скрипт, который сам по себе грязный хак и не поддерживается в whl-пакетах. Есть ли способ избежать этого?


Так как в boto3 многие методы и классы в одном файле называются одинаково, для корректной работы аннотаций модуль импортирует сам себя. Это не работает в Python 3.6.5 и выглядит не очень. Какие есть альтернативы?


Ну и, наконец, если у вас все в порядке с C#, можно добавить поддержку литералов в перегруженные функции для Python Language Server, чтобы в VSCode всё тоже работало из коробки. Я не смог и сдался.


Всем спасибо!