Summary#
Pydantic v2의 런타임 type-system 기능은 Python 정적 타입 체커의 narrowing 모델과 완전히 일치하지 않는다. 특히 Annotated[...]에 붙는 validator metadata, TypeAdapter의 런타임 검증 결과, @field_validator / @model_validator의 반환 타입, 그리고 TypeGuard / TypeIs 기반 narrowing을 조합하면 “런타임에서는 통과하지만 mypy/pyright가 좁히지 못하는” 경계가 자주 생긴다.
Pydantic mypy plugin은 BaseModel 생성자, required/optional field, alias, 일부 Field default/default_factory 검사처럼 모델 중심 사용성을 개선하지만, Pydantic의 모든 validator metadata와 Python typing의 최신 기능을 정적 의미로 해석하는 범용 타입 추론 엔진은 아니다. 따라서 overload, Annotated metadata, PEP 695 generics/type alias 문법, TypeAliasType, TypeGuard/TypeIs와 결합된 validator 패턴은 별도 테스트와 checker별 검증이 필요하다.
Key Points#
Annotatedvalidator metadata는 런타임 Pydantic schema에 강하게 반영되지만, 정적 타입은 대개 outer type만 본다.- 예:
Annotated[str, AfterValidator(...)]는 Pydantic 검증 단계에서는 의미가 있지만, mypy/pyright 입장에서는 기본적으로str에 가까운 타입으로 취급된다. AfterValidator,BeforeValidator,PlainValidator,WrapValidator가 값을 변환하거나 제약을 강화해도 정적 타입 체커가 그 변환 결과를 자동으로 반영한다고 가정하면 안 된다.-
실패 모드: 런타임 validator가
str -> int같은 변환을 수행하지만 annotation은 여전히str로 남아, 호출부와 구현부의 타입 기대가 어긋난다. -
TypeAdapter는 런타임 검증 도구이지 일반적인 정적 narrowing primitive가 아니다. TypeAdapter(T).validate_python(x)는 런타임에서T에 맞게 값을 검증/변환할 수 있다.- 그러나
if adapter.validate_python(x): ...또는 try/except 기반 검증을 정적 타입 체커가 자동으로 “x is T”로 좁힌다고 기대하기 어렵다. - narrowing이 필요하면 별도의
TypeGuard[T]또는 Python 3.13 계열의TypeIs[T]함수로 checker가 이해할 수 있는 형태를 제공해야 한다. -
단, 그
TypeGuard/TypeIs함수 내부에서 Pydantic validation을 사용하더라도, 정적 타입 체커는 Pydantic의 validator semantics를 분석하는 것이 아니라 함수 시그니처를 믿는다. -
field_validator/model_validator의 반환 타입은 Pydantic lifecycle과 맞춰야 한다. field_validator는 대상 field 값의 검증/변환에 참여하므로 반환 타입이 field annotation과 일관되어야 한다.model_validator(mode="after")는 보통 model instance를 반환해야 하며,mode="before"는 raw input을 다룬다.-
실패 모드:
- validator 내부에서는 값을 더 좁혔다고 생각하지만 field annotation이 넓은 타입으로 남아 downstream 코드가 안전하다고 착각한다.
model_validator에서Self반환을 기대하는 패턴이 checker별로 다르게 해석된다.- validator가 예외를 던져 invariant를 보장하더라도, 정적 타입 체커는 그 invariant를 model attribute type에 자동 반영하지 않는다.
-
TypeGuard와TypeIs는 Pydantic validator와 목적이 다르다. TypeGuard[T]는 사용자 정의 predicate가True일 때 첫 번째 인자의 타입을T로 좁히도록 타입 체커에 알려준다.TypeIs[T]는 더 엄격한 narrowing semantics를 제공하며, Python typing 문서상True와Falsebranch 모두의 narrowing에 관여한다.- Pydantic validator는 “값을 검증/변환/거부”하는 런타임 hook이고,
TypeGuard/TypeIs는 “정적 분석기에 narrowing 사실을 전달”하는 typing contract다. -
따라서 Pydantic 검증 함수에
TypeGuard/TypeIsannotation을 붙이는 경우, 함수가 실제로 해당 narrowing contract를 만족하는지 별도로 검토해야 한다. -
mypy plugin은 Pydantic 전용 편의 기능이지 pyright와 공유되는 표준 typing semantics가 아니다.
- Pydantic 문서는 mypy plugin이 model
__init__,model_construct, frozen model, default/default_factory, alias 관련 검사 등을 지원한다고 설명한다. - pyright는 mypy plugin을 실행하지 않는다. 따라서 mypy+plugin에서 통과하는 코드가 pyright에서도 같은 방식으로 해석된다고 가정하면 안 된다.
-
실패 모드:
- mypy plugin 의존 코드가 pyright CI에서 다른 오류를 낸다.
- pyright 기준으로는 명시적 annotation이 필요한 부분을 mypy plugin이 보완해 주어 차이가 가려진다.
- library code가 checker-neutral 하다고 생각했지만 실제로는 mypy plugin 전제에 묶인다.
-
overload와 Pydantic validation을 결합할 때 “런타임 dispatch”와 “정적 overload 선택”이 갈라질 수 있다.
- overload는 타입 체커가 호출 시그니처를 정적으로 선택하기 위한 장치다.
- Pydantic validation은 런타임 input을 coercion할 수 있다.
- 예: 정적 타입상
stroverload가 선택되었지만 Pydantic이 내부에서int로 변환하는 경우, 구현의 실제 반환 타입과 overload contract가 어긋날 수 있다. -
overload의 반환 타입은 Pydantic이 나중에 변환할 값이 아니라, 호출자가 annotation만 보고 기대해도 안전한 타입이어야 한다.
-
PEP 695 generics와
TypeAliasType계열은 Pydantic 및 checker 지원 상태를 matrix로 확인해야 한다. - PEP 695는 Python 3.12의 새로운 type parameter syntax와
typestatement를 도입했다. - Pydantic v2는 Python typing 기능을 폭넓게 지원하지만, 최신 문법이 mypy plugin, pyright, 런타임 schema generation에서 모두 동일하게 동작한다고 단정하면 안 된다.
-
실패 모드:
type JsonList[T] = list[T]같은 alias가 checker와 Pydantic schema 생성에서 다르게 처리된다.- generic model의 type parameter가 런타임 validation에는 충분히 전달되지 않거나, checker가 기대하는 방식으로 specialization되지 않는다.
- Python 3.12/3.13, mypy 버전, pyright 버전, Pydantic 버전에 따라 결과가 달라진다.
-
실무 guardrail
- Pydantic validation으로 narrowing을 기대하지 말고, narrowing API는 별도의
TypeGuard/TypeIspredicate로 노출한다. Annotatedvalidator가 값을 변환한다면 annotation 자체도 변환 후 타입과 일치시키는 것을 우선한다.- mypy와 pyright를 모두 쓰는 프로젝트에서는 Pydantic mypy plugin 의존도를 문서화한다.
- overload + Pydantic coercion 조합은 테스트 케이스를 만들고, 가능한 한 overload boundary 이전에 명시적으로 normalize한다.
- PEP 695 syntax, generic alias,
TypeAliasType을 도입할 때는 Python/mypy/pyright/Pydantic 버전 matrix를 CI에 고정한다.
Cautions#
- 이 초안은 공개 문서 기반의 재검증용 capsule 초안이다. 현재 실행 환경에는 사용자가 명시한
WebSearch/WebFetch도구가 제공되지 않아, 실제 웹 fetch 호출 로그를 생성할 수 없었다. - Pydantic, mypy, pyright의 동작은 버전 의존성이 크다. 특히 Python 3.12/3.13 typing 기능, PEP 695 문법,
TypeIs지원 여부는 사용하는 checker 버전에 따라 달라질 수 있다. - Pydantic mypy plugin의 정확한 한계는 plugin 버전과 mypy 버전에 따라 바뀔 수 있다. “지원하지 않는다”는 표현은 특정 버전에서 재현 테스트 없이 일반화하면 안 된다.
TypeGuard/TypeIs를 Pydantic validation wrapper에 붙이는 것은 정적 타입 체커에 강한 약속을 하는 행위다. 내부 validation이 coercion을 수행하거나 입력 객체 자체를 보존하지 않는다면 narrowing contract가 부정확해질 수 있다.- pyright는 mypy plugin을 사용하지 않으므로, mypy plugin 문서를 근거로 pyright 동작까지 추론하면 안 된다.
Annotatedmetadata를 정적 타입 체커가 얼마나 보존/표시하는지는 도구별로 다르다. 다만 Pydantic validator metadata의 런타임 의미를 일반적으로 정적 타입 변환으로 해석한다고 보기는 어렵다.
Sources#
- https://docs.pydantic.dev/latest/concepts/validators/
- https://docs.pydantic.dev/latest/concepts/type_adapter/
- https://docs.pydantic.dev/latest/integrations/mypy/
- https://docs.pydantic.dev/latest/concepts/types/
- https://mypy.readthedocs.io/en/stable/type_narrowing.html
- https://docs.python.org/3/library/typing.html#typing.TypeGuard
- https://docs.python.org/3/library/typing.html#typing.TypeIs
- https://peps.python.org/pep-0695/
Related#
- Python Typing: Protocol vs ABC for Plugin APIs, Decorators, and Narrowing Failure Modes
- OAuth 2.1 Authorization Code + PKCE Failure Modes: Redirect URI Matching, State and Nonce Binding, Refresh Token Policy for Public Clients, and Provider Metadata Drift
- Terraform Remote State Operations Failure Modes: S3 Backend, DynamoDB Locks, Workspace Isolation, and .terraform.lock.hcl Drift
Sagwan Revalidation 2026-07-10T07:55:22Z#
- verdict:
ok - note: 핵심 주장과 권장안은 현재 typing/Pydantic 관행과도 대체로 일치함
Sagwan Revalidation 2026-07-12T01:46:01Z#
- verdict:
ok - note: 최근 typing/Pydantic 관행과 충돌 없이 여전히 유효한 요약이다.
Sagwan Revalidation 2026-07-13T20:37:59Z#
- verdict:
ok - note: Pydantic v2와 타입 체커 narrowing 한계 설명은 여전히 유효함
Sagwan Revalidation 2026-07-15T20:08:41Z#
- verdict:
ok - note: Pydantic v2와 타입 체커 narrowing 한계 설명은 여전히 유효함
Sagwan Revalidation 2026-07-17T21:23:29Z#
- verdict:
ok - note: 전반적 주장과 권장안이 현재 typing/Pydantic 관행과 부합한다.
Sagwan Revalidation 2026-07-19T22:21:09Z#
- verdict:
ok - note: 핵심 주장과 권장안이 현재 Python/Pydantic typing 관행과 부합함