Hacker News

Type Server Protocol

Type Server Protocol: Python 타입 분석 서버 프로토콜

Microsoft가 Python 타입 분석 결과를 구조화된 데이터로 주고받는 JSON-RPC 프로토콜 TSP를 공개했습니다. LSP 연결과 워크스페이스 상태를 활용해 타입·import 정보를 질의하며, 스냅샷과 선택적 읽기 전용 연결로 결과의 유효성과 동시 요청을 관리합니다.

에디터 노트

구조화된 타입 결과는 사람이 읽는 hover 문자열을 파싱하지 않고 편집기나 도구가 타입을 처리하게 합니다. 댓글에서 지적했듯 LSP의 기존 응답은 대체로 표시용 문자열이며 형식도 충분히 규정되지 않았으므로, TSP가 채우려는 공백은 구체적입니다. 반면 프로토콜 정의와 타입 종류가 Python에 맞춰져 있어 범용 이름과 실제 적용 범위 사이에는 간격이 있습니다. 별도 프로토콜은 형식과 스냅샷, 다중 연결 규칙을 명시하지만 LSP 확장으로 구현하지 않은 이유는 제공된 문서에 설명돼 있지 않습니다. Basedpyright가 지원을 거부한다는 댓글은 호환성과 생태계 채택에 관한 우려를 보여주지만, 그 비판의 동기 설명은 댓글 작성자의 주장입니다. 현재 문서가 뒷받침하는 성과는 Python 타입 정보를 구조화해 질의하는 규약을 정의했다는 데까지입니다.

AI 요약

Microsoft의 Type Server Protocol(TSP)은 Python 타입 분석 서버에 분석 정보를 질의하는 JSON-RPC 프로토콜입니다. 서버는 워크스페이스의 타입 분석 상태를 관리하고 프로토콜 버전, 분석 스냅샷, Python 검색 경로, import 해석, 계산된 타입·선언된 타입·기대 타입을 반환합니다. 메시지 이름에는 typeServer/ 접두사를 붙입니다. 위치와 범위, URI 표현 등은 Language Server Protocol(LSP)의 규칙과 자료형을 따릅니다.

프로토콜 정의와 호환성

프로토콜 정의의 기준 파일은 TypeScript 인터페이스와 요청 형식을 담은 typeServerProtocol.ts, 기계 판독용 모델인 tsp.json, 그 JSON Schema인 tsp.schema.json입니다. 클라이언트와 서버는 semantic version 문자열로 호환성을 확인합니다. 0.x 버전에서는 마이너 버전이 바뀌면 호환성이 깨질 수 있지만, 같은 마이너 버전 안의 패치 변경은 하위 호환을 유지합니다. 버전은 0.1.0에서 시작해 0.4.1까지 갱신됐으며, 0.4.1에는 다중 연결 협상과 연결 제어 요청이 추가됐습니다.

연결과 요청 순서

클라이언트는 서버가 지원하는 전송 방식으로 연결합니다. 주 연결은 stdio나 Node IPC를 사용할 수 있으며, stdio를 쓸 때 표준 출력은 JSON-RPC 메시지 전용으로 남겨야 합니다. 서버가 요청에 답할 만큼 워크스페이스와 문서 상태를 받아야 하므로, 클라이언트는 보통 LSP 초기화와 문서 동기화를 먼저 진행한 뒤 같은 연결에서 TSP 요청을 보냅니다. 일반적인 순서는 서버 연결, LSP 초기화와 initialized 처리, 지원 프로토콜 버전 확인, 첫 스냅샷 조회, 타입·import·검색 경로 요청입니다. 분석 결과가 무효화되면 서버는 typeServer/snapshotChanged 알림을 보냅니다.

TSP는 선택 기능으로 읽기 전용 연결을 추가할 수 있습니다. 초기화 때 클라이언트와 서버가 capabilities.experimental.typeServerMultiConnection에 지원 전송 방식을 알리고, 두 목록에 공통 방식이 있을 때만 기능을 켭니다. 지원 정보가 없거나 공통 전송 방식이 없으면 클라이언트는 typeServer/connection을 보내지 않고 단일 연결을 사용합니다. 추가 연결에 현재 내장 지원되는 방식은 IPC입니다. 연결을 열거나 닫는 요청은 주 연결에서 보내며, IPC 전송에 따라 전체 양방향 스트림 하나 또는 입력·출력 엔드포인트 두 개를 전달합니다.

주 연결은 초기화, 워크스페이스와 문서 동기화, 설정 변경, 스냅샷 알림, 연결 제어를 맡습니다. 추가 연결은 협상한 TSP 버전에서 허용한 읽기 전용 요청만 처리합니다. 타입 조회나 import 해석처럼 상태를 바꾸지 않는 요청을 병렬 처리하는 데 쓸 수 있습니다. 서버는 추가 연결에서 LSP 메시지, 상태 변경 알림, 연결 제어 요청, 해당 버전에서 허용하지 않은 TSP 요청을 거부해야 합니다. 다중 연결은 성능을 위한 선택 사항이며 단일 연결에서도 프로토콜이 정상 동작해야 합니다.

스냅샷과 타입 응답

스냅샷은 분석 상태의 특정 시점을 나타내는 0 이상의 정수입니다. 파일 변경, 설정 변경, 감시 대상 파일이나 의존성 변경, 분석 결과를 바꾸는 캐시 초기화가 있으면 기존 결과가 무효가 될 수 있으므로 서버는 스냅샷을 올려야 합니다. 스냅샷 번호는 세션 동안 감소하지 않습니다. 오래된 스냅샷을 넣은 요청은 클라이언트가 새 스냅샷을 받아 재시도하도록 JSON-RPC 오류로 알려야 하며, LSP의 ServerCancelled 코드인 -32802를 권장합니다. 핸들, 선언, 심볼 등 반환 객체도 요청에 사용한 스냅샷에 묶입니다.

세 가지 타입 질의는 같은 위치를 대상으로 서로 다른 정보를 돌려줍니다. getComputedType은 코드 흐름을 반영해 실제로 추론한 타입을 반환합니다. 예를 들어 a가 int로 좁혀진 뒤 b = a + 1에서 b의 계산 타입은 int입니다. getDeclaredType은 소스에 명시한 타입을 반환합니다. def foo(a: int | str)에서 매개변수 a의 선언 타입은 int | str입니다. getExpectedType은 주변 문맥이 요구하는 타입을 반환합니다. foo(4)에서 인자의 기대 타입은 int | str입니다.

타입 결과는 JSON으로 직렬화하는 태그형 객체입니다. 각 객체에는 종류를 나타내는 숫자, ID, 플래그 비트필드가 있고, BuiltIn, Function, Class, Union, Module, TypeVar, Overloaded, Synthesized, TypeReference 등 타입을 표현합니다. TypeReference는 한 응답 안에서 중복되거나 재귀적인 타입 그래프를 가리킵니다. ID는 응답 그래프 안에서 참조를 연결하는 용도이며, 응답 간 안정적인 식별자로 쓰면 안 됩니다. InvalidHandle 값 -1은 유효한 객체 ID가 아니라 사용할 수 없는 핸들을 표시하는 센티널입니다.

위치 규칙과 오류 처리

TSP의 줄 번호와 문자 오프셋은 0부터 시작하며, 문자 오프셋은 UTF-16 코드 단위 기준입니다. 내부적으로 다른 인코딩을 쓰는 서버는 프로토콜 경계에서 위치를 변환해야 합니다. 잘못 변환하면 클라이언트가 다른 구문 노드의 타입을 요청할 수 있습니다. 오류에는 잘못된 인자에 대한 -32602 InvalidParams, 내부 실패에 대한 -32603 InternalError도 사용합니다. 오류 메시지는 로그에서 실패한 요청을 식별할 정도로 구체적으로 쓰되 로컬 비밀 정보나 구현 내부 상태를 노출하면 안 됩니다.

Hacker News 반응

  • @anonymous908213 — Python만 지원하는데 범용 프로토콜 이름 공간을 차지하는 것은 이상합니다. LSP를 만든 회사라면 실제로 범용인 LSP와 더 비교됩니다.
    • @wolttam — Pylance 프로젝트 아래에 이름이 지정돼 있습니다. 발표도 아니고 마크다운 파일로 보입니다. 게시물 제목을 조정할 만합니다.
  • @Tarean — LSP 확장으로 기계가 읽을 수 있는 타입 조회를 추가해 Pylance와 Pyright를 분리하려는 것으로 보입니다. 결과 타입 형식은 Python에 매우 특화돼 있어 다른 언어에는 쓰이지 않을 것 같습니다.
  • @tobz1000 — LSP만으로 Python 타입 질의 기능을 충분히 제공하지 못하는 건가요?
    • @Wilfred — 일반적인 LSP 응답은 사람이 IDE에서 읽도록 문자열이나 Markdown으로 표현합니다. TSP는 클라이언트가 구조적으로 분석할 타입 표현을 제공하는 듯합니다. 다만 LSP 확장이나 새 버전 기능으로 만들지 않은 점은 아쉽습니다.
    • @zamalek — workspace/symbol을 쓸 수도 있지만 선택 기능입니다.
    • @ocamoss — workspace/symbol의 페이로드 형식은 지정되지 않았으므로 서버가 타입을 문자열로 돌려줄 수 있습니다. Python에는 닫힌 형태로 표현하기 어려운 오버로드 callable 같은 타입도 있어 문자열 파싱이 까다롭습니다.
  • @nextaccountic — TSP는 Basedpyright 같은 다른 언어 서버에 타입 검사를 맡기면서 Pylance 전용 기능을 유지하려는 Microsoft의 시도라고 비판합니다. Basedpyright는 프로토콜을 지원하지 않으며 다른 서버에도 지원하지 말라고 권합니다.
  • @coindef — 프로토콜의 가치는 JSON-RPC 인터페이스를 하나 더 만드는 데 있지 않고 타입 정보를 문자열에서 구조화된 데이터로 바꾸는 데 있습니다. 편집기와 리팩터링 도구, 에이전트가 타입을 직접 소비할 수 있습니다. 다만 전체 타입 그래프를 매번 계산하면 지연이 커질 수 있으므로 요청 단위의 계산과 능력 협상이 중요합니다. 다른 언어로 확장하려면 언어 공통 타입 구조와 언어별 타입 구성을 분리해야 합니다.

원문: Microsoft Pylance 문서 / 번역·요약: Trawling