Картинка предо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 всё тоже работало из коробки. Я не смог и сдался.
Всем спасибо!
Tihon_V
До появления аннотаций — использовал rST-docstrings и абстрактные классы. В PyCharm (в т. ч. и CE) — они индексируются аналогично аннотациям. Вроде бы видел такое же поведение в Atom. Работает с модулями.
PyVolshebnyi Автор
Вот в чем проблема с boto3.
Тут метод клиента и класс называются абсолютно одинаково и такое в каждом первом клиенте. mypy справедливо считает что возвращаемый тип — это метод, а не класс. Я нашел решение с само-импортом модуля
Так все работает, но ранит чувства.
Tihon_V
Методы/переменные должны именоваться с алфавитного символа нижнего регистра. К сожалению Amazon не слишко следует pep-8.
У mypy — есть задокументированое решение для подобных ситуаций.
PyVolshebnyi Автор
Спасибо большое. То есть, для примера выше должно быть что-то вроде
Tihon_V
Cykooz
Было бы неплохо сделать аналогичный пакет для botocore. Наверное это должно быть не очень сложно, раз изначально всё генерируется из его данных.
PyVolshebnyi Автор
Нет, для botocore стабы можно написать только руками. Это гораздо проще, но затратно по времени. Плюс, при обновлении botocore нужно будет проверять, не устарели ли стабы, в то время как для boto3 засчет генерации, обновление может быть полностью автоматизированно.
Но да, в 95% случаев, в новом релизе botocore меняются только JSON-файлы с описанием сервисов, так что стабы останутся без изменений.
sobolevn
Круто! Добавляйтесь в github.com/typeddjango/awesome-python-typing
PyVolshebnyi Автор
Спасибо, отправил вам PR https://github.com/typeddjango/awesome-python-typing/pull/34