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 라이브러리는 이 경우 텍스트를 잘라 내며, 다른 라이브러리에서도 비슷한 문제가 나타날 수 있습니다.

참고 문헌