2P by GN⁺ | ★ favorite | 댓글 1개
  • CPython은 PEP 703 수용 방침으로 GIL을 선택 사항으로 만드는 장기 전환을 시작했고, 대형 기업 기여와 핵심 개발자 논의가 맞물리며 Python 런타임의 큰 변화가 가시화됨
  • GIL 제거는 기본 동작을 즉시 바꾸지 않고, 3.13/3.14 목표의 실험 빌드에서 공식 지원 대안 빌드와 장기 기본값 전환으로 이어지는 단계적 계획임
  • LPython과 Cython 3.0은 Python 코드를 더 빠르게 실행하려는 흐름을 보여주며, 특히 수치 연산 병목과 일반 Python 문법 지원이 핵심 축임
  • Pydantic 2는 2.1 릴리스와 FastAPI 지원으로 도입 장벽이 낮아졌지만, 초기 메이저 버전 특성상 개인 프로젝트에서 먼저 검증하는 접근이 제안됨
  • PEP 387의 soft deprecation 추가로 optparse·getopt는 제거 일정 없이 유지되지만 새 코드에는 argparse·typer·click 같은 대안을 쓰는 편이 적절함

CPython의 GIL 선택화 계획

  • PEP 703은 CPython에서 Global Interpreter Lock을 선택 사항으로 만드는 제안이며, 세부 사항을 정리한 뒤 수용될 예정으로 발표됨
  • Meta는 PEP 703이 수용되면 nogil CPPython 반영을 위해 엔지니어 3년분의 지원을 약속할 수 있다고 밝힘
  • 전환은 세 단계로 진행됨
    • 단기: GIL 없는 Python의 지원되지 않는 실험 버전을 일반 버전과 병행 공개하며 목표는 3.13/3.14임
    • 중기: no-GIL 버전을 공식 지원하되, GIL이 있는 Python의 대안 빌드로 유지함
    • 장기: no-GIL이 기본값이 됨
  • 기본값 전환은 커뮤니티가 충분한 지원을 보인 뒤에만 진행되며, 여러 해가 걸릴 수 있음
  • 핵심 개발자들은 no-GIL 프로젝트의 ROI가 나쁘다고 판단되면 장기 전환 전에 결정을 되돌리고 중단할 수 있음
  • no-GIL 빌드에서 GIL을 사용하는 C 확장을 하나라도 가져오면 자동으로 GIL 모드로 되돌아가도록 설계됨
    • Python 2에서 Python 3으로 넘어갈 때처럼 호환되지 않는 코드가 깨지는 상황은 아님
    • no-GIL 호환 확장은 기존 인터프리터에서도 동작함
  • Python 코드 자체는 두 빌드에서 자연스럽게 동작해야 하며, GIL이 있는 경우 스레드는 단일 코어로 제한됨
  • 별도 빌드를 두는 주된 이유는 알 수 없는 위험을 관리하기 위해서임
    • no-GIL이 문제를 일으킬 것으로 기대되지는 않지만, 큰 프로젝트에서는 확신하기 어려움
    • ABI 호환성은 까다롭고, 새 확장은 no-GIL을 위해 명시적으로 컴파일되어야 함

LPython: 수치 연산 병목을 겨냥한 새 컴파일러

  • LPython은 Python 코드를 LLVM, C, C++, WASM 대상으로 변환하는 BSD 3 라이선스 컴파일러임
  • 전체 프로그램도 컴파일할 수 있지만, 주된 목표는 numba·Cython처럼 수치 연산 병목을 빠르게 만드는 데 있음
  • 벤치마크는 유망하며, Ahead-of-Time과 Just-in-Time 사이를 전환할 수 있음
  • 사용하려면 머신에 전체 컴파일 체인이 설치되어 있어야 함
  • LPython은 원시 Python 코드를 선호하며, 스니펫 안에서 Python 함수를 호출하려면 데코레이터로 명시해야 함
    • 대부분의 사용자는 매우 특정한 코드 조각에 적용할 가능성이 큼

Pydantic 2와 FastAPI 지원

  • Pydantic 2는 데이터 검증과 스키마 정의에 많이 쓰이며, 새 버전은 더 빠른 성능을 목표로 함
  • 안정 버전은 이전 달에 나왔지만, 안정화된 메이저 버전도 초기에는 다듬을 부분과 제한적인 커뮤니티 지원이 남을 수 있음
  • Pydantic 2.1이 릴리스되면서 초기의 큰 버그들이 제거됨
  • FastAPI가 Pydantic 2 지원을 발표함
    • FastAPI는 Pydantic 사용을 크게 견인하는 프로젝트라서 중요한 이정표임
  • 개인 프로젝트에서 먼저 시도한 뒤, 잘 동작하면 몇 달 뒤 업무 프로젝트로 옮기는 접근이 제안됨

Soft deprecation과 optparse·getopt

  • PEP 387 – Backwards Compatibility Policysoft deprecation 개념이 추가됨
  • soft deprecation은 새 코드에서 더 이상 사용하지 말아야 하는 API에 적용되지만, 기존 코드에서 계속 사용하는 것은 안전한 상태를 뜻함
    • API는 문서화와 테스트가 유지됨
    • 추가 개발이나 개선은 없음
    • 일반적인 hard deprecation과 달리 제거 일정이 전제되지 않음
  • optparsegetopt는 soft-deprecated로 표시됨
    • 두 모듈은 과거 스크립트 인자 파싱의 사실상 표준 역할을 했음
    • 계속 사용할 수는 있지만 새 코드에는 권장되지 않음
  • 표준 라이브러리에서는 argparse가 더 현대적인 대안임
  • 서드파티 대안으로는 typerclick이 있음

Cython 3.0의 일반 Python 문법 지원

  • Cython은 잘 알려진 Python 컴파일러이며, 버전 3을 릴리스함
  • 여러 개선 가운데 순수 Python 지원이 특히 두드러짐
  • 이전에는 일부 기능을 표현하기 위해 Python의 상위 집합 문법을 사용해야 하는 한계가 있었음
  • 릴리스 노트에 따르면 이제 일반 Python 문법으로 모든 Cython 코드와 기능을 표현할 수 있어야 함
  • 기존 Python 코드베이스에도 Cython을 적용해 보는 방식이 가능해짐

PEP 722: 단일 파일 스크립트 의존성 표기

  • PEP 722는 단일 Python 스크립트의 의존성을 주석 문법으로 표기하는 방식을 공식화하려는 제안임
  • 예시는 # Requirements: 아래에 requests, rich 같은 패키지를 나열하는 형태임
  • 핵심 목적은 서드파티 도구가 이 주석을 파싱할 수 있도록 문법을 공식화하는 데 있음
  • 이 개념은 새롭지 않으며, pip-run은 이미 주석으로 의존성을 적은 스크립트를 실행할 수 있음
    • 필요한 패키지는 임시 virtual env에 설치됨
    • 실행 후 환경은 삭제됨
    • JavaScript 생태계의 npx와 비슷한 방식임
  • 이 PEP는 Python이나 pip가 해당 기능을 통합한다는 뜻이 아니며, 현재는 문법 공식화에만 초점을 둠

VSCode의 Python 지원 성능 개선

  • VSCode에서 여러 린터를 사용하면 IDE가 느려질 수 있으며, 특히 mypy 명령은 시작이 느리고 VSCode가 데몬 모드를 사용하지 않았음
  • 2023년 7월 릴리스에서 공식 mypy 확장이 새로 제공됨
  • 이 확장은 dmypy 데몬을 사용하며, 속도 향상 덕분에 현재 파일뿐 아니라 전체 코드베이스 검사까지 제공할 수 있음
  • Microsoft의 Python 지원 확장인 pylance는 서드파티 라이브러리 인덱싱 작업을 영속화함
    • 시작이 더 가벼워짐
    • 큰 프로젝트에서는 인덱싱 시간이 줄어 더 빠른 경험을 제공함

터미널에서 실행되는 Paint

  • textual-paint는 터미널에서 실행되는 Paint 구현임
  • Python 라이브러리 textual을 사용함
  • 반응성이 좋고, Ctrl-Z를 처리함
  • 작업을 저장하려고 할 때 파일 선택기도 제공함

댓글과 토론

Hacker News 의견들
  • 관련 글: Intent to approve PEP 703: making the GIL optional - https://news.ycombinator.com/item?id=36913328 - 2023년 7월, 댓글 488개

  • 예전부터 수십~수백 GB 자료구조를 메모리에 올리고 그 위에 HTTP API를 얹는 서비스를 여러 번 만들었음
    Python으로 빠르게 만든 서비스가 사내에서 인기를 얻어 수백~수천 클라이언트를 상대하게 되면, 요청 처리를 위해 더 많은 스레드를 던질 수 있도록 Java로 다시 쓰는 일이 많았음
    뭔가 놓쳤을 수도 있지만 Python에서 멀티스레드 성능을 끌어내는 방법을 찾지 못했고, 그래서 no-GIL은 흥미로워 보임

    • 이미 어려운 부분은 해냈으니 HTTP API를 다시 쓰기 전에 먼저 이런 최적화를 고려하겠음
      여러 프로세스에는 gunicorn [1]을 쓰면 코드를 크게 건드리지 않고 앱을 여러 프로세스에서 실행할 수 있음. CPU 코어 수만큼 같은 백엔드 앱 인스턴스를 띄우는 것과 같고, 코어당 백엔드 프로세스 하나로 완전히 격리됨
      여러 스레드에는 gunicorn + gevent 워커 [2]를 쓰면 입출력 중심 작업에서 다중 프로세스와 다중 스레드 기능을 바로 얻을 수 있음. 완벽하진 않지만 어떤 상황에서는 아주 잘 동작함
      마지막으로 병목이 CPU라면 어느 정도 메모리 여유가 있다는 뜻이니, 같은 결과를 반환하는 함수나 비싼 입출력을 하는 함수 위에 LRU 캐시나 cachetools [3]를 얹을 수 있음
      [1]: https://www.joelsleppy.com/blog/gunicorn-sync-workers/
      [2]: https://www.joelsleppy.com/blog/gunicorn-async-workers-with-...
      [3]: https://pypi.org/project/cachetools/
    • 답은 multiprocessing임. Python의 multiprocessing 모듈을 쓰거나 WSGI 뒤에 여러 프로세스를 띄우면 됨
      이미 multithreading 모듈로 작성했다면 거의 대체품처럼 쓸 수 있음. 자료구조는 공유 메모리에 놓이고 모든 프로세스가 GIL의 제약 없이 동시에 접근 가능함
      물론 Python 자체가 전반적으로 매우 느리다는 문제를 해결하진 않음. 단지 CPU 코어 하나만 100%로 쓰는 대신 모든 코어를 꽉 채울 수 있게 해줌
    • 대체로 맞는 얘기임. 기술적으로 틀리진 않은 답은 “CPU 병목이 아니라면 애플리케이션은 WSGI로 병렬화해야 하고, 그런 데이터를 전부 메모리에 올리면 안 되니 여러 Python 프로세스가 많은 입출력 중심 요청을 처리해도 문제 없어야 한다”는 식임
      실제로 꽤 많이 해봤고 어느 정도 맞지만, 매우 유연하지 않음. “그건 잘못하고 있는 거다. 웹 서버에 GB 단위 메모리가 필요하면 안 된다. 작업 큐 같은 걸 배워라”가 답인 아키텍처나 패턴은 싫음
      항상 틀린 건 아니지만, 그런 “안티패턴”을 걱정하기엔 너무 이른 경우가 정말 많음
    • 여러 프로세스 사이에서 쉽게 공유하기 어려운 큰 단일 자료구조가 있는 상황으로 보임. 그래도 multiprocessing을 쓸 수 없나 싶음
      자료구조를 파일에 매핑하고 여러 프로세스에서 mmap하는 방법도 있고, 거대한 중첩 딕셔너리 대신 전체를 데이터베이스로 감싸는 방법도 있음
      하지만 상상 가능한 대안들보다 멀티스레딩이 훨씬 덜 고통스러워 보임. 자료구조가 잘 맞는다면 멀티스레딩만 추가해도 현재 하드웨어에서 큰 추가 작업 없이 10배 이상 개선될 수 있음
    • 입출력 중심처럼 들리지만 CPU 중심이라고 했으니, 그런 경우 Python은 작업에 맞는 도구가 아니라고 봄. 다만 multiprocessing으로 버틸 수는 있을 듯함
  • 개발 중인 프로젝트였을 때는 HN의 합의가 “정말 대단한 작업이고 운영위원회가 채택하지 않을 것 같아 아쉽다”에 가까웠던 느낌임
    이제 채택하니 모두가 싫어하는 것처럼 보임

    • 영원한 추의 왕복
      위험을 감수하지 않으면 프로젝트가 정체됐다고 비난받고, 위험을 감수하면 무모하다고 비난받음
      새 기능을 채택하지 않으면 언어가 낡고 무의미해져 간다며 “X가 없어서 못 쓰겠다”는 댓글이 쏟아짐
      호환성을 깨면 안정성이 필요한 사용자를 신경 쓰지 않는다는 무리가 몰려옴. 이 스레드에도 “Python treadmill”이라는 표현이 이미 있음
      게다가 대부분 기여도 하지 않고 돈 한 푼 낸 적 없는 오픈소스 프로젝트에 벌어지는 일임
    • 아마 서로 다른 시점에 서로 다른 집단이 목소리를 내는 것일 가능성이 큼. 처음에 열광하던 사람들이 갑자기 태도를 바꿔 이제 싫어한다고 보긴 어려움
    • 실패할 가능성이 높아 보일 때는 단점을 무시하고 장점에 집중하기가 더 쉬움. 예상 밖으로 실제 채택이 일어나자 이제 현실적으로 양쪽을 더 따져봐야 하는 상황이 됨
    • 인터넷에 사람이 한 명만 있는 게 아닌 것 같음
    • 언어 설계 애호가들이 Python에 이런 하위 호환성 파괴를 밀어 넣는 것 같음. 정작 그들은 언어의 사용자가 아니기 때문임
      끝없는 비호환성 변화로 코드가 깨지는 사람들과는 다른 집단임
      그래도 최소한 일거리는 생김
  • 요약: GIL 없는 Python이 본격화되고, LPython이라는 새 Python 컴파일러가 있으며, Pydantic 2가 쓸 만해지고 있음
    PEP 387은 “Soft Deprecation”을 정의하고 getoptoptparse가 soft deprecated 됨
    Cython 3.0은 더 나은 순수 Python 지원과 함께 출시됐고, PEP 722는 단일 파일 스크립트의 의존성 명세를 다룸
    Python VSCode 지원은 더 빨라졌고, 터미널에서 그림 그리기도 포함됨

  • no-GIL에 아직 안심이 되진 않음. “또 다른 Python 2→3 상황은 원치 않는다”고 하지만, 그 시나리오를 어떻게 피할지에 대한 구체안은 거의 없어 보임
    스레드 안전 코드 작성 문서, 경쟁 상태를 잡는 린트 도구 제안, 인기 C 라이브러리와의 논의, 상위 패키지를 위한 전용 지원 채널, 지금은 동작하지만 방치된 긴 꼬리의 확장 모듈을 어떻게 할지 등이 필요함

    • 크고 명백한 차이는 GIL 대 no-GIL 관련 일이 백그라운드에서 일어나며, 평균적인 Python 개발자는 원하면 그냥 무시할 수 있다는 것임
      인터프리터가 no-GIL에 동의하지 않은 C 확장을 감지하면 GIL 버전을 제공하게 됨
      이는 Python 2에서 3으로의 전환과는 매우 다름. 그때는 관심이 전혀 없는 사람까지도 Python 3을 쓰려면 반드시 코드를 바꿔야 했음
    • 예전에 PHP로 밥벌이를 했는데, Python 2가 해 질 녘까지 사라지는 동안 PHP는 5.2에서 5.3, 다시 5.6에서 7.0으로 두 번의 큰 마이그레이션을 끝냈음
      두 언어의 대비가 놀라웠음. PHP는 업그레이드할 이유를 충분히 제공했고, 호환성을 깨는 변경은 최소화했으며, 예전 코드가 계속 동작하도록 쉽게 shim을 붙일 방법도 자주 함께 제공했음
      no-GIL이 Python에 들어가길 정말 바라지만, 한편으로는 2에서 3으로의 전환에서 어떤 교훈을 얻었는지 걱정됨. 이번에는 Python 팀에 더 효과적인 계획이 있을까
    • no-GIL은 가져온 모든 라이브러리가 지원할 때만 켜질 거라고 생각했음. 즉 라이브러리가 no-GIL 준비 완료로 표시되어 있어야 하고, 아니면 import가 예외를 던지는 방식임
      지금 구현이 정확히 어떤지는 모르지만 꽤 합리적으로 들림. no-GIL 호환 코드는 코어 라이브러리에서 시작해 점차 확장될 것임
      레거시 라이브러리를 쓰면 GIL 모드로 되돌아가야 할 뿐임. no-GIL을 지원하는 라이브러리는 GIL 모드에서도 100% 동작해야 하므로 Python 2→3 전환 상황이 반복될 거라고 보진 않음
    • 방치된 확장 모듈의 긴 꼬리 문제는, GIL을 계속 남겨둘 계획이라고 하니 아마 수년 동안 그대로 동작할 것 같음
      걱정은 일부 라이브러리가 GIL 지원을 그냥 버리는 경우인데, 그 역시 그렇게 되지 않길 기대함
    • no-GIL 빌드에서 GIL을 쓰는 C 확장 하나라도 가져오면 자동으로 GIL로 되돌아가도록 설계됐다고 함. 그래서 호환되지 않는 코드가 깨지는 2→3 상황은 아님
      이 정도면 충분해 보이는데, 내가 뭘 놓치고 있나 싶음
  • 제목은 “GIL removed”라고 하지만, 글에는 “앞으로 몇 년 안에 Python의 GIL이 제거될 것”이라고 되어 있음
    글이 맞고 GIL은 아직 제거되지 않았으며, 앞으로 제거할 계획이 있다는 뜻으로 이해함. 아니라면 바로잡아 주면 좋겠음

    • 아직 제거되지 않았음. PEP 703이 승인됐고 no-GIL로 가는 경로가 생겼음
      no-GIL 버전은 3.13 또는 3.14부터 실험 버전으로 제공될 예정임
      https://peps.python.org/pep-0703/
      https://discuss.python.org/t/a-steering-council-notice-about...
    • 결국 잘 풀리면 나중에 GIL 제거로 이어질 수 있는 개발 계획을 시작하기로 아마 결정할 거라는 발표가 있었음
      그 계획이 PEP 703이고, 사실적 근거는 “PEP 703을 받아들일 의향이 있지만, 수락 세부 사항은 아직 작업 중”이라는 문장임
    • 맞음. 이번에는 GIL이 확실히 제거될 거라는 느낌을 몇 단어로 전달하려 했음. 하지만 프랑스인이라 더 나은 표현을 찾지 못했음
      “GIL will be removed”가 가장 가까웠지만 너무 길고, 과거에도 제거하겠다는 약속만 있었고 결국 안 됐던 때들처럼 들림
      그래서 예언적 완료 시제가 최선의 타협이었음. 거의 확실함을 주장하면서도 짧고, 최악의 경우에도 본문이 모호함을 제거해 줌
      게다가 이번 주 HN 첫 페이지에 관련 뉴스가 올라왔으니 많은 사람이 맥락을 알고 있었음
    • 제목에 과거 시제를 쓴 건 도를 넘은 클릭베이트
  • 바로 이걸 기다려 왔음. no-GIL을 할 수 있게 해주고, 개발자인 내가 선택하게 해주면 됨
    물론 문제가 있겠지만 그 사실을 의식하고 있고, no-GIL의 이점을 분석해 보면 특정 사용 사례에서는 훨씬 이득이 큼
    중요한 사례 중 하나는 운영체제 문맥 밖의 스레딩임. Cortex M0의 두 코어를 모두 쓰고 싶다면 어떻게 할 건가. 프로세스가 없으니 multiprocessing은 도움이 안 됨. 락이 필요하면 주어진 플랫폼에서 직접 구현할 수 있음
    두 번째는 CPU 코어 수가 점점 늘고 있다는 점임. multiprocessing을 쓰면 프로그램이 예상보다 훨씬 복잡해짐. 운영체제와 CPU가 더 나은 도구를 이미 제공하는데 왜 메시지 전달과 공유 메모리를 해야 하나 싶음. 프로세스 ID도 더럽힘
    모든 애플리케이션을 Python으로 만든다고 상상하면, 여러 코어를 쓰기 위해 수십만 개의 개별 프로세스가 생길 것임. 이 문제는 대체로 Python에 특유한 탓에, Python이 더 나았을 애플리케이션도 결국 다른 언어로 만들게 됨
    거의 무엇이든 “그냥 Python 쓰세요”라고 말할 수 있는 세상을 원함. 새 프로그래머에게 원하는 결과를 얻으려면 좋아하는 언어를 버리고 다른 언어를 쓰라고 하면 혁신이 즉시 죽음. 아이디어를 만드는 대신 불필요하다고 보는 언어를 배우는 데 시간을 쓰게 됨

    • “개발자가 선택하게 해달라”는 말에서, no-GIL을 유일한 옵션으로 밀어붙이는 마지막 단계가 큰 문제임
      선택 가능한 no-GIL은 괜찮지만 시간 낭비에 가깝고, 기본값으로 만드는 건 나쁨
      Cortex M0의 두 코어를 모두 쓰고 싶다면, Python에서 성능이 필요한 문제의 해법은 Numpy처럼 C 확장을 작성하는 것임. Python은 성능 지향 언어가 아님
      GIL은 병렬성의 복잡성을 생각하지 않고 코드를 쓰게 해줌
  • pip-run 같은 도구가 주석에 적힌 의존성을 보고 스크립트를 실행할 수 있고, 패키지는 임시 가상 환경에 설치됐다가 실행 뒤 삭제된다고 함. JS 세계에서 예전 npx가 하던 방식과 비슷함
    그런데 이게 효율적인가 싶음. 패키지를 내려받고 설치한 뒤 몇 초 후 삭제하는 건 귀중한 SSD 셀을 낭비함

    • 안타깝게도 효율성을 모르거나 신경 쓰지 않는 개발자가 엄청 많음. 큰 자원 소비가 있는 명령도 아무 생각 없이 실행하고, 그런 일이 벌어진다는 생각조차 못 하는 경우가 있음
      얼마 전 함께 일하던 개발자에게 왜 어떤 내용을 한 번 찾아보려고 매번 수십 MB짜리 PDF를 검색해 내려받고 열었다가 닫으면 삭제하느냐고 물었는데, 그 개념을 전혀 이해하지 못한 듯했음
      나는 프로젝트 문서를 모아두고 대부분 계속 열어두는 편이라 그게 보통이라고 생각했지만, 다른 사람들은 매번 온라인에서 검색하고 브라우저를 닫았다가 필요할 때마다 다시 여는 듯함
      공개적으로도 최근 Docker와, 더 관련 있게는 PyPI가 대역폭 사용량을 걱정하기 시작했음: https://news.ycombinator.com/item?id=24262757 https://news.ycombinator.com/item?id=27205586
  • optparse를 빼면 전부 어제 첫 페이지에 있던 내용임. 이제 optparse가 deprecated 됐음
    스레딩을 위해 확장 모듈을 감사하는 것 말고도 일이 더 늘어난 셈임. Python treadmill에서의 삶은 참 좋음

    • 기억이 맞다면 optparse는 3.5쯤에서 제거될 예정이었지만 반발이 컸음
      문서에는 3.2부터 폐기 경고가 있었음
      오랫동안 “그냥 argparse를 쓰세요” 상태였고, 이번에는 실제 코드 경고를 “추가”한 것뿐임
    • argparse를 좋아해 보려 했지만 너무 복잡함. 쓸 때마다 항상 문서를 다시 읽어야 함
      getopt에는 나름의 잔혹한 단순함이 있음
  • Python 커뮤니티는 왜 Python 2에서 Python 3으로 넘어갈 때 GIL을 제거하지 않았을까

    • 2→3 마이그레이션 당시에는 병렬성이 오늘날만큼 중요하게 여겨지지 않았기 때문임
    • 그걸 할 만큼 똑똑하고 동기 있는 사람이 몇 년 전에서야 나타났기 때문임