Python 타입 검사 전략: 공개 API 검증 우선순위 지정

소스 코드 엄격성보다 공개 API 검증을 우선시하십시오

라이브러리 유지 관리자는 내부 소스 코드에만 집중하기보다 테스트 스위트에서 가능한 한 많은 타입 검사기를 실행하는 것을 우선시해야 합니다. 소스 코드에 타입 검사기를 실행하는 것은 내부 로직을 검증하지만, 테스트에서 여러 검사기를 실행하면 사용자가 어떤 도구를 사용하든 공개 API가 최대한 넓은 범위의 사용자에게 호환되고 사용 가능하도록 보장할 수 있습니다.

이 접근 방식은 소스 코드에 수많은 # type: ignore 주석이 추가되어 코드가 "pollution"되는 것을 방지합니다. Mypy, Pyrefly, Pyright, ty, Zuban과 같은 서로 다른 타입 검사기들은 Python typing spec에 대해 서로 다른 해석을 하거나 서로 다른 엄격함의 수준을 가질 수 있기 때문에, 구현 내에서 이들 모두를 만족시키려 시도하는 것은 불필요하고 주의를 분산시키는 어노테이션을 초래할 수 있습니다.

사례 연구: Polars와 __eq__ 구현

Polars 라이브러리에서 DataType.__eq__와 같은 메서드를 구현하는 것은 다중 검사기 소스 검증으로 인해 발생하는 마찰을 잘 보여줍니다. Python에서 __eq__는 일반적으로 bool을 반환하도록 기대됩니다. 그러나 Polars에서는 이 함수가 입력값에 따라 다른 타입을 반환할 수 있으므로, 오버로드(overload)를 사용해야 합니다.

소스 코드에서 Mypy, Pyrefly, ty를 동시에 만족시키기 위해 개발자는 다음과 같은 함수 시그니처를 작성해야 할 수도 있습니다:

@overload  # type: ignore[override]
def __eq__(  # pyrefly: ignore[bad-override]
    self, other: pl.DataTypeExpr
) -> pl.Expr: ...

@overload
def __eq__(self, other: PolarsDataType) -> bool: ...

def __eq__(self, other: pl.DataTypeExpr | PolarsDataType) -> pl.Expr | bool:  # ty: ignore[invalid-method-override]  # pyright: ignore[reportIncompatibleMethodOverride]

이로 인해 7줄의 코드에 4개의 서로 다른 type-ignore 주석이 발생합니다. 반대로, 동일한 기능이 테스트 스위트를 통해 검증될 때, Mypy, Pyrefly, Pyright, ty, Zuban은 모두 오류 없이 사용법을 타입 검사합니다. 이는 검사기들이 기능이 어떻게 구현되어야 하는지에 대해서는 의견이 다를 수 있지만, 공개 API가 어떻게 동작해야 하는지에 대해서는 일반적으로 동의한다는 것을 보여줍니다.

Python 타입 검사기 환경 이해하기

현재 5개의 주요 Python 타입 검사기가 있습니다: Mypy, Pyrefly, Pyright, ty, Zuban입니다. 이들 사이의 차이점은 Python typing spec의 모호함, 특히 불충분하게 지정된 타입 정보(under-specified typing information)를 처리하는 방식에서 비롯됩니다.

타입 검사기는 일반적으로 두 가지 범주로 나뉩니다:

  • Strict Checkers: 이들은 잠재적인 버그를 방지하는 것을 우선시하며, 최대의 안전성을 보장하기 위해 false positives를 발생시킬 수 있습니다. Pyrefly는 엄격하고 빠르며 규격에 부합하는 옵션으로 자리 잡고 있습니다.
  • Lenient Checkers: 이들은 타입 어노테이션을 더 점진적으로 도입할 수 있도록 하여, 기존 레거시 코드베이스에 타입을 추가할 때 발생하는 초기 마찰을 줄여줍니다.

커뮤니티 관점 및 과제

개발자들 사이의 기술적 논의는 현재 Python typing의 상태에 대해 몇 가지 시스템적적인 불만사항을 강조합니다:

파편화 및 도구 오버헤드

많은 개발자는 검사기의 급증이 "tacked on"된 타입 시스템이라는 신호라고 생각합니다. 일부 사용자들은 Pyright(in CI)와 Mypy(locally)를 모두 실행한다고 보고합니다. Pyright는 오버로드에 대해 더 엄격하고, Mypy는 None 이슈를 잡아내는 데 더 효과적이기 때문입니다.

"오류의 벽"과 추론

Mypy에서 더 엄격한 대안으로 전환하는 개발자들은 특히 Django와 같은 복잡한 복잡한 프레임워크에서 "오류의 벽"을 마라는하게 됩니다. 타입이 모든 수동식 어노테이션을 요구하는 대신 사용법(예: + 연산자)으로부터 추론론적 제약 조건과 추론에 기반한 시스템에 대한 강력한 요구가 있습니다.

정적 vs. 동적 트레이드오프

일부 비판론자들은 Python에서 광범위한 타입 검사를에 대한 노정력은 처음부터 정적 타입 언어를 사용하는 것보다 비효의적입니다. 정적 타입 언어는 AI 코딩 에이전트에게 결정론적(determinism)을 제공하고 런타임 성능 향상을 제공합니다.

신규 도구의 등장

격차를 메우기 위한 새로운 도구들이 등장하고 있습니다. 예를 들어, RightTyper는 프로그램 실행을 모모니터링하여 약 25%의 런타임 오버헤드를 발생시키며 타입 어노테이션을 자동으로 생성합니다. 이를 통해 개발자는 수동 작성 없이 기존 테스트에 타입을 통합할 수 있습니다.

Sources