PEP 597 – 선택적 EncodingWarning 추가
- Author:
- Inada Naoki <songofacandy at gmail.com>
- Status:
- Final
- Type:
- Standards Track
- Created:
- 05-Jun-2019
- Python-Version:
- 3.10
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain or CC0-1.0, whichever is more permissive 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
새로운 경고 범주인 EncodingWarning을 추가합니다. open()의 encoding인자를 생략하고 기본 로캘별 인코딩을 사용하면 이 경고가 발생합니다.
이 경고는 기본적으로 비활성화되어 있습니다. 새로운 -X warn_default_encoding 명령줄 옵션과 PYTHONWARNDEFAULTENCODING 환경 변수를 사용하여 이 경고를 활성화할 수 있습니다.
encoding에 대한 "locale" 인자 값도 추가합니다. 이 값은 로캘 인코딩을 사용하도록 명시하여 경고를 표시하지 않습니다.
동기
기본 인코딩을 사용하는 것은 흔한 실수입니다.
macOS 또는 Linux를 사용하는 개발자는 기본 인코딩이 항상 UTF-8은 아니라는 점을 잊을 수 있습니다.
예를 들어 setup.py에서 long_description = open("README.md").read()를 사용하는 것은 흔한 실수입니다. UTF-8로 인코딩된 README.md파일에 ASCII가 아닌 문자가 하나라도 포함되어 있으면(예: 이모지, 작성자 이름, 저작권 기호 등) 많은 Windows 사용자가 이러한 패키지를 설치할 수 없습니다.
PyPI에서 가장 많이 다운로드된 4000개 패키지 중 489개는 README에 ASCII가 아닌 문자를 사용하며, 82개는 ASCII가 아닌 파일의 인코딩을 지정하지 않아 UTF-8이 아닌 로캘에서 소스 코드로 설치할 때 실패합니다. [1]
또 다른 예는 logging.basicConfig(filename="log.txt")입니다. 일부 사용자는 기본적으로 UTF-8을 사용할 것으로 예상할 수 있지만, 실제로 사용되는 것은 로캘 인코딩입니다. [2]
Python 전문가조차 기본 인코딩이 UTF-8이라고 가정할 수 있습니다. 이로 인해 Windows에서만 발생하는 버그가 생깁니다. 예를 들어 [3], [4], [5], [6] 을 참조하십시오.
encoding인자를 생략했을 때 경고를 발생시키면 이러한 실수를 찾는 데 도움이 됩니다.
로캘별 인코딩을 명시적으로 사용하는 방법
open(filename)은 어떤 인코딩이 필요한지 명시적으로 나타내지 않습니다.
- ASCII를 가정한다면 버그는 아니지만, 특히 Latin-1이 아닌 로캘 인코딩을 사용할 때 Windows에서 성능 저하가 발생할 수 있습니다.
- UTF-8을 가정한다면 버그일 수도 있고 플랫폼별 스크립트일 수도 있습니다.
- 로캘 인코딩을 가정한다면 동작은 예상대로 이루어집니다(단, 향후 Python 버전에서 기본값을 변경하면 달라질 수 있습니다).
이러한 관점에서 open(filename)은 가독성이 높은 코드가 아닙니다.
encoding=locale.getpreferredencoding(False)를 사용하여 로캘 인코딩을 명시적으로 지정할 수 있지만, 너무 길고 잘못 사용하기 쉽습니다(예: 인자로 False를 전달하는 것을 잊을 수 있습니다).
이 PEP는 로캘 인코딩을 명시적으로 지정하는 방법을 제공합니다.
기본 인코딩을 UTF-8로 변경할 준비
UTF-8이 사실상의 표준 텍스트 인코딩이 되었으므로, 향후 파일을 열 때 UTF-8을 기본값으로 사용할 수 있습니다.
그러나 이러한 변경은 많은 애플리케이션과 라이브러리에 영향을 미칩니다. encoding인자를 생략하는 모든 곳에서 DeprecationWarning을 발생시키기 시작하면 경고가 지나치게 많아지고 처리하기도 어려울 것입니다.
이 PEP는 기본 인코딩 변경을 제안하지는 않지만, 다음과 같은 방식으로 그러한 변경을 가능하게 하는 데 도움이 됩니다:
- 기본적으로
DeprecationWarning을 발생시키기 시작하기 전에 라이브러리에서 생략되는encoding인자의 수를 줄입니다. - 사용자가
encoding="locale"를 전달하여 현재 경고와 향후 추가되는DeprecationWarning을 억제할 수 있도록 하고, 이후 Python 버전에서 기본값이 변경되더라도 일관된 동작을 유지하여 Python 버전 >=3.10을 지원합니다.
사양
EncodingWarning
Warning의 서브클래스로 새로운 EncodingWarning 경고 클래스를 추가합니다. encoding 인자가 생략되고 기본 로케일별 인코딩이 사용될 때 발생합니다.
경고를 활성화하는 옵션
-X warn_default_encoding 옵션과 PYTHONWARNDEFAULTENCODING 환경 변수를 추가합니다. 이들은 EncodingWarning을 활성화하는 데 사용됩니다.
sys.flags.warn_default_encoding도 추가됩니다. EncodingWarning이 활성화되면 이 플래그는 참입니다.
플래그가 설정되면 io.TextIOWrapper(), open() 및 이를 사용하는 다른 모듈은 encoding 인자가 생략될 때 EncodingWarning을 발생시킵니다.
EncodingWarning은 Warning의 서브클래스이므로 DeprecationWarning과 달리 기본적으로 표시됩니다(warn_default_encoding 플래그가 설정된 경우).
encoding="locale"
io.TextIOWrapper는 "locale"을 encoding에 유효한 인자로 허용합니다. 현재의 encoding=None과 같은 의미이지만, encoding="locale"을 지정하면 io.TextIOWrapper는 EncodingWarning을 발생시키지 않습니다.
io.text_encoding()
io.text_encoding()은 encoding=None 매개변수가 있고 이를 io.TextIOWrapper()나 open()에 전달하는 함수를 위한 도우미입니다.
순수 Python 구현은 다음과 같은 형태입니다.:
def text_encoding(encoding, stacklevel=1):
"""A helper function to choose the text encoding.
When *encoding* is not None, just return it.
Otherwise, return the default text encoding (i.e. "locale").
This function emits an EncodingWarning if *encoding* is None and
sys.flags.warn_default_encoding is true.
This function can be used in APIs with an encoding=None parameter
that pass it to TextIOWrapper or open.
However, please consider using encoding="utf-8" for new APIs.
"""
if encoding is None:
if sys.flags.warn_default_encoding:
import warnings
warnings.warn(
"'encoding' argument not specified.",
EncodingWarning, stacklevel + 2)
encoding = "locale"
return encoding
예를 들어 pathlib.Path.read_text()은 다음과 같이 사용할 수 있습니다.
def read_text(self, encoding=None, errors=None):
encoding = io.text_encoding(encoding)
with self.open(mode='r', encoding=encoding, errors=errors) as f:
return f.read()
io.text_encoding()을 사용하면 read_text()자체가 아니라 read_text()의 호출자에게 EncodingWarning이 발생합니다.
영향을 받는 표준 라이브러리 모듈
이 변경으로 많은 표준 라이브러리 모듈이 영향을 받습니다.
encoding=None 매개변수를 허용하는 대부분의 API는 이전 절에 작성된 대로 io.text_encoding()을 사용합니다.
로케일 인코딩을 기본 인코딩으로 사용하는 것이 합리적인 경우에는 encoding="locale"를 대신 사용합니다. 예를 들어 subprocess 모듈은 파이프의 기본 인코딩으로 로케일 인코딩을 사용합니다.
많은 테스트에서 ASCII 텍스트 파일을 읽기 위해 encoding을 지정하지 않은 채 open()을 사용합니다. 이러한 테스트는 encoding="ascii"를 사용하도록 다시 작성해야 합니다.
근거
선택적 활성화 경고
DeprecationWarning은 기본적으로 억제되지만, encoding 인자가 생략될 때마다 DeprecationWarning을 항상 발생시키면 경고가 너무 많아집니다.
경고가 너무 많으면 개발자가 DeprecationWarning을 무시하게 될 수 있습니다.
“locale”은 코덱 별칭이 아닙니다.
로캘은 런타임에 변경될 수 있으므로 “locale”을 코덱 별칭으로 추가하지 않습니다.
또한, TextIOWrapper는 encoding=None일 때 os.device_encoding()을 확인합니다. 이 동작은 코덱에서 구현할 수 없습니다.
하위 호환성
새 경고는 기본적으로 발생하지 않으므로 이 PEP는 100% 하위 호환됩니다.
상위 호환성
"locale"을 encoding에 대한 인자로 전달하는 것은 상위 호환되지 않습니다. 이를 사용하는 코드는 Python 3.10보다 오래된 버전에서 작동하지 않으며, 대신 LookupError: unknown encoding: locale를 발생시킵니다.
개발자가 Python 3.9 지원을 중단할 수 있을 때까지 EncodingWarning은 누락된 encoding="utf-8" 인자를 찾는 데에만 사용할 수 있습니다.
이것을 가르치는 방법
신규 사용자의 경우
EncodingWarning은 플랫폼 간 코드를 작성하는 데 사용되므로 신규 사용자에게 이를 가르칠 필요는 없습니다.
텍스트 파일에는 UTF-8을 사용하고 파일을 열 때 encoding="utf-8"를 사용하도록 권장하면 됩니다.
숙련된 사용자의 경우
UTF-8로 인코딩된 텍스트 파일을 읽기 위해 open(filename)을 사용하는 것은 흔한 실수입니다. UTF-8이 기본 인코딩이 아니므로 Windows에서는 작동하지 않을 수 있습니다.
-X warn_default_encoding 또는 PYTHONWARNDEFAULTENCODING=1을 사용하여 이러한 유형의 실수를 찾을 수 있습니다.
로캘 인코딩으로 인코딩된 텍스트 파일을 열 때 encoding인자를 생략하는 것은 버그가 아니지만, encoding="locale"를 사용하는 것이 더 명시적이므로 Python 3.10 이상에서는 이를 권장합니다.
참조 구현
논의
최신 토론 스레드는 다음과 같습니다: https://mail.python.org/archives/list/python-dev@python.org/thread/SFYUP2TWD5JZ5KDLVSTZ44GWKVY4YNCV/
- 린터에서 이를 구현하지 않는 이유는 무엇입니까?
encoding="locale"와io.text_encoding()는 Python에서 구현해야 합니다.open()이나TextIOWrapper()를 감싸는 함수의 모든 호출자를 찾기는 어렵습니다(io.text_encoding()섹션 참조).
- 많은 개발자는 이 옵션을 사용하지 않을 것입니다.
참고 자료
Copyright
This document is placed in the public domain or under the CC0-1.0-Universal license, whichever is more permissive.