PEP 497 – 하위 호환성을 위한 표준 메커니즘
- Author:
- Ed Schofield <ed at pythoncharmers.com>
- PEP-Delegate:
- Brett Cannon <brett at python.org>
- Status:
- Rejected
- Type:
- Process
- Created:
- 04-Aug-2015
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
거부 통지
운영 위원회는 이 제안의 __past__측면이 잠재적 이점에 비해 너무 복잡하다고 결정했습니다. 더 강력한 하위 호환성 요구 사항이라는 다른 측면은 PEP 387에서 다루어야 합니다.
범위
이 PEP는 PEP 5, 236, 387을 보완하며, 유사한 목표를 공유합니다.
이 PEP는 PEP 5, “언어 발전 지침”을 지원하기 위한 추가 호환성 메커니즘의 필요성을 설명합니다. PEP 236, “__future__로 돌아가기”는 PEP 5를 지원하기 위한 전방 호환성 메커니즘을 도입했지만, 하위 호환성을 위한 새로운 메커니즘은 해당 PEP의 범위를 벗어난다고 언급했습니다. 관련 PEP(현재 진행 중)는 이러한 하위 호환성 메커니즘을 도입합니다.
PEP 5, “언어 발전 지침”은 “이 PEP [PEP 5]는 ‘하위 호환 파서의 동적 로딩’과 같은 다른 호환성 전략을 대체하거나 배제하지 않습니다.”라고 언급합니다.
배경
다음은 PEP 236에서 인용한 내용입니다: “때때로 Python은 핵심 언어 구성 요소의 공표된 의미를 호환되지 않게 변경하거나, 어떤 방식으로든 우연한(구현에 종속적인) 동작을 변경합니다. 이러한 변경은 결코 변덕스럽게 이루어지는 것이 아니며 장기적으로 언어를 개선하려는 목적에 따라 항상 이루어지지만, 단기적으로는 논란을 일으키고 혼란을 초래합니다. PEP 5, 언어 발전 지침은 이러한 고통을 완화할 방법을 제안하며, 이 PEP [PEP 236]는 이를 지원하는 일부 메커니즘을 도입합니다.”
또한 PEP 236에서 다음과 같이 말합니다. “future_statement의 목적은 최신 릴리스를 적시에 따라가는 사람들의 삶을 더 쉽게 만드는 것입니다. 그렇지 않다고 해서 여러분을 미워하는 것은 아니지만, 여러분의 문제는 해결하기가 훨씬 더 어렵고, 그러한 문제를 가진 누군가는 이를 다루는 PEP를 작성해야 합니다. future_statement는 다른 독자를 대상으로 합니다.”
현재 상황
핵심 언어 구문이나 의미에 호환되지 않는 변경이 이루어질 때, Python은 현재 새로운 구문이나 의미를 적용하는 릴리스가 나올 때까지 전방 호환성을 제공하는 future_statement 메커니즘을 제공하지만, 해당 릴리스 이후에 하위 호환성을 제공하는 이에 상응하는 표준 메커니즘은 제공하지 않습니다.
문제
이러한 비대칭의 결과는 호환성을 깨는 변경과 관련하여 이전의 변경 전 Python 인터프리터 버전이 더 새로운 변경 후 버전보다 더 많은 기능을 제공한다는 것입니다. 이전 인터프리터는 변경 전에 설계된 코드와 새로운 코드를 모두 사용할 수 있지만, 새로운 인터프리터는 변경된 기능을 지원하도록 업그레이드된 코드만 사용할 수 있습니다.
예를 들어, PEP 236이 future_statement 메커니즘을 도입한 직후인 2001년에 PEP 238에서 도입된 나눗셈 연산자의 변경을 살펴보십시오. PEP 238은 Python 2.x 계열에서 “참 나눗셈”을 위한 유용한 전방 호환성 메커니즘 모음을 설명하지만, Python 3.0에서 “참 나눗셈”이 처음 적용된 이후를 위한 하위 호환성 메커니즘은 포함하지 않았습니다. 3.0 이후의 Python 버전은 기존의 “고전적 나눗셈” 의미를 기대하는 코드를 위한 from __past__ import division과 같은 하위 호환성 메커니즘을 제공하지 않는 반면, 3.0 이전의 Python 버전은 “고전적 나눗셈” 코드와 “참 나눗셈”을 기대하는 코드에 대한 전방 호환성을 모두 지원합니다. 이로 인한 또 다른 결과는 실제로 사용되는 다양한 나눗셈 관련 Python 코드를 고려할 때 “가장 호환성이 높은” 인터프리터가 호환성을 깨는 변경이 처음 적용되기 전 버전인 Python 2.7이라는 것입니다.
“내리막 업그레이드”를 가능하게 하는 하위 호환성
이 상황과 대조적으로, 오피스 제품군과 같은 애플리케이션 소프트웨어의 최신 버전은 데이터 파일 형식의 여러 버전을 로드하는 지원 측면에서 이전 버전보다 더 많은 기능을 제공하는 경향이 있습니다. 일반적으로 최신 애플리케이션 버전은 최신 형식이나 이전 형식의 데이터를 모두 투명하게 로드할 수 있으며, 최신 버전은 기본적으로 데이터를 최신 형식으로 저장합니다. 최신 애플리케이션 소프트웨어 버전은 기본적으로 하위 호환성을 갖는 경향이 있습니다. 전방 호환성은 비교적 드뭅니다.
이러한 정책은 최신 애플리케이션 소프트웨어 사용자에게 이전 소프트웨어 사용자보다 유리하게 작용하며, 이전 소프트웨어는 일반적으로 최신 형식의 데이터를 로드할 수 없습니다. 때로는 최신 소프트웨어 애플리케이션 버전의 사용자가 이 옵션을 명시적으로 선택하여 데이터를 이전 버전으로 내보낼 수 있습니다. 이러한 경우 이 기능으로 가능해진 전방 호환성이 완벽할 수도 있고 그렇지 않을 수도 있습니다. 일부 기능이 누락되거나 결과가 다른 방식으로 최적이 아닐 수 있습니다. 따라서 업그레이드는 쉽지만 다운그레이드는 더 어렵습니다.
새롭고 매력적인 기능과 하위 호환성 기능을 결합한 이러한 정책이 여러 사용자에게 미치는 결과는 각 사용자가 자신의 애플리케이션 버전을 업그레이드하도록 자연스러운 압력이 형성된다는 것입니다. 또한 한 사용자가 데이터 파일을 교환하는 다른 사용자가 많을수록 이러한 압력은 더욱 커집니다.
제안 - 1부
이 PEP는 서로 관련된 두 가지 구체적인 제안을 제시합니다. 첫 번째 제안은 다음과 같습니다.
“하위 호환성이 깨지는 기능 도입 절차” 절의 6번째 단계로 PEP 5를 보완하여, 핵심 언어 구문이나 의미론에 호환되지 않는 변경을 적용할 때 변경 사항을 기본적으로 채택한 이후의 향후 Python 버전에서 가능한 경우 하위 호환성 메커니즘을 고려하고 제공하는 것을 Python-dev의 정책으로 선호하고 기대한다는 점을 명시해야 합니다. 이는 새로운 future_statements와 같이 상위 호환성을 위해 제안된 메커니즘에 추가되는 것입니다. 또한 PEP 387, “하위 호환성 정책”(승인되는 경우)에도 동일한 6번째 단계를 추가해야 합니다.
예제
이 PEP를 적용하는 방법의 예로, “진정한 나눗셈” PEP(238)의 최신 개정안이 오늘 제안된다면 불완전한 것으로 간주될 것입니다. PEP 238은 이 제안으로 인해 발생하는 “심각한 하위 호환성 문제”를 지적하고, Abstract 및 API Changes 절에서 상위 호환성을 위한 여러 조치를 설명합니다. 또한 c.l.py에서 제기된 일부 하위 호환성 아이디어를 언급하며, 여기에는 “모듈에서 고전적 나눗셈 의미론을 사용하려면 from __past__ import division을 사용하십시오”가 포함되지만, 제안의 일부로 하위 호환성 계획을 제시하지는 않습니다.
이 PEP가 승인된다면, PEP 238과 같은 제안은 대규모 호환성 영향을 미치므로, 변경 사항이 적용된 이후의 향후 Python 버전 사용자들이 자신의 코드에서 고전적 나눗셈 동작을 쉽게 다시 활성화할 수 있도록 하는 하위 호환성 계획도 함께 제공될 것으로 예상됩니다.
제안 - 2부
두 번째 제안은 다음과 같습니다.
Python은 상위 호환성을 위한__future__모듈 메커니즘과 병행하여 표준 하위 호환성 메커니즘을 제공해야 합니다.
참고로 이 문서에서는 이를 이후부터 “__past__” 메커니즘이라고 부르지만, __future__ 모듈 및 future_statement 메커니즘의 모든 특성을 갖출 필요는 없습니다.
__past__ 메커니즘의 구체적인 형태와 구현은 별도의 PEP에서 다루는 주제입니다(현재 진행 중입니다). 그러나 이 PEP는 __past__ 메커니즘을 __future__에 대해 PEP 296에서 제시한 기준과 유사한 기준을 충족하도록 설계할 것을 권장합니다. 구체적으로는 다음과 같습니다.
- 개별 모듈이 모듈별로 이전 Python 버전의 폐기된 동작을 다시 활성화하도록 지정할 수 있어야 합니다.
__past__메커니즘을 호출하는 사용자 모듈에 대해 Python 3.6 이상과 이전 버전의 마이너 릴리스 모두에서 이전 Python 구문이나 의미론에 대한 하위 호환성을 다시 도입할 수 있을 만큼 유연해야 합니다.- 호출된 구체적인
__past__기능을 알지 못하거나, 하위 호환성을 위한__past__메커니즘이 존재한다는 사실조차 알지 못하는 2.x와 같은 이전 Python 버전에서__past__동작을 호출하도록 수정된 이전 코드를 실행할 수 있어야 합니다.
반례
이러한 기준을 위반하는 __past__ 메커니즘의 일부 구현은 다음과 같습니다.
- 임포트 훅입니다. 이는 일반적으로 모듈별로 작동하지 못하며, 대신 한 모듈 내부에서 임포트되는 모든 새 모듈에 재귀적으로 적용됩니다.
- 이전 버전과 호환되지 않는 Python 3.6의 새로운 구문 또는 새로운 의미론입니다.
- 이전 Python 버전에도 동일한 이름으로 존재하는 함수를 Python 표준 라이브러리의 모듈에 Python 3.6에서 추가하는 것입니다.
이점
Python-dev가 이 제안을 채택함으로써 얻는 이점은, 향후 하위 호환성이 깨지는 변경 사항마다 해당하는 __past__ 기능이 구현되어 있고 향후 Python 버전 사용자가 이를 쉽게 호출할 수 있다면 그러한 변경 사항이 덜 큰 혼란을 일으킬 수 있다는 점입니다. 이는 설계상의 실수를 바로잡기 위해 언어가 더 빠르고 효과적으로 발전하는 데 도움이 될 수 있습니다.
보수적인 사용자에게 주어지는 이점은 분명합니다. 각 모듈에 __past__ 주문을 추가하기만 하면(어쩌면 한 줄만 추가하면) 최신의 호환성이 깨지는 Python 버전을 자신의 코드에서 지원할 수 있으며, 이 작업은 자동화할 수 있습니다. 그러면 인터프리터를 최신 버전으로 업그레이드하고 최신의 새로운 Python 기능을 이용할 수 있습니다.
커뮤니티에 주어지는 이점은, 만 명의 사용자가 패키지 XYZ에 의존하고 있고 패키지 XYZ가 최신 Python 버전을 쉽게 지원할 수 있다면, 그 만 명의 사용자도 패키지 XYZ가 이 작업을 수행하기를 기다리느라 발이 묶이지 않고 최신 Python 버전으로 신속하게 업그레이드할 수 있다는 점입니다.
질문과 답변입니다.
Q1: 이 PEP는 Python이 각 하위 호환성이 깨지는 기능에 대해 가능한 두 가지 의미 체계를 영원히 유지하도록 요구합니까?
A1: 결코 그렇지 않습니다. 레거시 기능은 적절한 시점에, 즉 사용자 기반의 대다수가 더 새로운 Python 버전으로 이전했을 때 여전히 단계적으로 폐기할 수 있습니다. 이 PEP는 호환성을 위한 개발 노력의 중점을 순방향 호환성에 100% 두는 것에서 역방향 호환성에 최소 50% 두는 것으로 전환하자고 제안할 뿐입니다. 하위 호환성은 사용자 기반이 최신 Python 인터프리터 버전을 채택하도록 하는 데 있어 두 개념 중 더 강력한 개념입니다.
대부분의 사용자가 Python 2.1을 훌쩍 넘어 편안하게 이동했기 때문에, 중첩되지 않은 스코프에 대한 하위 호환성을 대부분의 사용자가 신경 쓰지 않게 된 지 오래되었다는 점에 주목하십시오.
Q2: 하지만 Python-dev는 이미 업무가 과중하며 추가적인 복잡성을 구현하고 유지 관리할 여력이 없습니다!
A2: Python-dev는 개발자 커뮤니티에 자신들이 중요하게 여기는 레거시 언어 기능의 하위 호환성을 Python에서 유지 관리하는 데 나서 달라고 요청할 수 있습니다. 커뮤니티가 특정한 오래된 동작에 더 이상 관심을 두지 않으면 Python-dev도 더 이상 관심을 두지 않아도 됩니다.
핵심 개발자의 부담을 줄이기 위해 __past__ 메커니즘을 커뮤니티가 확장할 수 있도록, 예를 들어 표준이면서도 “공인된” PyPI 패키지로 설계할 수도 있습니다.
Q3: 하위 호환성 기능으로 인해 Python에 많은 낡은 잔재와 비대화 및 부담이 생기지 않습니까?
A3: 반드시 그렇지는 않습니다. 첫째, Python의 새로운 호환성 파괴 기능에 대한 제안은 관련 __past__ 기능 구현의 단순성과 유지 관리 용이성을 사전에 일부 기준으로 삼아 평가할 수 있습니다.
둘째, 일부 오래된 기능은 하위 호환성을 제공하기가 간단합니다. Python 3.0 이전의 “고전적 나눗셈” 동작을 생각해 보십시오. python-future 프로젝트에는 future.utils.old_div 함수에 고전적 나눗셈의 호환 가능한 구현이 포함되어 있습니다.
def old_div(a, b):
"""
Equivalent to ``a / b`` on Python 2 without ``from __future__ import
division``.
"""
if isinstance(a, numbers.Integral) and isinstance(b, numbers.Integral):
return a // b
else:
return a / b
이러한 함수를 Python 3.x 버전에 포함하고, 적절한 __past__ 호출 이후 a / b가 나타날 때마다 이를 호출할 수 있는 간단한 메커니즘을 함께 제공하는 것은 부담스럽지 않을 수 있습니다.
Q4: 성능은 어떻게 됩니까? 레거시 기능의 부담으로 인해 최신 Python 버전의 성능이 저하되지 않습니까?
A4: 이는 사례별로 평가할 수 있습니다. 가장 큰 잠재적 우려 사항은 레거시 옵션이 존재하더라도 새로운 기본 동작을 사용할 때의 성능이 부당하게 저하되지 않는다는 점입니다. __past__ 호출의 영향을 받는 경우의 성능은 부차적으로 중요합니다.
Copyright
This document has been placed in the public domain.