PEP 686 – UTF-8 모드를 기본값으로 만들기
- Author:
- Inada Naoki <songofacandy at gmail.com>
- Discussions-To:
- Discourse thread
- Status:
- Final
- Type:
- Standards Track
- Created:
- 18-Mar-2022
- Python-Version:
- 3.15
- Post-History:
- 18-Mar-2022, 31-Mar-2022
- Resolution:
- Discourse message
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
이 PEP는 UTF-8 모드를 기본으로 활성화할 것을 제안합니다.
이 변경으로 Python은 파일, stdio, 파이프의 기본 인코딩으로 일관되게 UTF-8을 사용합니다.
동기
UTF-8은 사실상의 표준 텍스트 인코딩이 되었습니다.
- Python 소스 파일의 기본 인코딩은 UTF-8입니다.
- JSON, TOML, YAML은 UTF-8을 사용합니다.
- Visual Studio Code와 Windows 메모장을 비롯한 대부분의 텍스트 편집기는 기본적으로 UTF-8을 사용합니다.
- 인터넷의 대부분의 웹 사이트와 텍스트 데이터는 UTF-8을 사용합니다.
- 그리고 Node.js, Go, Rust, Java를 비롯한 다른 많은 인기 프로그래밍 언어도 기본적으로 UTF-8을 사용합니다.
기본 인코딩을 UTF-8로 변경하면 Python이 이들과 더 쉽게 상호 운용할 수 있습니다.
또한 Unix를 사용하는 많은 Python 개발자는 기본 인코딩이 플랫폼에 따라 다르다는 사실을 잊곤 합니다. 이들은 UTF-8로 인코딩된 텍스트 파일(예: JSON, TOML, Markdown, Python 소스 파일)을 읽을 때 encoding="utf-8"을 지정하는 것을 빠뜨립니다. 일관되지 않은 기본 인코딩은 많은 버그를 일으킵니다.
명세
UTF-8 모드를 기본으로 활성화
Python은 Python 3.15부터 UTF-8 모드를 기본으로 활성화합니다.
사용자는 PYTHONUTF8=0 또는 -X utf8=0을 설정하여 여전히 UTF-8 모드를 비활성화할 수 있습니다.
locale.getencoding()
UTF-8 모드는 locale.getpreferredencoding(False)에 영향을 주므로, UTF-8 모드와 관계없이 로케일 인코딩을 얻을 수 있는 API가 필요합니다.
이 목적을 위해 locale.getencoding()이 추가됩니다. 이 함수도 로케일 인코딩을 반환하지만, UTF-8 모드는 무시합니다.
warn_default_encoding 옵션이 지정되면 locale.getpreferredencoding()은 open()과 마찬가지로 EncodingWarning을 발생시킵니다(PEP 597도 참고하십시오).
이 API는 Python 3.11에서 추가되었습니다.
encoding="locale" 옵션 수정
PEP 597은 TextIOWrapper에 encoding="locale" 옵션을 추가했습니다. 이 옵션은 로케일 인코딩을 명시적으로 지정하는 데 사용됩니다. 이 옵션이 지정되면 TextIOWrapper는 기본 텍스트 인코딩과 관계없이 로케일 인코딩을 사용해야 합니다.
하지만 현재 TextIOWrapper는 UTF-8 모드에서 encoding="locale"이 지정되더라도 "UTF-8"을 사용합니다. 이 동작은 PEP 597의 동기와 일치하지 않습니다. 이는 Python이 기본 텍스트 인코딩을 변경할 때 UTF-8 모드를 기본값으로 만들 것을 예상하지 못했기 때문입니다.
이 불일치는 UTF-8 모드를 기본값으로 만들기 전에 수정되어야 합니다. TextIOWrapper는 UTF-8 모드에서도 encoding="locale"이 전달되면 로케일 인코딩을 사용해야 합니다.
이 문제는 Python 3.11에서 수정되었습니다.
하위 호환성
대부분의 Unix 시스템은 UTF-8 로케일을 사용하며, Python은 로케일이 C 또는 POSIX일 때 UTF-8 모드를 활성화합니다. 따라서 이 변경은 주로 Windows 사용자에게 영향을 줍니다.
Python 프로그램이 기본 인코딩에 의존하는 경우, 이 변경으로 인해 UnicodeError, 문자 깨짐(mojibake), 심지어는 소리 없는 데이터 손상까지 발생할 수 있습니다. 따라서 이 변경은 널리 알려야 합니다.
이 하위 호환성 문제를 해결하기 위한 지침은 다음과 같습니다:
- UTF-8 모드를 비활성화하십시오.
EncodingWarning(PEP 597)을 사용하여 UTF-8 모드가 영향을 미치는 모든 곳을 찾으십시오.encoding옵션을 생략했다면encoding="utf-8"또는encoding="locale"을 사용하는 것을 고려하십시오.locale.getpreferredencoding()을 사용한다면"utf-8"또는locale.getencoding()을 사용하는 것을 고려하십시오.
- UTF-8 모드에서 애플리케이션을 테스트하십시오.
선행 사례
- Ruby는 Ruby 3.0(2020)에서 Windows의 기본
external_encoding을 UTF-8로 변경했습니다. - Java는 JDK 18(2022)에서 기본 텍스트 인코딩을 UTF-8로 변경했습니다.
Ruby와 Java 모두 하위 호환성을 위한 옵션을 제공합니다. 두 언어 모두 기본 인코딩 사용에 대해 Python의 PEP 597의 EncodingWarning과 같은 경고를 제공하지 않습니다.
거부된 대안
암시적 인코딩 폐지
기본 인코딩 사용을 폐지하는 방안이 검토되었습니다.
하지만 기본 인코딩이 ASCII 텍스트만 읽거나 쓰는 데 사용되는 경우가 많습니다. 또한 이러한 경고는 Unix에서 실행되는, 크로스 플랫폼이 아닌 애플리케이션에는 유용하지 않습니다.
따라서 사용자에게 모든 곳에서 encoding을 지정하도록 강제하는 것은 너무 큰 부담입니다. DeprecationWarning을 대량으로 발생시키면 사용자가 경고를 무시하게 될 것입니다.
PEP 387은 하위 호환되지 않는 변경에 대해 경고를 추가할 것을 요구합니다. 하지만 DeprecationWarning을 사용할 것까지 요구하지는 않습니다. 따라서 선택적인 EncodingWarning을 사용하는 것은 PEP 387을 위반하지 않습니다.
Java 역시 JEP 400에서 이 아이디어를 거부했습니다.
PIPE에 PYTHONIOENCODING 사용
하위 호환성 문제를 완화하기 위해 subprocess 모듈에서 PYTHONIOENCODING을 PIPE의 기본 인코딩으로 사용하는 방안이 검토되었습니다.
이 방안을 따르면 사용자는 UTF-8 모드에서도 subprocess.Popen(text=True)에 레거시 인코딩을 사용할 수 있습니다.
하지만 이 아이디어는 “기본 인코딩”을 복잡하게 만듭니다. 그리고 이 아이디어는 하위 호환성도 깨뜨립니다.
따라서 이 아이디어는 기각되었습니다. 사용자는 text=True를 encoding="utf-8" 또는 encoding="locale"로 바꿀 때까지 UTF-8 모드를 비활성화할 수 있습니다.
교육 방법
신규 사용자에게는 이 변경으로 가르쳐야 할 내용이 줄어듭니다. 사용자는 처음 1년 동안 텍스트 인코딩에 대해 배울 필요가 없습니다. UTF-8이 아닌 텍스트 파일을 사용해야 할 때 배우면 됩니다.
기존 사용자는 Backward Compatibility 절을 참조하십시오.
참고 문헌
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.