[Final] PEP 263 - Defining Python Source Code Encodings
원문 링크: PEP 263 - Defining Python Source Code Encodings
상태: Final 유형: Standards Track 작성일: 06-Jun-2001
PEP 263: Python 소스 코드 인코딩 정의
개요
이 PEP는 Python 소스 파일의 인코딩을 선언하는 문법을 도입할 것을 제안합니다. 이 인코딩 정보는 Python 파서가 파일을 해석하는 데 사용됩니다. 특히, 소스 코드 내 유니코드 리터럴(Unicode literals)의 해석을 개선하고, 유니코드 지원 편집기에서 UTF-8 등으로 유니코드 리터럴을 직접 작성할 수 있도록 합니다.
문제점
Python 2.1에서는 유니코드 리터럴이 라틴-1(Latin-1) 기반 인코딩인 “unicode-escape”를 사용해서만 작성될 수 있었습니다. 이로 인해 많은 아시아 국가와 같이 라틴-1이 아닌 로케일에서 작업하는 Python 사용자들에게는 개발 환경이 불편했습니다. 프로그래머는 자신이 선호하는 인코딩으로 8비트 문자열을 작성할 수 있었지만, 유니코드 리터럴의 경우 “unicode-escape” 인코딩에 묶여 있었습니다.
제안된 해결책
이 PEP는 파일 상단에 특별한 주석을 사용하여 파일별로 Python 소스 코드 인코딩을 명확하게 선언하고 변경할 수 있도록 제안합니다. 이 인코딩 선언을 Python이 인식하도록 하기 위해서는 Python 소스 코드 데이터 처리 방식에 대한 여러 개념적 변경이 필요합니다.
인코딩 정의
다른 인코딩 힌트가 주어지지 않으면 Python은 기본적으로 ASCII를 표준 인코딩으로 사용합니다.
소스 코드 인코딩을 정의하려면, 파일의 첫 번째 또는 두 번째 줄에 다음과 같은 “매직 주석(magic comment)”을 배치해야 합니다.
# coding=<encoding name>
또는 널리 사용되는 편집기가 인식하는 형식:
#!/usr/bin/python
# -*- coding: <encoding name> -*-
또는:
#!/usr/bin/python
# vim: set fileencoding=<encoding name> :
더 정확히는, 첫 번째 또는 두 번째 줄이 다음 정규 표현식과 일치해야 합니다.
^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)
이 표현식의 첫 번째 그룹은 인코딩 이름으로 해석됩니다. Python이 해당 인코딩을 알 수 없는 경우, 컴파일 중 오류가 발생합니다. 인코딩 선언을 포함하는 줄에는 Python 문장이 없어야 합니다. 첫 번째 줄이 정규 표현식과 일치하면 두 번째 줄은 무시됩니다.
Windows와 같이 유니코드 BOM(Byte Order Mark)을 유니코드 파일 시작 부분에 추가하는 플랫폼을 돕기 위해, UTF-8 서명 \xef\xbb\xbf
는 매직 인코딩 주석이 없더라도 ‘utf-8’ 인코딩으로 해석됩니다. 소스 파일이 UTF-8 BOM 서명과 매직 인코딩 주석을 모두 사용하는 경우, 주석에 허용되는 유일한 인코딩은 ‘utf-8’입니다. 다른 인코딩은 오류를 발생시킵니다.
예시
소스 파일 상단에서 인코딩을 정의하는 다양한 스타일의 예시입니다.
인터프리터 바이너리 및 Emacs 스타일 파일 인코딩 주석 사용:
#!/usr/bin/python
# -*- coding: latin-1 -*-
import os, sys
...
``````python
#!/usr/bin/python
# -*- coding: iso-8859-15 -*-
import os, sys
...
``````python
#!/usr/bin/python
# -*- coding: ascii -*-
import os, sys
...
인터프리터 줄 없이 일반 텍스트 사용:
# This Python file uses the following encoding: utf-8
import os, sys
...
텍스트 편집기는 파일 인코딩을 정의하는 다른 방식을 가질 수 있습니다. 예를 들어:
#!/usr/local/bin/python
# coding: latin-1
import os, sys
...
인코딩 주석이 없는 경우, Python 파서는 ASCII 텍스트로 가정합니다.
#!/usr/local/bin/python
import os, sys
...
작동하지 않는 인코딩 주석 예시:
“coding:”
접두사 누락:
#!/usr/local/bin/python
# latin-1
import os, sys
...
인코딩 주석이 1번 또는 2번 줄에 없음:
#!/usr/local/bin/python
#
# -*- coding: latin-1 -*-
import os, sys
...
지원되지 않는 인코딩:
#!/usr/local/bin/python
# -*- coding: utf-42 -*-
import os, sys
...
개념
이 PEP는 매직 주석 사용을 가능하게 하기 위해 구현되어야 하는 다음 개념들을 기반으로 합니다.
- 전체 Python 소스 파일은 단일 인코딩을 사용해야 합니다. 다르게 인코딩된 데이터의 포함은 허용되지 않으며 Python 소스 코드 컴파일 중 디코딩 오류를 유발합니다.
- 위에서 언급된 방식으로 첫 두 줄을 처리할 수 있는 모든 인코딩은 소스 코드 인코딩으로 허용됩니다. 여기에는 ASCII 호환 인코딩과 Shift_JIS와 같은 특정 멀티바이트(multi-byte) 인코딩이 포함됩니다. UTF-16처럼 모든 문자에 두 개 이상의 바이트를 사용하는 인코딩은 포함되지 않습니다. 이는 토크나이저(tokenizer)의 인코딩 감지 알고리즘을 단순하게 유지하기 위함입니다.
- 이스케이프 시퀀스(escape sequence) 처리는 현재와 같이 작동해야 하지만, 가능한 모든 소스 코드 인코딩에서 작동해야 합니다. 즉, 표준 문자열 리터럴(8비트 및 유니코드 모두)은 이스케이프 시퀀스 확장을 따르고, 원시 문자열 리터럴(raw string literals)은 매우 작은 부분 집합의 이스케이프 시퀀스만 확장합니다.
Python의 토크나이저/컴파일러 조합은 다음과 같이 작동하도록 업데이트되어야 합니다.
- 파일을 읽습니다.
- 고정된 파일별 인코딩을 가정하여 유니코드로 디코딩합니다.
- UTF-8 바이트 문자열로 변환합니다.
- UTF-8 내용을 토큰화(tokenize)합니다.
- 주어진 유니코드 데이터에서 유니코드 객체를 생성하고, UTF-8 데이터를 주어진 파일 인코딩을 사용하여 8비트 문자열 데이터로 다시 인코딩하여 유니코드 리터럴 데이터에서 문자열 객체를 생성하며 컴파일합니다.
Python 식별자는 인코딩의 ASCII 부분 집합으로 제한되므로, 4단계 이후 추가 변환이 필요하지 않습니다.
구현 단계
현재 인코딩 선언 없이 문자열 리터럴에 비-ASCII 문자를 사용하는 기존 코드와의 하위 호환성(backwards-compatibility)을 위해, 구현은 두 단계로 도입됩니다.
1단계: 인코딩 선언이 없는 경우 내부적으로 “iso-8859-1” 선언으로 처리하여 문자열 리터럴 및 주석에 비-ASCII 문자를 허용합니다. 이를 통해 임의의 바이트 문자열이 처리의 2단계와 5단계 사이에서 올바르게 왕복(round-trip)되고, 비-ASCII 바이트를 포함하는 유니코드 리터럴에 대해 Python 2.2와의 호환성을 제공합니다. 부적절하게 인코딩된 입력 파일당 한 번씩, 입력에서 비-ASCII 바이트가 발견되면 경고가 발행됩니다.
2단계: 경고를 제거하고 기본 인코딩을 “ascii”로 변경합니다.
내장 compile()
API는 유니코드를 입력으로 받도록 개선될 것입니다. 8비트 문자열 입력은 위에서 설명한 인코딩 감지를 위한 표준 절차를 따릅니다. 코딩 선언이 있는 유니코드 문자열이 compile()
에 전달되면, SyntaxError
가 발생합니다.
SUZUKI Hisao가 패치를 작업 중입니다 (자세한 내용은 참조). 1단계만 구현하는 패치는에서 사용할 수 있습니다.
위 1단계와 2단계의 구현은 Python 2.3에서 완료되었지만, 기본 인코딩을 “ascii”로 변경하는 부분은 제외되었습니다. 기본 인코딩은 Python 2.5 버전에서 “ascii”로 설정되었습니다.
범위
이 PEP는 현재 (다소) 정의되지 않은 소스 코드 인코딩 상황에서 보다 견고하고 이식 가능한 정의로의 업그레이드 경로를 제공하는 것을 목표로 합니다.
참고 자료
- 1단계 구현:
https://bugs.python.org/issue526840
- 2단계 구현:
https://bugs.python.org/issue534304
역사
- 1.10 이상: CVS 기록 참조
- 1.8:
coding
정규 표현식에.
추가. - 1.7: 1단계 구현에 경고 추가. 라틴-1 기본 인코딩을 인터프리터의 기본 인코딩으로 대체.
compile()
에 대한 조정 추가. - 1.4 - 1.6: 사소한 조정.
- 1.3: Martin v. Loewis의 의견 반영: UTF-8 BOM 마크 감지, Emacs 스타일 매직 주석, 구현의 2단계 접근 방식.
저작권
이 문서는 퍼블릭 도메인(public domain)에 공개되었습니다.
PEP 263: Python 소스 코드 인코딩 정의
개요
이 PEP는 Python 소스 파일의 인코딩을 선언하는 문법을 도입할 것을 제안하며, 이 인코딩 정보는 Python 파서가 파일을 해석하는 데 사용됩니다. 이를 통해 소스 코드 내 유니코드 리터럴(Unicode literals)의 해석이 개선되고, 유니코드 지원 편집기에서 UTF-8 등으로 유니코드 리터럴을 직접 작성할 수 있게 됩니다.
문제점
Python 2.1에서는 유니코드 리터럴이 라틴-1(Latin-1) 기반 인코딩인 “unicode-escape”만을 사용하여 작성 가능했습니다. 이는 아시아 국가와 같이 라틴-1이 아닌 지역의 사용자들에게 불편함을 주었으며, 프로그래머는 8비트 문자열에 선호하는 인코딩을 사용할 수 있었지만 유니코드 리터럴에는 “unicode-escape” 인코딩에 묶여 있었습니다.
제안된 해결책
이 PEP는 파일 상단에 특별한 주석(magic comment)을 사용하여 Python 소스 코드의 인코딩을 파일별로 명시적으로 선언하고 변경할 수 있도록 제안합니다. 이러한 인코딩 선언을 Python이 인식하도록 하기 위해서는 소스 코드 데이터 처리 방식에 대한 여러 개념적 변경이 필요합니다.
인코딩 정의
인코딩 힌트가 주어지지 않으면 Python은 기본적으로 ASCII를 표준 인코딩으로 간주합니다. 소스 코드 인코딩을 정의하기 위해, 파일의 첫 번째 또는 두 번째 줄에 다음과 같은 “매직 주석”을 배치해야 합니다.
# coding=<encoding name>
또는 널리 사용되는 편집기가 인식하는 형식:
#!/usr/bin/python
# -*- coding: <encoding name> -*-
또는:
#!/usr/bin/python
# vim: set fileencoding=<encoding name> :
더 정확하게는, 첫 번째 또는 두 번째 줄이 정규 표현식 ^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)
과 일치해야 하며, 이 표현식의 첫 번째 그룹이 인코딩 이름으로 해석됩니다. Python이 해당 인코딩을 알 수 없는 경우 컴파일 오류가 발생합니다. 인코딩 선언 줄에는 다른 Python 문장이 없어야 합니다.
또한, Windows와 같이 유니코드 BOM(Byte Order Mark)을 사용하는 플랫폼을 위해, UTF-8 서명 \xef\xbb\xbf
는 매직 인코딩 주석이 없더라도 ‘utf-8’ 인코딩으로 해석됩니다. 만약 소스 파일이 UTF-8 BOM과 매직 인코딩 주석을 모두 사용한다면, 주석에 허용되는 유일한 인코딩은 ‘utf-8’이며, 다른 인코딩은 오류를 유발합니다.
예시
인코딩을 정의하는 다양한 스타일의 예시는 다음과 같습니다.
- 인터프리터 바이너리 및 Emacs 스타일 주석:
#!/usr/bin/python
과# -*- coding: latin-1 -*-
- 인터프리터 줄 없이 일반 텍스트:
# This Python file uses the following encoding: utf-8
- 편집기별 정의:
#!/usr/local/bin/python
과# coding: latin-1
인코딩 주석이 없는 경우, Python 파서는 ASCII 텍스트로 가정합니다. 작동하지 않는 예시로는 “coding:”
접두사 누락, 주석이 1번이나 2번 줄에 없음, 지원되지 않는 인코딩 사용 등이 있습니다.
개념
이 PEP의 핵심 개념은 다음과 같습니다.
- 전체 Python 소스 파일은 단일 인코딩을 사용해야 하며, 다른 인코딩 데이터의 혼용은 허용되지 않습니다.
- 첫 두 줄을 처리할 수 있는 모든 인코딩은 소스 코드 인코딩으로 허용되며, 여기에는 ASCII 호환 및 Shift_JIS와 같은 멀티바이트 인코딩이 포함됩니다. UTF-16과 같이 모든 문자에 두 개 이상의 바이트를 사용하는 인코딩은 토크나이저의 인코딩 감지 알고리즘 단순화를 위해 제외됩니다.
- 이스케이프 시퀀스 처리는 모든 소스 코드 인코딩에서 계속 작동하며, 표준 문자열 리터럴은 이스케이프 시퀀스 확장을 따르고 원시 문자열 리터럴은 작은 부분 집합만 확장합니다.
Python의 토크나이저/컴파일러는 다음 절차에 따라 작동하도록 업데이트됩니다.
- 파일 읽기.
- 고정된 파일별 인코딩을 가정하여 유니코드로 디코딩.
- UTF-8 바이트 문자열로 변환.
- UTF-8 내용 토큰화.
- 주어진 유니코드 데이터에서 유니코드 객체를 생성하고, UTF-8 데이터를 파일 인코딩을 사용하여 8비트 문자열 데이터로 다시 인코딩하여 유니코드 리터럴 데이터에서 문자열 객체를 생성하며 컴파일.
Python 식별자는 인코딩의 ASCII 부분 집합으로 제한되므로, 4단계 이후 추가 변환이 필요하지 않습니다.
구현 단계
이 제안의 구현은 기존 코드와의 하위 호환성을 위해 두 단계로 진행되었습니다.
1단계: 인코딩 선언이 없는 경우 내부적으로 “iso-8859-1”로 처리하여 문자열 리터럴 및 주석에 비-ASCII 문자를 허용했습니다. 이 단계에서는 비-ASCII 바이트가 발견될 경우 경고가 발생했습니다.
2단계: 경고를 제거하고 기본 인코딩을 “ascii”로 변경했습니다.
내장 compile()
API는 유니코드를 입력으로 받도록 개선되었으며, 코딩 선언이 있는 유니코드 문자열이 compile()
에 전달되면 SyntaxError
가 발생합니다.
1단계와 2단계의 구현은 Python 2.3에서 완료되었지만, 기본 인코딩을 “ascii”로 변경하는 것은 Python 2.5에서 이루어졌습니다.
범위
이 PEP는 현재 불분명한 소스 코드 인코딩 상황을 보다 견고하고 이식 가능한 정의로 전환하기 위한 업그레이드 경로를 제공하는 것을 목표로 합니다.
⚠️ 알림: 이 문서는 AI를 활용하여 번역되었으며, 기술적 정확성을 보장하지 않습니다. 정확한 내용은 반드시 원문을 확인하시기 바랍니다.
Comments