validators

Python用のシンプルで使いやすいデータ検証ライブラリ

validators

validatorsは、Pythonで様々な形式のデータを検証するためのシンプルで使いやすいライブラリです。メールアドレス、URL、IPアドレス、クレジットカード番号など、一般的なデータ形式の検証機能を提供しています。

主な特徴

  • 豊富な検証機能: メール、URL、ドメイン、IPアドレス、UUID、クレジットカードなど多数の検証器を提供
  • シンプルなAPI: 直感的で使いやすい関数ベースのインターフェース
  • 軽量: 最小限の依存関係で高速に動作
  • Python標準: Python 3.8以上に対応
  • 型ヒント対応: 完全な型アノテーションによる開発体験の向上

インストール

pip install validators

基本的な使い方

メールアドレスの検証

import validators

# 有効なメールアドレス
result = validators.email('[email protected]')
print(result)  # True

# 無効なメールアドレス
result = validators.email('invalid-email')
print(result)  # ValidationError(func=email, args={'value': 'invalid-email'})

# 真偽値での判定
if validators.email('[email protected]'):
    print("有効なメールアドレスです")

URLの検証

import validators

# HTTPSのURL
result = validators.url('https://www.example.com')
print(result)  # True

# HTTPのURL
result = validators.url('http://example.com/path')
print(result)  # True

# 無効なURL
result = validators.url('not a url')
print(result)  # ValidationError(func=url, args={'value': 'not a url'})

# プロトコルを限定
result = validators.url('https://example.com', public=True)
print(result)  # True

利用可能な検証器

ネットワーク関連

# ドメイン名
validators.domain('example.com')  # True
validators.domain('sub.example.com')  # True

# IPアドレス(IPv4)
validators.ipv4('192.168.1.1')  # True
validators.ipv4('192.168.1.1', cidr=False)  # CIDR記法を無効化

# IPアドレス(IPv6)
validators.ipv6('::1')  # True
validators.ipv6('2001:0db8:85a3::8a2e:0370:7334')  # True

# MACアドレス
validators.mac_address('01:23:45:67:89:ab')  # True
validators.mac_address('01-23-45-67-89-AB')  # True

識別子関連

# UUID
validators.uuid('550e8400-e29b-41d4-a716-446655440000')  # True
validators.uuid('550e8400-e29b-41d4-a716-446655440000', version=4)  # バージョン指定

# MD5ハッシュ
validators.md5('5d41402abc4b2a76b9719d911017c592')  # True

# SHA1ハッシュ
validators.sha1('da39a3ee5e6b4b0d3255bfef95601890afd80709')  # True

# SHA256ハッシュ
validators.sha256('e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855')  # True

金融関連

# クレジットカード番号(Luhnアルゴリズム)
validators.card_number('4111111111111111')  # True (Visa)
validators.card_number('5500000000000004')  # True (Mastercard)

# IBAN(国際銀行口座番号)
validators.iban('DE89370400440532013000')  # True
validators.iban('GB82WEST12345698765432')  # True

その他の検証器

# スラッグ(URLフレンドリーな文字列)
validators.slug('my-blog-post')  # True
validators.slug('my_blog_post')  # False(アンダースコアは無効)

# 整数
validators.integer(42)  # True
validators.integer('42')  # True(文字列形式の整数も受け付ける)
validators.integer(3.14)  # False

# 真偽値
validators.truthy(True)  # True
validators.truthy(1)  # True
validators.truthy('yes')  # True
validators.truthy('false')  # True(文字列'false'は真と判定)

エラーハンドリング

import validators

# ValidationErrorの処理
result = validators.email('invalid-email')

if isinstance(result, validators.ValidationError):
    print(f"検証エラー: {result}")
    print(f"関数名: {result.func}")
    print(f"引数: {result.args}")

# 例外として扱う
try:
    email = 'invalid-email'
    if not validators.email(email):
        raise ValueError(f"無効なメールアドレス: {email}")
except ValueError as e:
    print(e)

カスタム検証器の作成

import validators
from validators import ValidationError

def custom_password(value, min_length=8, require_special=True):
    """カスタムパスワード検証器"""
    if not isinstance(value, str):
        return ValidationError("パスワードは文字列である必要があります")
    
    if len(value) < min_length:
        return ValidationError(f"パスワードは{min_length}文字以上である必要があります")
    
    if require_special and not any(c in "!@#$%^&*()_+-=" for c in value):
        return ValidationError("パスワードには特殊文字を含める必要があります")
    
    return True

# 使用例
print(custom_password("mypassword123!"))  # True
print(custom_password("short"))  # ValidationError

複数の検証を組み合わせる

import validators

def validate_user_input(email, website, age):
    """ユーザー入力の総合的な検証"""
    errors = {}
    
    # メールアドレスの検証
    if not validators.email(email):
        errors['email'] = "有効なメールアドレスを入力してください"
    
    # ウェブサイトの検証
    if website and not validators.url(website):
        errors['website'] = "有効なURLを入力してください"
    
    # 年齢の検証
    if not validators.integer(age) or int(age) < 0 or int(age) > 150:
        errors['age'] = "有効な年齢を入力してください(0-150)"
    
    return errors if errors else None

# 使用例
errors = validate_user_input(
    email="[email protected]",
    website="https://example.com",
    age="25"
)

if errors:
    print("検証エラー:", errors)
else:
    print("すべての入力が有効です")

Djangoとの統合

from django import forms
import validators

class UserProfileForm(forms.Form):
    """validators を使用したDjangoフォーム"""
    
    email = forms.CharField(max_length=255)
    website = forms.CharField(max_length=255, required=False)
    twitter_handle = forms.CharField(max_length=50, required=False)
    
    def clean_email(self):
        email = self.cleaned_data['email']
        if not validators.email(email):
            raise forms.ValidationError("有効なメールアドレスを入力してください")
        return email
    
    def clean_website(self):
        website = self.cleaned_data['website']
        if website and not validators.url(website):
            raise forms.ValidationError("有効なURLを入力してください")
        return website
    
    def clean_twitter_handle(self):
        handle = self.cleaned_data['twitter_handle']
        if handle and not validators.slug(handle.lstrip('@')):
            raise forms.ValidationError("有効なTwitterハンドルを入力してください")
        return handle

ベストプラクティス

1. 検証結果の適切な処理

import validators

def process_email(email_input):
    """メールアドレスを処理する関数"""
    # 検証結果を明示的にチェック
    validation_result = validators.email(email_input)
    
    if validation_result is True:
        # 正常な処理
        return email_input.lower()
    else:
        # エラー処理
        raise ValueError(f"無効なメールアドレス: {validation_result}")

2. 型安全な検証

from typing import Union
import validators

def validate_input(value: str) -> Union[bool, validators.ValidationError]:
    """型アノテーションを使用した検証"""
    if validators.email(value):
        return True
    elif validators.url(value):
        return True
    else:
        return validators.ValidationError(
            func='validate_input',
            args={'value': value, 'message': '有効なメールアドレスまたはURLを入力してください'}
        )

3. バッチ検証

import validators
from typing import List, Dict, Any

def batch_validate_emails(emails: List[str]) -> Dict[str, Any]:
    """複数のメールアドレスを一度に検証"""
    results = {
        'valid': [],
        'invalid': []
    }
    
    for email in emails:
        if validators.email(email):
            results['valid'].append(email)
        else:
            results['invalid'].append(email)
    
    return results

# 使用例
emails = [
    '[email protected]',
    '[email protected]',
    'invalid-email',
    '[email protected]'
]

results = batch_validate_emails(emails)
print(f"有効: {results['valid']}")
print(f"無効: {results['invalid']}")

4. カスタム検証ルールの作成

import validators
import re

class CustomValidators:
    """アプリケーション固有の検証ルール"""
    
    @staticmethod
    def japanese_phone(number: str) -> bool:
        """日本の電話番号を検証"""
        # ハイフンを除去
        number = number.replace('-', '')
        
        # 日本の電話番号パターン
        patterns = [
            r'^0\d{9,10}$',  # 固定電話
            r'^0[789]0\d{8}$',  # 携帯電話
            r'^050\d{8}$',  # IP電話
        ]
        
        return any(re.match(pattern, number) for pattern in patterns)
    
    @staticmethod
    def strong_password(password: str) -> bool:
        """強力なパスワードを検証"""
        if len(password) < 12:
            return False
        
        has_upper = any(c.isupper() for c in password)
        has_lower = any(c.islower() for c in password)
        has_digit = any(c.isdigit() for c in password)
        has_special = any(c in "!@#$%^&*()_+-=[]{}|;:,.<>?" for c in password)
        
        return all([has_upper, has_lower, has_digit, has_special])

# 使用例
print(CustomValidators.japanese_phone("090-1234-5678"))  # True
print(CustomValidators.strong_password("MyStr0ng!Pass123"))  # True

まとめ

validatorsは、Pythonアプリケーションで一般的なデータ検証タスクを簡単に実装できる優れたライブラリです。シンプルなAPIと豊富な検証機能により、ユーザー入力の検証、データクレンジング、フォームバリデーションなど、様々な場面で活用できます。カスタム検証器の作成も容易で、アプリケーション固有の要件にも柔軟に対応できます。