Pydantic은 데이터의 유효성을 검증할 수 있는 대표적인 라이브러리입니다. 파이썬의 타입 힌트를 활용해 데이터의 유효성을 검사하고 오류 메시지를 제공하는데 사용됩니다. JSON과 쉽게 연동되기 때문에 FastAPI 같은 웹 프레임워크와 사용해 API의 입력 데이터와 출력 데이터를 검증하는 데 유용합니다.
일반적인 사용 사례는 다음과 같습니다.
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
# 유효한 데이터 생성
user1 = User(name="John", age=30)
print(user1)
# 유효하지 않은 데이터 생성
try:
user2 = User(name="Jane", age="twenty")
except Exception as e:
print(e)
위 코드에서 User 클래스는 pydantic의 BaseModel을 상속받아 name과 age 속성을 정의합니다. user1은 유효한 데이터를 생성하지만, user2는 age가 문자열이기 때문에 오류가 발생합니다. 위 사례는 조금 단순한 사례이기 때문에 조금 더 복잡한 사례로 연습해보겠습니다.
Case1 : Web Input Validation
웹을 구축하면서 요청과 응답에 대해 크게 3가지를 체크하는 것을 생각해보겠습니다.
- 올바른 url 입력
- 1~10 사이의 정수 입력
- 올바른 폴더 이름 입력
python 버전 3.7 이상을 사용한다면 dataclasses라는 라이브러리를 활용할 수 있습니다. 라이브러리 내의 dataclass를 데코레이터를 사용해 init 메서드로 별도로 정의할 필요가 없고, __post_init__ 메서드로 검증을 수행하는 로직을 생성 시점에서 수행하게끔 합니다.
from dataclasses import dataclass
class ValidationError(Exception):
pass
@dataclass
class ModelInput:
url : str
rate : int
target_dir : str
def _validate_url(self, url: str) -> bool:
from urllib.parse import urlparse
try:
result = urlparse(url)
return all([result.scheme, result.netloc])
except:
return False
def _validate_directory(self, dir: str) -> bool:
import os
return os.path.isdir(dir)
def validate(self) -> bool:
validation_results = [
self._validate_url(self.url),
1 <= self.rate <= 10,
self._validate_directory(self.target_dir)
]
return all(validation_results) # 모두 True일 때 True 반환
def __post_init__(self):
if not self.validate():
raise ValidationError("Incorrect input")
if __name__ == '__main__':
try:
dataclasses_test = ModelInput(**INPUT) # INPUT은 딕셔너리 형태의 입력 데이터
except ValidationError as exc:
print('Error : ', exc.json())
pass
원하는 데로 파이썬의 저수준에서 검증 로직을 만든다는 점에서는 좋으나, 검증 로직을 쌓아가야 하기 때문에 코드가 길어질 수 있습니다. 이러한 부분을 보완하기에 적합한 것이 pydantic 입니다. 이미 만들어져 있는 클래스와 함수들을 가져다 씀으로 동일한 기능을 훨씬 짧은 코드로 구현이 가능합니다.
from pydantic import BaseModel, HttpUrl, Field, DirectoryPath, ValidationError
class ModelInput:
url : HttpUrl
rate : int = Field(ge=1, le=10)
target_idr : DirectoryPath
if __name__ == '__main__':
try:
pydantic_test = ModelInput(**INPUT)
except ValidationError as exc:
print('Error : ', exc.json())
pass
Case2 : Config 관리
Config란 Configuration(환경 설정)의 약자로 코드에 필요한 여러 변수들을 저장해두고 사용하는 것을 말합니다. 이를 위해 코드 내에서 활용하거나 yaml과 같은 파일을 만들어서 읽어주거나 pydantic을 활용할 수 있습니다.
다음 예제는 pydantic을 사용해서 애플리케이션의 설정을 관리하고 오버라이드(덮어 쓰기)하는 예제입니다. 우선 기본적인 환경 설정을 정의합니다. 과거에는 pydantic에 있었으나 pydantic-settings로 옮겨진 BaseSettings 클래스를 상속받습니다.
from pydantic import Field
from pydantic_settings import BaseSettings
from enum import Enum
class ConfigEnv(str, Enum):
DEV = "dev"
PROD = "prod"
class DBConfig(BaseSettings):
host: str = Field(default="localhost", env="db_host")
port: int = Field(default=3306, env="db_port")
username: str = Field(default="user", env="db_username")
password: str = Field(default="user", env="db_password")
database: str = Field(default="dev", env="db_database")
class AppConfig(BaseSettings):
env: ConfigEnv = Field(default="dev", env="env")
db: DBConfig = DBConfig()
Field 함수는 모델의 필드에 메타데이터를 추가하거나 바꾸는 기능을 수행합니다. 여기서는 default와 env 인자를 사용했는데, 각각 다음과 같이 정리할 수 있습니다.
- default : 해당 필드의 기본 값을 지정. host : str = Field(default="localhost")는 host 필드의 기본값이 localhost 임.
- env : 해당 필드의 환경 변수로 지정해서 해당 변수에서 값을 가져오도록 설정.
전체 애플리케이션에 대한 기본 설정을 담은 yaml 파일이 있다고 가정하고, 이를 로드해 AppConfig 클래스에 전달합니다. 그리고 이를 검증하는 과정을 거칩니다.
with open("dev_config.yaml", "r") as f:
config = load(f, FullLoader)
config_with_pydantic = AppConfig(**config)
assert config_with_pydantic.env == "dev"
assert config_with_pydantic.db.model_dump() == expected
만약 환경 변수로 설정 오버라이딩을 원하는 경우 아래와 같이 수정할 수 있습니다. 필요하다면 검증도 추가적으로 할 수 있습니다.
os.environ["ENV"] = "prod"
os.environ["DB_HOST"] = "mysql"
os.environ["DB_USERNAME"] = "admin"
os.environ["DB_PASSWORD"] = "SOME_SAFE_PASSWORD"
prod_config_with_pydantic = AppConfig()
assert prod_config_with_pydantic.env == "prod"
assert prod_config_with_pydantic.model_dump() != expected
이를 통해 BaseSettings에서 상속을 받아서 타입에 대한 검증 수행이 가능하고, Field 클래스의 env 인자는 해당 필드로 바꿔서 써주는 효과가 있습니다.
참고자료
[1] 변성윤. "[Product Serving] FastAPI (2)". boostcamp AI Tech.
[2] https://docs.pydantic.dev/latest/concepts/models/
[3] https://docs.pydantic.dev/latest/concepts/pydantic_settings/
[4] https://docs.pydantic.dev/latest/concepts/fields/
[5] https://docs.pydantic.dev/latest/migration/#basesettings-has-moved-to-pydantic-settings
'Python > Data Prep' 카테고리의 다른 글
파이썬 이미지 처리 라이브러리 비교 | PIL, OpenCV, Numpy, PyTorch, Albumentations (0) | 2024.11.18 |
---|---|
DeepL을 활용해서 외국어 OCR 영수증 데이터셋 합성하기 (2) | 2024.11.09 |
[OD] 객체 탐지(Object Detection) 대표 데이터 포맷 공부 | COCO, Pascal VOC, YOLO (0) | 2024.10.04 |
DataLoader에서 오류가 난다면 누락 데이터가 있는지 확인 필요 | DataLoader는 이터레이터 (2) | 2024.09.26 |
PyTorch에서 Dataset과 DataLoader 클래스를 활용해 데이터 파이프라인 구축하기 (1) | 2024.09.09 |