Pendulum이 Python에서 가장 저주받은 + 연산자를 쓸 수밖에 없었던 이유

Why Pendulum had to write the most cursed "+" operator in all of Python

Pendulum은 DST 인식 산술과 드롭인 호환성이라는 두 가지 약속을 지키기 위해, + 연산자에서 호출 스택을 검사해 astimezone 호출인지에 따라 다른 결과를 내는 편법을 썼다. 이 방식은 600배 느리고, dateutil이나 PyPy 같은 다른 호출자에서는 버그를 일으킨다. 저자는 astimezone에서 표준 datetime을 사용하는 수정안을 제안해 병합됐지만, 근본 문제는 서브클래싱 자체에 있다고 지적한다.

Pendulum은 진퇴양난에 빠졌다. 변환 로직을 바꿀 수도 없고(표준 라이브러리 소관), + 오버로드를 제거할 수도 없고(DST 인식 산술 약속 위반), 서브클래스를 그만둘 수도 없다(드롭인 호환성 약속 위반).
  1. shoo

    음. 콜 스택을 내성적으로 살펴보고 제어 흐름을 바꾸는 건 상당히 고약한 짓이지.

    한 프로젝트에서 콜 스택 내성(introspection)을 활용해 괜찮은 용도를 찾았던 게 기억나. 제어 흐름을 바꾸기 위해서가 아니라 로깅을 개선하기 위해서였어. open_db_connection 유틸리티 함수 안에서, 코드베이스의 어느 부분이 연결을 열었는지 설명하는 정보성 이름을 자동 생성하는 데 썼지:

    예를 들어: some_backend_process: main -> ... > grandparent -> parent -> open_db_connection

    우리는 콜 스택 정보를 사용해 호출 지점을 설명하는 문자열 "some_backend_process grandparent.parent"를 생성했고, 이걸 연결을 설정할 때 postgres에 application_name [1]으로 전달할 수 있었어. 그러면 데이터베이스 쪽에서 런타임에 문제가 되는 쿼리가 있을 때, 백엔드 코드베이스의 어느 부분이 책임이 있는지 파악할 단서가 훨씬 많아졌지 [2].

    콜 스택을 들여다보지 않고도 이에 상응하는 걸 할 방법이 있긴 해. 호출자가 설명적이고 고유한 이름을 명시적으로 전달하도록 요구하는 거지. 하지만 그건 특히 일부 개발자들이 기존 코드 덩어리를 복사-붙여넣기하고 같은 이름을 여기저기 재사용하는 경향이 있어서 실수하기 더 쉬워져.

    [1] https://www.postgresql.org/docs/current/libpq-connect.html#L...

    [2] https://www.postgresql.org/docs/current/monitoring-stats.htm...

  2. jvolkman

    내가 이걸 한 수 더 올려보자면, 호출자에 따라 `sys.platform` 값을 바꾸는 내 핵이 있어: https://github.com/jvolkman/rules_pycross/blob/main/pycross/...

    이건 바이너리 휠을 크로스 컴파일할 때 사용돼 (예를 들어 리눅스 빌드 호스트에서 macos 휠을 빌드하거나 그 반대의 경우).

  3. ariebovenberg

    저자입니다. 제가 유지보수하는 datetime 라이브러리인 `whenever`와 Pendulum을 비교하다가 이 문제를 발견했어요. 이 핵을 제거하는 패치를 보냈지만, 이 글은 근본적인 설계를 같은 방식으로 패치할 수 없는 이유에 관한 것입니다. 질문 있으면 기꺼이 답변할게요.

이 날의 다른 글

2026-10-07