Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 383 – 시스템 문자 인터페이스에서 디코딩할 수 없는 바이트

Author:
Martin von Löwis <martin at v.loewis.de>
Status:
Final
Type:
Standards Track
Created:
22-Apr-2009
Python-Version:
3.1
Post-History:


Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

POSIX에서는 파일 이름, 환경 변수 및 명령줄 인자를 문자 데이터로 정의하지만, C API에서는 이러한 데이터가 특정 인코딩을 따르는지 여부와 관계없이 임의의 바이트를 전달할 수 있습니다. 이 PEP에서는 원래 바이트 문자열을 재생성할 수 있도록 바이트를 문자 문자열에 삽입하여 이러한 불규칙성을 처리하는 방법을 제안합니다.

근거

C char 형식은 문자 데이터와 바이트를 모두 나타내는 데 일반적으로 사용되는 데이터 형식입니다. 일부 POSIX 인터페이스는 문자 데이터에서 작동하는 것으로 지정되고 널리 이해되지만, 시스템 호출 인터페이스는 이러한 데이터의 인코딩에 대해 어떠한 가정도 하지 않으며 데이터를 있는 그대로 전달합니다. Python 3에서는 문자 문자열이 유니코드 기반 내부 표현을 사용하므로, C 인터페이스가 인코딩을 무시할 수 있는 것과 같은 방식으로 바이트 문자열의 인코딩을 무시하기가 어렵습니다.

반면 Microsoft Windows NT는 Unix의 원래 설계상 한계를 수정하고, 유니코드 기반 API를 제공하여(하위 호환성을 위해 C char 기반 API도 유지함) 이러한 데이터(파일 이름, 환경 변수, 명령줄 인자)가 실제로 문자 데이터임을 시스템 인터페이스에서 명시했습니다.

Python 3에서 제안된 한 가지 해결책은 바이트 지향 API와 문자 지향 API라는 두 가지 API 집합을 제공하는 것입니다. 이때 문자 지향 API는 모든 데이터를 정확하게 표현할 수 없도록 제한됩니다. 안타깝게도 Windows에서는 상황이 정확히 반대가 됩니다. 바이트 지향 인터페이스는 모든 데이터를 표현할 수 없으며, 문자 지향 API만 그렇게 할 수 있습니다. 결과적으로 모든 사용자 데이터를 플랫폼 간 방식으로 지원하려는 라이브러리와 애플리케이션은 Python 2.x에서 끝없는 문제를 일으켰던 방식 그대로 바이트와 문자가 뒤섞인 형태를 받아들여야 합니다.

이 PEP를 사용하면 이러한 데이터를 문자로 균일하게 처리할 수 있습니다. 이러한 균일성은 특정 인코딩 알고리즘을 사용하여 달성되며, 이는 동일한 인코딩을 사용하는 경우에만 POSIX 시스템에서 데이터를 다시 바이트로 변환할 수 있음을 의미합니다.

이러한 문자열을 균일하게 처리할 수 있으면 애플리케이션 작성자가 운영 체제에 특화된 세부 사항을 추상화할 수 있으며, 한 API가 작동했을 다른 API가 실패할 위험도 줄어듭니다.

사양

Windows에서 Python은 와이드 문자 API를 사용하여 문자 지향 API에 접근하므로, 환경 데이터를 Python str 객체로 직접 변환할 수 있습니다 (PEP 277).

POSIX 시스템에서 Python은 현재 로캘의 인코딩을 적용하여 바이트 데이터를 유니코드로 변환하며, 디코딩할 수 없는 문자에서는 실패합니다. 이 PEP를 사용하면 128 이상인 디코딩할 수 없는 바이트가 단독 서로게이트 코드 U+DC80..U+DCFF로 표현됩니다. 128 미만인 바이트는 예외를 발생시킵니다. 아래 논의를 참조하십시오.

디코딩할 수 없는 바이트를 변환하기 위해 이러한 서로게이트를 생성하는 새로운 오류 처리기 (PEP 293)인 “surrogateescape”가 도입됩니다. 인코딩할 때 오류 처리기는 서로게이트를 해당 바이트로 다시 변환합니다. 이 오류 처리기는 파일 이름, 명령줄 인자 또는 환경 변수를 받거나 생성하는 모든 API에서 사용됩니다.

오류 처리기 인터페이스는 인코딩 오류 처리기가 유니코드 문자열을 반환하여 다시 인코딩하도록 하는 것뿐만 아니라 바이트 문자열을 즉시 반환할 수 있도록 확장됩니다(아래 논의도 참조하십시오).

Python 3.0에 이미 존재하는 바이트 지향 인터페이스는 이 사양의 영향을 받지 않습니다. 이러한 인터페이스는 개선되지도, 더 이상 사용되지 않도록 지정되지도 않습니다.

파일 이름을 처리하는 외부 라이브러리(예: GUI 파일 선택기)도 PEP에 따라 파일 이름을 인코딩해야 합니다.

논의

이 surrogateescape 인코딩은 Markus Kuhn이 UTF-8b [3]라고 부른 아이디어에 기반합니다.

이 인터페이스는 디코딩할 수 없는 바이트에 균일한 API를 제공하지만, 선택된 표현은 surrogateescape 오류 처리기를 사용해서도 데이터를 다시 바이트로 변환하는 경우에만 “작동한다”는 한계가 있습니다. 로캘의 인코딩과 (기본값인) strict 오류 처리기를 사용하여 데이터를 인코딩하면 예외가 발생하고, UTF-8을 사용하여 인코딩하면 무의미한 데이터가 생성됩니다.

다른 출처에서 얻은 데이터는 이 PEP가 생성하는 데이터와 충돌할 수 있습니다. 그러한 충돌을 처리하는 것은 이 PEP의 범위를 벗어납니다.

이 PEP는 문자열 안에 바이트를 “밀반입”할 가능성을 허용합니다. 대상 시스템에서 경로 이름 구분자와 같이 문자로 해석될 때 보안상 중요한 바이트라면, 이는 보안 위험이 될 수 있습니다. 이러한 이유로 이 PEP는 128 미만의 바이트를 밀반입하는 것을 거부합니다. 대상 시스템이 EBCDIC를 사용하는 경우, 이렇게 밀반입된 바이트는 여전히 보안 위험이 될 수 있으며, 예를 들어 대괄호나 백슬래시의 밀반입을 허용할 수 있습니다. Python은 현재 EBCDIC를 지원하지 않으므로, 실제로는 이것이 문제가 되지 않을 것입니다. Python을 EBCDIC 시스템으로 이식하는 사람이라면 오류 처리기를 조정하거나, 보안 위험을 해결할 다른 방법을 고안하고자 할 수 있습니다.

ASCII와 호환되지 않는 인코딩은 이 명세에서 지원되지 않습니다. ASCII 범위 안의 바이트가 디코딩에 실패하면 예외가 발생합니다. 그러한 인코딩은 로케일 문자 집합으로 사용되어서는 안 된다는 것이 널리 합의되어 있습니다.

대부분의 애플리케이션은 시스템 인터페이스에서 받은 데이터를 결국 같은 시스템 인터페이스로 다시 전달한다고 가정합니다. 예를 들어, os.listdir()를 호출하는 애플리케이션은 결과 문자열을 os.stat()나 open() 같은 API에 다시 전달할 가능성이 높으며, 이 API들은 해당 문자열을 원래의 바이트 표현으로 다시 인코딩합니다. 원래의 바이트 문자열을 처리해야 하는 애플리케이션은 파일 시스템 인코딩으로 문자열을 인코딩하면서 “surrogateescape”를 오류 처리기 이름으로 전달함으로써 그 바이트 문자열을 얻을 수 있습니다. 예를 들어, 바이트를 받아들이고 반환한다는 점을 제외하면 os.listdir처럼 동작하는 함수는 다음과 같이 작성할 수 있습니다.:

def listdir_b(dirname):
    fse = sys.getfilesystemencoding()
    dirname = dirname.decode(fse, "surrogateescape")
    for fn in os.listdir(dirname):
        # fn is now a str object
        yield fn.encode(fse, "surrogateescape")

이 PEP에서 제안하는 인코드 오류 처리기 인터페이스의 확장은 ‘surrogateescape’ 오류 처리기를 구현하는 데 필요합니다. 대체 유니코드로부터 생성할 수 없는 필수 바이트 시퀀스가 존재하기 때문입니다. 하지만 현재 인코드 오류 처리기 인터페이스는 원본 문자열의 인코딩 불가능한 유니코드 대신 대체 유니코드를 제공하도록 요구합니다. 그런 다음 그 대체 유니코드를 즉시 인코딩합니다. 여기서 제안하는 ‘surrogateescape’와 같은 일부 오류 처리기의 경우, 인코더가 원하는 바이트를 만들어낼 유니코드를 강제로 계산하게 하는 것보다 오류 처리기가 사전 인코딩된 대체 바이트 문자열을 제공하는 편이 더 단순하고 효율적입니다.

몇 가지 대안적 접근 방식이 제안되었습니다:

  • 임베디드 바이트를 지원하는 새로운 문자열 서브클래스를 만드는 방법
  • NUL 문자로 이스케이프하거나 드물게 쓰이는 문자에 매핑하는 등, 서로 다른 이스케이프 방식을 사용하는 방법.

이러한 제안 중, 각 바이트 XX를 시퀀스 U+0000 U+00XX로 이스케이프하는 접근 방식은 UTF-8로 인코딩할 때 UTF-8 시퀀스에 NUL 바이트가 도입된다는 단점이 있습니다. 그 결과, C 라이브러리는 문자열이 계속됨에도 불구하고 이를 문자열 종료로 해석할 수 있습니다. 특히 gtk 라이브러리는 이 경우 텍스트를 잘라내며, 다른 라이브러리들도 비슷한 문제를 보일 수 있습니다.

참고 자료