【Termux+Python】Pydanticを導入してみた

【Termux+Python】Pydanticを導入してみた

Pydantic は、Python用のデータバリデーションおよびデータモデルライブラリです。Pythonの型ヒント (type hints) を活用して、データのバリデーションや変換を自動で行います。
Pydanticを導入することにより、下記の利点があります。

  • バリデーションの自動化型ヒントを使うだけで、詳細なバリデーション処理を簡単に定義できる。
  • 可読性の向上データモデルが明確になり、コードの可読性が高まる。
  • エラー対応が楽になるバリデーションエラーが詳細に表示され、デバッグしやすい。
  • API開発が効率化FastAPIなどと組み合わせると、エンドポイントのパラメータバリデーションやスキーマ生成が簡単。
  • 型変換が便利データベースや外部APIから取得したデータの変換が容易。

Pydanticの導入方法

  1. Rustをインストールする

pydantic v2は高速化のために一部をRustで実装しており、デフォルトでビルド時にRustコンパイラを要求します。したがって、Pydanticをインストールする前にRustを導入します。

$ pkg update
$ pkg upgrade
$ pip install rust
$ pip install pydantic
  1. Pydanticをインストールします。
$ pip install pydantic
$ pip install pydantic-settings
$ pip install python-dotenv

Pydanticのインストールにはかなりの時間がかかります。
フリーズしているようにも見えますので、コーヒーでも飲みながら気長に待ちましょう。

なお、.envファイルを扱う場合は、環境変数や設定ファイルから設定情報を管理・読み込むためのpydantic-settingsもインストールします。

pydantic-settings

  • .env ファイルや環境変数、設定ファイルから設定を読み込む。
  • デフォルト値や必須設定の定義。
  • Pydanticのバリデーション機能を使って設定の型チェック。

  1. Pydanticを導入したPythonのコードを書いてみる。

インストールが終わったら、早速Pydanticを導入したPythonコードを書いてみます。
次のコードは、スマホからGhostに記事をアップロードする際の認証トークンを作成するPythonのコードです。

# jwt_generator.py
import jwt
from datetime import datetime as date
from pydantic import BaseModel,  ValidationError, SecretStr, Field
from pydantic_settings import BaseSettings, SettingsConfigDict
import sys

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file="~/.termux/tasker/.env", regex_engine='python-re')  
    ADMIN_API_KEY: str = Field(pattern=r"^.*:.*$")  
    TOKEN_EXPIRY: int = 5 * 60  # トークンの有効期限(秒)

# トークン生成用のペイロードモデル
class TokenPayload(BaseModel):
    iat: int
    exp: int
    aud: str = '/admin/'

def generate_jwt(settings: Settings) -> str:
    try:
        client_id, secret = settings.ADMIN_API_KEY.split(':')
    except ValueError:
        raise ValueError("Invalid ADMIN_API_KEY format. Expected format: 'xxxx:yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy'")

#    print('client_id:'+client_id)
    iat = int(date.now().timestamp())

    header = {'alg': 'HS256', 'typ': 'JWT', 'kid': client_id}
    payload = TokenPayload(
        iat=iat,
        exp=iat + settings.TOKEN_EXPIRY
    ).model_dump() 

    token = jwt.encode(payload, bytes.fromhex(secret), algorithm='HS256', headers=header)
    return token

if __name__ == "__main__":
    try:
        settings = Settings()
        token = generate_jwt(settings)
        # sys.stdout.write(token)
        file_path = "/storage/emulated/0/Download/jwt_token.txt"
        with open(file_path, "w") as f:
            f.write(token)
    except ValidationError as e:
        sys.stderr.write(f"Configuration error: {e}\n")
    except ValueError as e:
        sys.stderr.write(f"Error: {e}\n")