IT技術で仕事を減らしたい!

ITエンジニアのメモ+α

Python AWS DynamoDBのシリアライザー・デシリアライザー

どうも、nippa です。

AWS DynamoDB で DynamoDB の専用の型を dict 型を渡す必要があったり、データを受け 取ったあとに処理しないといけません。

boto3 の dynamodb のライブラリの中には、その処理を簡略化する serializer、deserializer が用意されています。

今回はこの 2 つの使い方について記事にします。

環境

シリアライズ(dict -> DynamoDB 形式)

DynamoDB にデータを put_item する場合、通常 DynamoDB の型を記載する必要がありま す。

以下のようになります。

def put_item() -> None:
    table_name = "test"
    response = dynamodb.put_item(
        TableName=table_name,
        Item={
            "id": {"N": "1"},
            "email": {"S": "example@example.com"},
            "name": {"S": "Yamada"},
            "age": {"N": "30"},
        },
    )
    print(response)
    return

DynamdDB の型を Boto3 で準備されているシリアライザーを利用することで、簡略化する ことができます。

リアライザーで書き直したものが以下になり、適切な形に書き換えてくれます。

import boto3
from boto3.dynamodb.types import TypeSerializer


dynamodb = boto3.client("dynamodb", region_name="us-west-2")
serializer = TypeSerializer()

def create_table() -> None:
    table_name = "test"
    dynamodb.create_table(
        TableName=table_name,
        KeySchema=[
            {"AttributeName": "id", "KeyType": "HASH"},  # Partition key
            {"AttributeName": "email", "KeyType": "RANGE"},  # Sort key
        ],
        AttributeDefinitions=[
            {"AttributeName": "id", "AttributeType": "N"},
            {"AttributeName": "email", "AttributeType": "S"},
        ],
        BillingMode="PAY_PER_REQUEST",
    )

    # table作成完了まで待つ
    dynamodb.get_waiter("table_exists").wait(TableName=table_name)

    print(f"Table {table_name} created successfully.")
    return


def put_item() -> None:
    table_name = "test"
    item: dict = {"id": 1, "email": "example@example.com", "name": "Yamada", "age": 30}

    response = dynamodb.put_item(
        TableName=table_name,
        Item={k: serializer.serialize(v) for k, v in item.items()},
    )
    print(response)
    return

if __name__ == "__main__":
    create_table()
    put_item()

シリアライズ(DynamoDB 形式 -> dict)

一般的な DynamoDB からアイテムを取得した場合のレスポンスは以下のようになります。

def get_item() -> None:
    table_name = "test"
    response = dynamodb.get_item(
        TableName=table_name,
        Key={
            "id": {"N": "1"},
            "email": {"S": "example@example.com"},
        },
    )
    item = response.get("Item")
    if item:
        print(item)
    else:
        print("Item not found")
    return

# response
{'id': {'N': '1'}, 'email': {'S': 'example@example.com'}, 'name': {'S': 'Yamada'}, 'age': {'N': '30'}}

この形では、取り扱うための処理が必要になりますが、Boto3 で用意されているデシリア ライザーを利用すると、取り扱いが非常に楽になります。

デシリアライザーを利用した場合は、以下のようになります。

import boto3
from boto3.dynamodb.types import TypeDeserializer

dynamodb = boto3.client("dynamodb", region_name="us-west-2")
deserializer = TypeDeserializer()

def get_item() -> None:
    table_name = "test"
    response = dynamodb.get_item(
        TableName=table_name,
        Key={
            "id": {"N": "1"},
            "email": {"S": "example@example.com"},
        },
    )
    item = response.get("Item")
    if item:
        print({k: deserializer.deserialize(v) for k, v in item.items()})
    else:
        print("Item not found")
    return


if __name__ == "__main__":
    get_item()

# response
{'id': Decimal('1'), 'email': 'example@example.com', 'name': 'Yamada', 'age': Decimal('30')}

感想

今回は DynamoDB のシリアライザー、デシリアライザーの使い方についてまとめました。

データの取り扱いが非常に便利になりますになりますので、是非活用してみてください。

ではでは、また次回。

Python Boto3でAWS DynamoDBの基本操作

どうも、nippa です。

PythonAWS DynamoDB にアクセスするためのコードについてまとめておきます。

Boto3 を利用して、アクセスします。

環境

boto3 について

boto3 は PythonAWS 公式の SDK になります。

boto3 を使った AWS サービスのアクセス方法には、clientresourceの 2 つの方法 がありますが、resourceは今後新機能は追加されないことが発表されています。

現在のresourceの inferface はライフサイクルが来るまでは、利用可能です。 AWS 公式ドキュメント resource

今後のことも踏まえて、今回はclientを利用していきます。

boto3 のインストール

  • poetry インストール
  poetry add boto3
  • pip インストール
  pip install boto3

基本的な使い方

CRUD(create, read, update, delete)に基づいて操作をまとめておきます。今回はパー ティションキー(Partition key)、ソートキー(Sort key)がある場合です。

テーブル設計は特に考えていないので、操作を試すために作成しています。

また、関数としてコードは記載します。

create_table

testテーブルの作成になります。テーブル作成が完了するまで、wait で待っています 。

idパーティションキーに、emailをソートキーに指定しています。

import boto3


dynamodb = boto3.client("dynamodb", region_name="us-west-2")

def create_table() -> None:
    table_name = "test"
    table = dynamodb.create_table(
        TableName=table_name,
        KeySchema=[
            {"AttributeName": "id", "KeyType": "HASH"},  # Partition key
            {"AttributeName": "email", "KeyType": "RANGE"},  # Sort key
        ],
        AttributeDefinitions=[
            {"AttributeName": "id", "AttributeType": "N"},
            {"AttributeName": "email", "AttributeType": "S"},
        ],
        BillingMode="PAY_PER_REQUEST",
    )

    # table作成完了まで待つ
    dynamodb.get_waiter("table_exists").wait(TableName=table_name)

    print(f"Table {table_name} created successfully.")
    return

put_item

id(パーティションキー)と email(ソートキー)を指定して、属性情報に name、age を指定しています。

import boto3


dynamodb = boto3.client("dynamodb", region_name="us-west-2")

def put_item() -> None:
    table_name = "test"
    response = dynamodb.put_item(
        TableName=table_name,
        Item={
            "id": {"N": "1"},
            "email": {"S": "example@example.com"},
            "name": {"S": "Yamada"},
            "age": {"N": "30"},
        },
    )
    print(response)
    return

get_item

id(パーティションキー)と email(ソートキー)を指定して、情報を取得します。

import boto3


dynamodb = boto3.client("dynamodb", region_name="us-west-2")

def get_item() -> None:
    table_name = "test"
    response = dynamodb.get_item(
        TableName=table_name,
        Key={
            "id": {"N": "1"},
            "email": {"S": "example@example.com"},
        },
    )
    item = response.get("Item")
    if item:
        print(item)
    else:
        print("Item not found")
    return

update_item

id(パーティションキー)と email(ソートキー)を指定して、属性情報を更新します。

パーティションキー、ソートキーは更新できないので、ご注意ください。

パーティションキー、ソートキーを更新する場合は、新しく put_item でアイテムを作成 します。

import boto3


dynamodb = boto3.client("dynamodb", region_name="us-west-2")

def update_item() -> None:
    table_name = "test"
    response = dynamodb.update_item(
        TableName=table_name,
        Key={"id": {"N": "1"}, "email": {"S": "example@example.com"}},
        UpdateExpression="SET age = :val1",
        ExpressionAttributeValues={":val1": {"N": "31"}},
    )
    print(response)
    return

delete_item

id(パーティションキー)と email(ソートキー)を指定して、アイテムを削除します。

import boto3


dynamodb = boto3.client("dynamodb", region_name="us-west-2")

def delete_item() -> None:
    table_name = "test"
    response = dynamodb.delete_item(
        TableName=table_name,
        Key={
            "id": {"N": "1"},
            "email": {"S": "example@example.com"},
        },
    )
    print(response)
    return

delete_table

テーブル名を指定して、テーブルを削除します。

import boto3


dynamodb = boto3.client("dynamodb", region_name="us-west-2")

def delete_table() -> None:
    table_name = "test"
    dynamodb.delete_table(
        TableName=table_name,
    )
    return

感想

今回 boto3 を使って、AWS DynamoDB の一般的な CRUD の操作をまとめました。

boto3 は指定するキーがコード上からは参照しにくく、毎回調べていたので、その手間を 減らしたいと思いまとめました。

AWS DynamoDB は癖が強いので、使う際には注意が必要ですね。

ではでは、また次回。

Python Poetry updateのwarningの解決

どうも、nippa です。

poetry のアップデートで warning がでました。

今回その対策を記事にしておきます。

環境

  • macOS 12.6
  • poetry 1.5.1

Warning の内容

poetry self updateを行った際に以下のようなアラートが表示されました。

Warning: The locked version 1.5.0 for shellingham is a yanked version. Reason for being yanked: Incorrect package metadata

このアラートはpoetry 1.5.0からshellinghamパッケージが削除もしくは無効化され たことによるアラートになります。

Warning の解決

解決方法としては、lock コマンドを利用して、update 後の環境をロックすることで解決 できます。

# 各依存関係をロックする前に、チェックを行う。
poetry self lock  --check

# 問題がなければ、ロックします。
poetry self lock

感想

poetry を update で警告が気になりましたので、記事に残しておきます。

ではでは、また次回。

macOS stable-diffusionを使ってみる

どうも、nippa です。

画像生成AIのstable-diffusionをmacOSで利用してみたいと思います。

stable-diffusion-webuiが便利そうなので、今回こちらを利用してWebUIで画像生成を行ってみます。

環境

インストール

まずは、stable-diffusion-webuiをインストールするのに必要なソフトウェアをインストールします。

brew install cmake protobuf rust python@3.10 git wget

stable-diffusion-webuiのソースコードをgitで取得します。

git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui

stable-diffusion-webuiをインストールします。

cd stable-diffusion-webui
./webui.sh

必要なソフトウェアをインストールしてくれます。以下のような表示がされればインストール完了です。

LatentDiffusion: Running in eps-prediction mode
DiffusionWrapper has 859.52 M params.
Running on local URL:  http://127.0.0.1:7860

stable-diffusion-webuiを使ってみる

ブラウザからhttp://127.0.0.1:7860にアクセスます。

下記のような画面が表示されます。

stable-diffusion-webui

Bananaというワードで、v1-5-pruned-emaonly.safetensorsモデルで画像生成してみます。

stable-diffusion-webui-banana

モデルの追加方法

ckptやsafetensorsファイルをダウンロードし、stable-diffusion-webui/models/Stable-diffusionに保存することで、利用可能になります。

試しに、実写や写真系のリアルな画像生成を得意とするrealistic-visionを追加してみます。

https://huggingface.co/SG161222にアクセスします。

今回、v2.0を利用してみます。

モデルページに遷移し、Files and versionsからRealistic_Vision_V2.0.safetensorsを選択してダウンロードします。

ダウンロードしたファイルをstable-diffusion-webui/models/Stable-diffusionに保存します。

mv ~/Download/Realistic_Vision_V2.0.safetensors [インストールディレクトリ]/stable-diffusion-webui/models/Stable-diffusion

stable-diffuison-webuiを再起動すると、Stable Diffusion checkpointからモデル選択できるようになります。

画像生成のエラーが発生した場合

画像生成時に、下記のようなエラーが発生する場合があります。

RuntimeError: "upsample_nearest2d_channels_last" not implemented for 'Half'

これは単精度による高速化オプションが有効になっており、対応していない場合に発生します。

この場合、以下のように起動時の読み込みファイルを修正することで、回避できます。

cd stable-diffusion-webui
vim webui-macos-env.sh

export COMMANDLINE_ARGSの部分を以下のように修正します。

# 修正前
export COMMANDLINE_ARGS="--skip-torch-cuda-test --upcast-sampling --no-half-vae --use-cpu interrogate"

# 修正後
export COMMANDLINE_ARGS="--skip-torch-cuda-test --upcast-sampling --no-half --use-cpu interrogatei"

修正後に再度、stable-diffusion-webuiを起動します。

./webui.sh

これで画像生成が行えるようになります。

感想

stable-diffusion-webuiを利用すると簡単に画像生成AIが利用できるようになります。

試しに使ってみてはいかがでしょうか?

ではでは、また次回。

Python TypingのLiteral型を使ってみる

どうも、nippa です。

最近 python で type annotation を使ってコード作成をしています。

Typescript では literal type をよく使うので、python でも Literal 型を扱うことが できます。

今回は typing の Laiteral についてまとめます。

環境

Typing の Literal 型

一般的な Literal 型の使い方は以下のようになります。

from typing import Literal

Fruit = Literal["apple", "banana"]

def fruit_choice(fruit: Fruit) -> None:
    print(f"You chose {fruit}.")

関数の引数に Literal 型で指定した文字列以外を指定すると、mypy でのチェックでエラ ー扱いになります。

実行時には、エラーにならず、そのまま実行されてしまいますので、注意が必要です。

Literal の結合

Literal で定義した、変数に値を追加することはできません。

typing の Uion を利用して、Literal を結合することは可能です。

from typing import Literal, Union

Fruit = Literal["apple", "banana"]
Meet = Literal["beef", "pork"]

Food = Union[Fruit, Meet]

def food_choice(food: Food) -> None:
    print(f"You chose {food}.")

このようにして、Literal 型を結合することができます。

Literal 型の list 型への変換

変数の Validation に Literal 型を使おうとするとそのままでは実行時にエラーになり ます。

get_argsを使って、引数を取得して、list 化する必要があります。

以下のような形であれば、friut か meet を判断することが

from typing import Literal, Union, get_args

Fruit = Literal["apple", "banana"]
Meet = Literal["beef", "pork"]

Food = Union[Fruit, Meet]


def food_choice(food: Food) -> None:
    if food in list(get_args(Fruit)):
        print("You chose fruit.")
    elif food in list(get_args(Meet)):
        print("You chose meet.")
    else:
        print("You cannot choose it")

Literal 型で定義した変数はこの形で Validation できます。

感想

今回、簡単な Literal 型の使い方についてまとめました。

python で literal 型を使うと開発やデバッグのコストが低下するので、必要に合わせて 使えるとよいと思っています。

ではでは、また次回。