PEP 278 – 유니버설 개행 지원
- Author:
- Jack Jansen <jack at cwi.nl>
- Status:
- Final
- Type:
- Standards Track
- Created:
- 14-Jan-2002
- Python-Version:
- 2.3
- Post-History:
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
이 PEP는 Python이 플랫폼의 고유 형식이 아닌 개행 형식을 가진 파일에 대해 I/O를 지원할 수 있는 방법을 논의하며, 이를 통해 각 플랫폼의 Python이 CR(매킨토시), LF(유닉스), 또는 CR LF(윈도우) 줄 끝을 가진 파일을 읽고 임포트할 수 있도록 합니다.
현재 플랫폼의 표준과 일치하지 않는 줄 끝을 가진 파일을 접하는 일이 점점 더 흔해지고 있습니다: 인터넷을 통해 다운로드된 파일, 다른 플랫폼에 원격으로 마운트된 파일 시스템, Mac과 유닉스 줄 끝이라는 이중 표준을 가진 Mac OS X 등이 그 예입니다.
편집기와 컴파일러 같은 많은 도구들이 이미 이를 매끄럽게 처리하고 있으므로, Python도 그렇게 한다면 좋을 것입니다.
명세
유니버설 개행 지원은 기본적으로 활성화되어 있지만, Python의 configure 과정에서 비활성화할 수 있습니다.
유니버설 개행을 지원하는 Python에서는 이 기능이 모든 import 문과 execfile() 호출에 대해 자동으로 활성화됩니다. eval()이나 exec에 대한 특별한 지원은 없습니다.
유니버설 개행을 지원하는 Python에서 open()의 mode 매개변수는 “U”일 수도 있으며, 이는 “유니버설 개행 해석을 사용하여 텍스트 파일로서 입력용으로 열기”를 의미합니다. “rb”와의 대칭성을 위해 mode “rU”도 허용됩니다. mode “U”는 “+”와 같은 다른 mode 플래그와 결합할 수 없습니다. 입력 파일의 어떤 줄 끝이든 Python에서는 '\n'으로 보이므로, 유니버설 개행을 처리하기 위해 변경해야 할 다른 코드는 거의 없습니다.
개행 변환은 read(), readline(), readlines() 등 데이터를 읽는 모든 호출에서 일어납니다.
다른 개행 규칙을 사용하는 파일로의 출력에 대한 특별한 지원은 없으므로, mode “wU”도 허용되지 않습니다.
유니버설 개행 모드로 열린 파일 객체는 파일에서 사용된 개행 규칙을 반영하는 새로운 속성 “newlines”를 갖게 됩니다. 이 속성의 값은 None(아직 개행을 읽지 않음), "\r", "\n", "\r\n" 중 하나이거나, 발견된 모든 개행 유형을 포함하는 튜플입니다.
근거
유니버설 개행 지원은 Python이 아닌 C로 구현됩니다. 이는 다른 형식의 개행 규칙을 가진 파일도 임포트할 수 있게 하여, Python Lib 디렉터리를 원격 파일 시스템 연결을 통해 공유하거나, Mac OS X에서 MacPython과 Unix-Python 사이에 공유할 수 있게 하기 위함입니다. 이것이 실현 가능하려면 유니버설 개행 규칙이 성능에 미치는 영향이 합리적으로 작아야 하는데, 이는 Python으로 구현하는 것이 선택지가 될 수 없음을 의미합니다. Python 구현은 모든 import를 지연시킬 것이기 때문입니다. 또한 Visual C++와 다른 윈도우 도구들이 흔쾌히 만들어내는, 여러 개행 규칙이 섞인 파일들 때문에, 파일에서 사용된 개행을 빠르게 검사하는 방식(플랫폼 고유의 개행이 발견되면 임포트를 C 코드로 넘기는 방식)은 통하지 않을 것입니다. 마지막으로, C 구현은 (Python 소스 모듈을 여는) 트레이스백 등도 쉽게 처리할 수 있게 해줍니다.
유니버설 개행에 대한 출력 구현은 없으며, Python 프로그램은 이를 스스로 처리하거나 그렇지 않으면 플랫폼 고유 규칙으로 파일을 작성할 것으로 예상됩니다. 그 이유는 입력이 어려운 경우이기 때문이며, 파일에 다른 개행을 출력하는 것은 Python에서 이미 충분히 쉽습니다.
또한 놀랍게도, 출력 구현은 입력 구현보다 훨씬 더 어려울 것입니다: 많은 출력이 PyXXX_Print() 메서드를 통해 이루어지는데, 이 시점에서는 파일 객체를 더 이상 사용할 수 없고 FILE *만 사용할 수 있기 때문입니다. 따라서 출력 구현은 어떻게든 FILE*에서 파일 객체로 가야 할 필요가 있는데, 현재의 개행 구분자가 저장되어 있는 곳이 바로 그 파일 객체이기 때문입니다.
입력 구현에는 이런 문제가 없습니다: Python 소스 트리에는 파일이 일부는 C에서, 일부는 Python에서 읽히는 경우가 없으며, 확장 모듈에서도 그러한 경우는 드물 것으로 예상됩니다. 만약 그런 경우가 존재한다면, 유일한 문제는 C에서 직접 이루어지는 fread()나 fgets() 호출 동안 파일 객체의 newlines 속성이 갱신되지 않는다는 것뿐입니다.
fp.write()에 전달된 문자열은 fp.newlines를 줄 종결자로 사용하도록 변환되지만 다른 모든 출력은 그렇지 않은, 부분적인 출력 구현은 제가 보기에 지나치게 놀라운 결과를 낳을 것입니다.
유니버설 개행에 대한 출력 지원이 없기 때문에 mode “rU+”에 대한 지원도 없습니다: 앞 문단에서 언급한 놀라움의 정도가 더욱 강하게 유지될 것이기 때문입니다.
eval()나 exec에 전달되는 문자열에서는 유니버설 개행 지원이 없습니다. 그러한 문자열은 항상 표준 \n 줄 바꿈 문자를 갖는 것으로 상정하며, 문자열이 파일에서 온 것이라면 그 파일은 유니버설 개행 방식으로 읽을 수 있습니다.
유니코드와 관련해서는 특별한 문제가 없다고 생각합니다. utf-16은 새로운 문제를 일으키지 않을 것인데, 그러한 파일은 어차피 바이너리 모드로 열어야 하기 때문입니다. utf-8과의 상호작용도 문제없습니다: 값 0x0a와 0x0d는 다중 바이트 시퀀스의 일부로 나타날 수 없습니다.
유니버설 개행 파일은 이터레이터와 xreadlines()에서도 잘 동작해야 하는데, 이들은 결국 일반 파일의 readline/readlines 메서드를 호출하기 때문입니다.
유니버설 개행은 임포트 시에는 자동으로 활성화되지만, 파일 열기 시에는 그렇지 않으며, 이 경우 명시적으로 open(..., "U")를 지정해야 합니다. 이는 논쟁의 여지가 있지만, 이러한 설계를 택한 몇 가지 이유는 다음과 같습니다:
- 호환성. 텍스트 파일에서 이미 자체적으로
\r\n을 해석하는 프로그램은 깨지게 됩니다. 그러한 프로그램의 예로는 다른 개행 관례로 저장된 파일을 열 때 경고를 표시하는 편집기가 있습니다. 유니버설 개행이 기본값이 되었다면, 그런 편집기는 저장 시 사용자도 모르게 줄 끝 문자를 로컬 관례로 변환해 버릴 것입니다. Unix에서 바이너리 파일을 텍스트 파일로 여는 프로그램도 깨질 것입니다(다만 그런 프로그램은 당연히 그럴 만하다고 볼 수도 있습니다 :-). - 인터페이스 명확성. 유니버설 개행은 입력 파일에서만 지원되며, 입출력 겸용 파일에서는 지원되지 않는데, 그렇게 하면 의미가 불분명해지기 때문입니다. 지금까지의 모든 읽기에서 Mac 개행만 발견되었다면, Mac 개행으로 쓰시겠습니까? 하지만 그 이후에 Unix 개행을 읽게 된다면 어떻게 하시겠습니까?
newlines 속성이 포함된 이유는 텍스트 편집기처럼 개행 관례를 실제로 신경 쓰는 프로그램이 파일에 어떤 개행이 있었는지 확인할 수 있도록 하기 위해서입니다. 그러면 그러한 프로그램은 동일한 개행 관례로 파일(의 사본)을 저장할 수 있습니다(혹은 개행이 혼합된 파일의 경우, 사용자에게 어떻게 할지 물어보거나 플랫폼 관례로 출력할 수 있습니다).
참조 구현의 한 항목에 대해 명시적으로 피드백을 구합니다: 유니버설 개행 루틴이 전역 인터프리터 잠금을 획득해야 하는지 여부입니다. 현재는 그렇게 하지 않는데, 이는 FileObject의 필드를 수정할 수도 있다는 점에서 위험을 감수하는 것으로 볼 수 있습니다. 하지만 이 루틴들은 fgets()와 fread()의 대체이기도 하므로, 루틴 호출 시점에 잠금이 유지되고 있는지 여부를 판단하기 어려울 수 있습니다. 게다가 유일한 위험은, 두 스레드가 동시에 같은 FileObject를 읽을 경우 불필요한 개행이 나타나거나 newlines 속성이 의도치 않게 혼합으로 설정될 수 있다는 것뿐입니다. 같은 FileObject를 두 스레드에서 동시에 읽는다면 어차피 문제를 자초하는 것이라고 주장하고 싶습니다.
fgets()나 fread() 대체 루틴에서는 전역적으로 접근 가능한 포인터를 조작하지 않고 정수 값 플래그만 다루므로, 코어 덤프가 발생할 가능성은 없습니다(라고 그는 말했습니다 :-).
유니버설 개행 지원은 약간의 성능 저하가 있고, 아직 구현이 모든 가능한 플랫폼에서 테스트되지 않았기 때문에 configure 시점에 비활성화할 수 있습니다. 또한 일부 플랫폼(예를 들어 WinCE나 Palm 기기)에서는 무의미할 수도 있습니다. 유니버설 개행 지원이 활성화되어 있지 않으면 파일 객체에 newlines 속성이 없으므로, 현재 Python이 이를 지원하는지는 다음과 같은 간단한 방법으로 테스트할 수 있습니다:
if hasattr(open, 'newlines'):
print 'We have universal newline support'
이 테스트가 file 타입 대신 open() 함수를 사용하는 이유는 file 타입이 존재하지 않던 버전의 Python에서도 실패하지 않도록 하기 위해서입니다(file 타입은 유니버설 개행 기능이 추가된 것과 같은 릴리스에서 내장 네임스페이스에 추가되었습니다).
또한, Python 2.5 이상 버전에서는 open()이 다시 함수로 만들어져 더 이상 file 타입과 동일하지 않으므로 이 테스트가 다시 실패한다는 점에도 유의하십시오.
참조 구현
참조 구현은 SourceForge 패치 #476814에서 확인할 수 있습니다: https://bugs.python.org/issue476814
참고 문헌
없음.
Copyright
This document has been placed in the public domain.