Schema

バリデーションライブラリPythonデータ構造型チェック軽量シンプル

ライブラリ

Schema

概要

Schemaは「Schema validation just got Pythonic」をキャッチフレーズに開発された、シンプルで軽量なPythonデータ構造バリデーションライブラリです。設定ファイル、フォーム、外部サービス、コマンドライン引数など、JSON/YAMLから変換されたPythonデータ型の検証に特化しています。Vladimir Keleshevによって開発され、Pythonらしい直感的なAPI設計と最小限の依存関係が特徴。複雑な機能を排除し、シンプルさと使いやすさを追求したバリデーションライブラリです。

詳細

Schemaは型チェック、callableバリデータ、論理演算子(And/Or)、Optional、Use、Regexなどの基本的なバリデーション機能を提供します。優先度ベースのディスパッチメカニズムにより、様々なデータ型と検証ルールを柔軟に処理できます。単一のschema.pyファイルで構成される自己完結型の実装で、プロジェクトに簡単に組み込めます。Python 2.6から3.9まで幅広いバージョンをサポートし、draft-07 JSON Schema生成機能も備えています。

主な特徴

  • 極めてシンプルなAPI: Pythonicな記法による直感的な使用感
  • 軽量実装: 単一ファイル(schema.py)のみで動作
  • 柔軟な型チェック: Python型、callable、正規表現による検証
  • 論理演算子サポート: And/Orによる複雑な条件の組み合わせ
  • JSON Schema生成: 標準的なdraft-07形式のスキーマ出力
  • 詳細なエラー報告: SchemaErrorによる明確なエラーメッセージ

メリット・デメリット

メリット

  • 学習コストが極めて低い(シンプルなAPI)
  • 依存関係なし(単一ファイルで完結)
  • 軽量で高速な動作
  • Pythonらしい直感的な記法
  • 幅広いPythonバージョンサポート(2.6〜3.9)
  • MITライセンスによる自由な利用

デメリット

  • 高度なバリデーション機能の不足
  • 型変換機能が限定的
  • エコシステムが小規模
  • 大規模プロジェクトには機能不足
  • パフォーマンス最適化されていない
  • 非同期処理未対応

参考ページ

書き方の例

インストールと基本セットアップ

# pipでインストール
pip install schema

# または単一ファイルをプロジェクトにコピー
wget https://raw.githubusercontent.com/keleshev/schema/master/schema.py

基本的な型チェック

from schema import Schema

# 単純な型チェック
Schema(int).validate(123)       # 成功: 123を返す
Schema(str).validate("hello")   # 成功: "hello"を返す

# 型が一致しない場合
try:
    Schema(int).validate("123")  # SchemaError発生
except SchemaError as e:
    print(f"エラー: {e}")

# is_validで検証のみ行う
if Schema(int).is_valid(123):
    print("有効なデータです")

辞書の検証

from schema import Schema, And, Use, Optional

# ユーザー情報の検証スキーマ
user_schema = Schema({
    'name': And(str, len),  # 空でない文字列
    'age': And(Use(int), lambda n: 18 <= n <= 99),  # 文字列を整数に変換して範囲チェック
    Optional('email'): And(str, lambda s: '@' in s),  # オプショナルなメールアドレス
    Optional('gender'): And(str, Use(str.lower), lambda s: s in ('male', 'female'))
})

# データの検証
data = {
    'name': 'Taro Yamada',
    'age': '25',  # 文字列で渡されても整数に変換
    'email': '[email protected]'
}

validated = user_schema.validate(data)
print(validated)  # {'name': 'Taro Yamada', 'age': 25, 'email': '[email protected]'}

リストとネストした構造の検証

from schema import Schema, And, Use

# 商品リストの検証
products_schema = Schema([{
    'id': And(int, lambda n: n > 0),
    'name': And(str, len),
    'price': And(Use(float), lambda p: p > 0),
    'tags': [str],  # 文字列のリスト
    'stock': {
        'quantity': int,
        'warehouse': str
    }
}])

# データの検証
products = [{
    'id': 1,
    'name': 'ノートPC',
    'price': '89800',  # 文字列から浮動小数点に変換
    'tags': ['電子機器', 'コンピュータ'],
    'stock': {
        'quantity': 15,
        'warehouse': '東京倉庫'
    }
}]

validated = products_schema.validate(products)

callableバリデータとカスタムエラー

from schema import Schema, Use, SchemaError

# カスタムバリデータ関数
def validate_email(email):
    import re
    pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$'
    if not re.match(pattern, email):
        raise SchemaError(f'無効なメールアドレス: {email}')
    return email

# パスワード強度チェック
def strong_password(password):
    if len(password) < 8:
        return False
    if not any(c.isupper() for c in password):
        return False
    if not any(c.isdigit() for c in password):
        return False
    return True

# スキーマ定義
account_schema = Schema({
    'username': And(str, lambda s: 3 <= len(s) <= 20),
    'email': Use(validate_email),
    'password': And(str, strong_password, error='パスワードは8文字以上で、大文字と数字を含む必要があります')
})

# 検証実行
try:
    account_schema.validate({
        'username': 'user123',
        'email': '[email protected]',
        'password': 'weak'  # エラーになる
    })
except SchemaError as e:
    print(f"バリデーションエラー: {e}")

OrとForbiddenの使用

from schema import Schema, Or, Forbidden

# 認証方法の選択(emailまたはusernameのいずれか一つ)
auth_schema = Schema({
    Or('email', 'username', only_one=True): str,
    'password': str,
    Forbidden('admin'): object  # adminフィールドは禁止
})

# 有効なデータ
auth_schema.validate({'email': '[email protected]', 'password': 'Pass123!'})
auth_schema.validate({'username': 'user123', 'password': 'Pass123!'})

# エラーケース
try:
    # 両方指定するとエラー
    auth_schema.validate({
        'email': '[email protected]',
        'username': 'user123',
        'password': 'Pass123!'
    })
except SchemaError as e:
    print(f"エラー: {e}")

JSON Schema生成

from schema import Schema, Optional, Literal
import json

# スキーマ定義(説明付き)
api_schema = Schema(
    {
        Literal("name", description="ユーザーの表示名"): str,
        Literal("age", description="年齢(18歳以上)"): And(int, lambda n: n >= 18),
        Optional(Literal("bio", description="自己紹介文")): str
    },
    description="ユーザープロファイルAPI"
)

# JSON Schema生成
json_schema = api_schema.json_schema("https://example.com/user-profile.json")
print(json.dumps(json_schema, indent=2, ensure_ascii=False))