PEP 397 – Windows용 Python 런처
- Author:
- Mark Hammond <mhammond at skippinet.com.au>, Martin von Löwis <martin at v.loewis.de>
- Status:
- Final
- Type:
- Standards Track
- Created:
- 15-Mar-2011
- Python-Version:
- 3.3
- Post-History:
- 21-Jul-2011, 17-May-2011, 15-Mar-2011
- Resolution:
- Python-Dev message
Table of Contents
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
이 PEP는 Windows 플랫폼용 Python 런처를 설명합니다. Python 런처는 여러 휴리스틱을 사용하여 Python 실행 파일을 찾고, 지정된 명령줄로 그 실행 파일을 실행하는 단일 실행 파일입니다.
근거
Windows는 실행 파일을 확장자와 연결할 수 있는 “파일 연결”을 제공하므로, 일부 상황(예: Windows 탐색기에서 파일을 더블 클릭하는 경우)에서는 스크립트를 직접 실행할 수 있습니다. 지금까지는 “마지막으로 설치된 Python이 우선한다”는 전략을 사용해 왔습니다. 이 전략은 이상적이지는 않지만 Python 2.x 릴리스의 변경이 보수적이었기 때문에 대체로 쓸 만했습니다. Python 3.x 스크립트는 Python 2.x 스크립트와 문법적으로 호환되지 않는 경우가 많습니다. 따라서 ‘.py’ 확장자를 가진 파일이 스크립트가 대상으로 하는 Python 버전에 따라 다른 실행 파일을 사용할 수 있도록 다른 전략을 사용해야 합니다. 이를 위해 다른 운영 체제의 기존 관행을 빌려 옵니다. 아래에서 설명하듯이, 스크립트는 “셔뱅(shebang)” 줄을 통해 자신에게 필요한 Python 버전을 지정할 수 있게 됩니다.
유닉스 계열 운영 체제(이 PEP에서는 간단히 “Unix”라고 부릅니다)는 스크립트에서 “셔뱅” 줄을 검사합니다. 이 줄은 스크립트를 실행하는 데 사용할 실제 실행 파일을 지정합니다. 이렇게 하여 운영 체제는 스크립트를 마치 실행 가능한 이미지인 것처럼 실행할 수 있게 합니다. 이는 evecve(2) 매뉴얼 페이지 [1]에 자세히 설명되어 있습니다. 이 기능에 대한 사용자 문서도 작성될 예정이지만, 이 PEP에서는 해당 매뉴얼 페이지를 유효한 셔뱅 줄의 설명으로 봅니다.
또한 이러한 운영 체제는 잘 알려진 디렉터리에 Python 실행 파일에 대한 심볼릭 링크를 제공합니다. 예를 들어, 많은 시스템에는 운영 체제에 설치된 특정 버전의 Python을 가리키는 /usr/bin/python 링크가 있습니다. 이러한 심볼릭 링크 덕분에 Python이 실제로 머신의 어디에 설치되어 있는지와 관계없이 Python을 실행할 수 있습니다(예를 들어, Python이 실제로 설치된 경로를 셔뱅 줄이나 PATH에서 참조하지 않아도 됩니다.) PEP 394 ‘The “python” command on Unix-Like Systems’는 특정 Python 버전을 더 세밀하게 명세하기 위한 추가 관례를 설명합니다.
이 두 기능을 함께 사용하면 Python을 대화형으로 시작하는 일과 Python 스크립트를 실행하는 일을 모두 이식성 있고 어느 정도 예측 가능한 방식으로 할 수 있습니다. 이 PEP는 Windows 플랫폼의 Python에 같은 이점을 제공할 수 있는 런처의 구현을 설명합니다. 따라서 런처가 ‘.py’ 파일에 연결된 실행 파일이 되어 여러 Python 버전을 동시에 지원할 수 있습니다.
이 PEP는 Windows와 Unix 모두에서 동작하는 셔뱅 줄을 사용할 수 있는 기능을 제공하지만, 이것이 이 PEP의 주된 동기는 아닙니다. 주된 동기는 특정 버전을 지정하는 데 필요한 새로운 문법이나 규칙을 만들어 내지 않고도 그 버전을 지정할 수 있게 하는 것입니다.
명세
이 PEP는 런처의 기능을 명세합니다. 프로토타입 구현은 [3]에서 제공됩니다. 이 구현은 Python의 Windows 설치 프로그램과 함께 배포되지만 별도로도 제공됩니다(단, 릴리스는 Python 설치 프로그램과 함께 이루어집니다). 여기에서 규정한 기능이 계속 동작하는 한, 런처에 새로운 기능을 추가할 수 있습니다.
설치
런처는 두 가지 버전으로 제공됩니다. 하나는 콘솔 프로그램이고, 다른 하나는 “windows”(즉, GUI) 프로그램입니다. 이 두 런처는 현재 Python과 함께 제공되는 ‘python.exe’ 및 ‘pythonw.exe’ 실행 파일에 대응합니다. 콘솔 런처의 이름은 ‘py.exe’이고, Windows 런처의 이름은 ‘pyw.exe’입니다. 런처의 “windows”(즉, GUI) 버전은 가상 셔뱅 줄이 단순히 “python”을 지정하더라도 pythonw.exe를 찾아 실행하려고 시도합니다. 사실 가상 셔뱅 줄에서는 끝에 붙는 ‘w’ 표기법을 전혀 지원하지 않습니다.
권한이 있는 사용자가 설치하면 런처는 Windows 디렉터리에 설치됩니다(아래 논의 참조). 독립 실행형 설치 프로그램은 설치 프로그램의 대체 위치를 묻고, 그 위치를 사용자의 PATH에 추가합니다.
Windows 디렉터리에 설치되는 것은 32비트 실행 파일입니다(논의 참조). 독립 실행형 설치 프로그램은 64비트 버전의 런처 설치도 제공할 수 있습니다.
런처 설치는 참조 카운터와 함께 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\CurrentVersion\SharedDLLs에 등록됩니다. 런처에는 함께 배포되는 pythonXY.dll의 버전 번호와 일치하는 버전 리소스가 포함되어 있습니다. 독립 설치는 이전 버전의 런처를 새 버전으로 덮어씁니다. 독립 실행형 릴리스는 기반이 되는 CPython 릴리스의 FIELD3에 릴리스 수준 0x10을 사용합니다.
설치가 완료되면 런처의 “console” 버전은 .py 파일에, “windows” 버전은 .pyw 파일에 연결됩니다.
런처는 특정 Python 버전에 종속되지 않습니다. 예를 들어 Python 3.3과 함께 배포된 런처는 모든 Python 2.x 및 Python 3.x 버전을 찾아 실행할 수 있어야 합니다. 그러나 런처 바이너리에는 함께 릴리스되는 Python 바이너리의 버전 리소스와 동일한 버전 리소스가 있습니다.
Python 스크립트 실행
런처는 Python 스크립트를 실행하는 용도로만 제한됩니다. 범용 스크립트 런처나 셔뱅 처리기로 쓰기 위한 것이 아닙니다.
런처는 [1]에 설명된 셔뱅 줄 문법을 나열된 모든 제한 사항까지 포함하여 지원합니다.
런처는 (정규식) 접두사 “/usr/bin/”, “/usr/local/bin”, “/usr/bin/env *” 중 하나를 사용하여 Python 실행 파일을 가리키는 셔뱅 줄뿐만 아니라, 다음 없이 지정된 바이너리도 지원합니다
예를 들어 Windows의 상대 디렉터리 “\usr\bin”에 실행 파일이 있을 가능성은 낮지만, ‘#! /usr/bin/python’이라는 셔뱅 줄은 동작해야 합니다. 이는 많은 스크립트가 하나의 셔뱅 줄을 사용하여 수정 없이 Unix와 Windows 모두에서 동작할 가능성이 높다는 것을 의미합니다.
런처는 실행 파일에 대한 완전한 경로를 지원할 것입니다. 이렇게 하면 스크립트가 본질적으로 이식성을 잃게 되지만, 이는 Unix에서 제공하는 기능이며 경우에 따라 Windows 사용자에게도 유용할 것입니다.
런처는 jython이나 IronPython 같은 CPython 이외의 구현도 지원할 수 있지만, Unix에 (“/usr/bin/jython” 같은) 공통 링크가 없고 런처가 Windows에서 이러한 구현의 설치 위치를 자동으로 찾을 수 없다는 점을 고려하여, 런처는 사용자 지정 옵션을 통해 이를 지원할 것입니다. 이 기능을 활용하는 스크립트는 이식성이 없지만(이러한 사용자 지정 옵션은 런처가 실행되는 머신의 구성을 반영하도록 설정해야 하므로), 그럼에도 이 기능은 가치가 있다고 판단됩니다.
Unix에서는 사용자가 /usr/bin에 있는 링크가 원하는 버전을 가리키도록 조정하여 사용할 Python 버전을 구체적으로 제어할 수 있습니다. Windows의 런처는 Windows 링크를 사용하지 않으므로, 어떤 버전의 Python을 사용할지 결정하는 의미를 재정의하기 위해 사용자 지정 옵션(환경 변수와 INI 파일 양쪽으로 제공)을 사용합니다. 예를 들어 “/usr/bin/python2”라는 셔뱅 줄은 Python 2.x 구현을 자동으로 찾지만, 정확히 어떤 Python 2.x 구현을 선택할지는 환경 변수로 재정의할 수 있습니다. “/usr/bin/python”과 “/usr/bin/python3”도 마찬가지입니다. 이에 대해서는 이 PEP 뒷부분에서 자세히 명시합니다.
셔뱅 줄 파싱
첫 번째 명령줄 인자가 대시(‘-’) 문자로 시작하지 않으면, 그 인자를 파일로 열어 [1]의 규칙에 따라 셔뱅 줄을 파싱하려고 시도합니다:
#! interpreter [optional-arg]
파싱이 끝나면 명령은 다음 규칙에 따라 분류됩니다:
- 명령이 사용자 지정 명령의 정의로 시작하고 그 뒤에 공백 문자(줄바꿈 포함)가 오면, 해당 사용자 지정 명령을 사용합니다. 사용자 지정 명령에 대한 설명은 아래를 참조하십시오.
- 런처는 Python을 실행하는 Unix 호환 명령으로 간주되는 접두사 집합, 즉 “/usr/bin/python”, “/usr/local/bin/python”, “/usr/bin/env python”, “python”을 정의할 것입니다. 명령이 이 문자열 중 하나로 시작하면 ‘가상 명령’으로 취급되며, 사용할 실행 파일을 찾는 데는 (아래의) Python 버전 한정자에 설명된 규칙이 사용됩니다.
- 그 밖의 경우 명령은 바로 실행할 수 있는 것으로 간주됩니다. 즉, 완전한 경로(또는
PATH에 있는 실행 파일에 대한 참조)이며, 선택적으로 그 뒤에 인자가 올 수 있습니다. 문자열의 내용은 파싱되지 않으며, 스크립트 이름과 런처 명령줄 인자를 덧붙인 뒤 Windows CreateProcess 함수에 그대로 전달됩니다. 이는 상대 경로 이름과 확장자가 없는 실행 파일 참조를 처리하는 방식을 포함하여 CreateProcess가 사용하는 규칙이 적용된다는 것을 의미합니다. 특히 Windows 명령 처리기는 사용되지 않으므로, 명령 처리기가 사용하는 특수 규칙(‘.exe’ 이외의 확장자 자동 추가, 배치 파일 지원 등)은 적용되지 않습니다.
‘가상’ 셔뱅 줄을 사용하면 여러 운영 체제와 같은 운영 체제의 서로 다른 설치 환경에서 동작하는 이식 가능한 셔뱅 줄을 지정할 수 있으므로, 이 방식의 사용을 권장합니다.
첫 번째 인자를 파일로 열 수 없거나 유효한 셔뱅 줄을 찾을 수 없으면, 런처는 ‘#!python’이라는 셔뱅 줄을 찾은 것처럼 동작합니다. 즉, 기본 Python 인터프리터를 찾아 그 인터프리터에 인자를 전달합니다. 그러나 유효한 셔뱅 줄을 찾았지만 그 줄에 지정된 프로세스를 시작할 수 없으면, 기본 인터프리터는 시작되지 않습니다. 지정된 자식 프로세스를 생성하지 못한 오류로 인해 런처는 적절한 메시지를 표시하고 특정 종료 코드로 종료합니다.
설정 파일
런처는 두 개의 .ini 파일을 검색합니다. 하나는 현재 사용자의 “application data” 디렉터리(즉, Windows 함수 SHGetFolderPath를 CSIDL_LOCAL_APPDATA와 함께 호출했을 때 반환되는 디렉터리로, Vista 이상에서는 %USERPROFILE%\AppData\Local, XP에서는 %USERPROFILE%\Local Settings\Application Data)에 있는 py.ini이고, 다른 하나는 런처와 같은 디렉터리에 있는 py.ini입니다. 런처의 ‘console’ 버전(즉, py.exe)과 ‘windows’ 버전(즉, pyw.exe) 모두 같은 .ini 파일을 사용합니다.
“애플리케이션 디렉터리”에 지정된 사용자 정의 설정이 실행 파일 옆에 있는 설정보다 우선하므로, 런처 옆에 있는 .ini 파일에 쓰기 권한이 없을 수도 있는 사용자도 그 전역 .ini 파일에 있는 명령을 재정의할 수 있습니다.
셔뱅 줄의 가상 명령
가상 명령은 Unix 플랫폼에서 동작할 것으로 예상되는 문자열로 시작하는 셔뱅 줄입니다. 예로는 ‘/usr/bin/python’, ‘/usr/bin/env python’, ‘python’ 등이 있습니다. 선택적으로, 가상 명령 뒤에 ‘/usr/bin/python2’나 ‘/usr/bin/python3.2’처럼 버전 한정자(아래 참조)를 붙일 수 있습니다. 실행되는 명령은 아래의 Python 버전 한정자에서 설명하는 규칙에 따라 결정됩니다.
사용자 정의 명령
런처는 Windows .ini 파일(즉, Windows 함수 GetPrivateProfileString으로 파싱할 수 있는 파일)에 “사용자 정의 명령”을 정의하는 기능을 지원할 것입니다. ‘[commands]’라는 섹션을 만들어, 키 이름으로 가상 명령을 정의하고 값으로 해당 가상 명령에 사용할 실제 명령줄을 지정할 수 있습니다.
예를 들어, INI 파일에 다음과 같은 내용이 있다고 가정합니다:
[commands]
vpython=c:\bin\vpython.exe -foo
그러면 ‘doit.py’라는 스크립트에 있는 ‘#! vpython’ 셔뱅 줄로 인해 런처는 c:\bin\vpython.exe -foo doit.py라는 명령줄을 사용하게 됩니다.
.ini 파일의 이름, 위치, 검색 순서에 대한 정확한 세부 사항은 런처 문서 [4]에 있습니다.
Python 버전 한정자
앞서 설명한 기능 중 일부는 선택적인 Python 버전 한정자를 사용할 수 있게 합니다.
버전 한정자는 메이저 버전 번호로 시작하며, 선택적으로 그 뒤에 마침표(‘.’)와 마이너 버전 지정자가 올 수 있습니다. 부 한정자가 지정된 경우, 선택적으로 그 뒤에 “-32”를 붙여 해당 버전의 32비트 구현을 사용하도록 지정할 수 있습니다. 64비트가 기본 구현이므로(아래 참조) “-64” 한정자는 필요하지 않다는 점에 유의하십시오.
64비트 Windows에 같은 (주.부) Python 버전의 32비트 구현과 64비트 구현이 모두 설치되어 있으면, 항상 64비트 버전이 우선합니다. 이는 런처의 32비트 구현과 64비트 구현 모두에 해당합니다. 즉, 32비트 런처도 지정된 버전의 64비트 Python 설치본이 있으면 그것을 우선하여 실행합니다. 이렇게 하는 이유는 PC에 어떤 버전이 설치되어 있는지만 알면 설치 순서와 관계없이(즉, 32비트와 64비트 중 어느 버전의 Python과 그에 대응하는 런처가 마지막에 설치되었는지 몰라도) 런처의 동작을 예측할 수 있도록 하기 위해서입니다. 위에서 언급했듯이, 버전 지정자에 선택적인 “-32” 접미사를 붙여 이 동작을 바꿀 수 있습니다.
명령에서 버전 한정자를 찾지 못하면, 환경 변수 PY_PYTHON을 설정하여 기본 버전 한정자를 지정할 수 있습니다. 기본값은 “2”입니다. 이 값은 메이저 버전만 지정할 수도 있고(예: “2”), major.minor 한정자를 지정할 수도 있으며(예: “2.6”), 심지어 major.minor-32를 지정할 수도 있다는 점에 유의하십시오.
마이너 버전 한정자를 찾지 못하면, PY_PYTHON{major} 환경 변수(여기서 {major}는 위에서 결정한 현재 메이저 버전 한정자입니다)를 설정하여 전체 버전을 지정할 수 있습니다. 그러한 옵션을 찾지 못하면, 런처는 설치된 Python 버전을 열거하여 해당 메이저 버전에서 찾은 최신 마이너 릴리스를 사용합니다. 이는 보장되지는 않지만 대개 그 계열에서 가장 최근에 설치된 버전일 것입니다.
환경 변수 외에도, 런처가 사용하는 .INI 파일에서 같은 설정을 구성할 수 있습니다. INI 파일의 섹션 이름은 [defaults]이고, 키 이름은 앞의 PY_ 접두사를 뺀 환경 변수 이름과 같습니다(INI 파일의 키 이름은 대소문자를 구분하지 않는다는 점에 유의하십시오.) 환경 변수의 내용은 INI 파일에 지정된 내용보다 우선합니다.
명령줄 처리
셔뱅 줄은 첫 번째 명령줄 인자에 대해서만, 그리고 그 인자가 ‘-‘로 시작하지 않는 경우에만 검사합니다.
명령줄 인자가 “-h” 또는 “–help” 하나뿐이면, 런처는 간단한 배너와 명령줄 사용법을 출력한 다음 그 인자를 기본 Python에 전달합니다. 그 결과 런처의 도움말이 먼저 출력되고, 이어서 Python 자체의 도움말이 출력됩니다. 런처의 출력은 확장된 도움말 정보가 Python이 아닌 런처에서 나온 것임을 분명히 보여 줍니다.
Python을 대화형으로 실행하는 경우를 배려하여, 런처는 특정 버전을 사용하도록 지정할 수 있도록 첫 번째 명령줄 인자로 대시(“-“)와 그 뒤에 오는 앞서 설명한 버전 한정자를 선택적으로 받을 수 있게 지원합니다. 예를 들어 “py.exe”는 설치된 최신 Python 2.x 구현을 찾아 실행할 수 있지만, “py.exe -3” 같은 명령줄은 최신 Python 3.x 구현을 실행하도록 지정할 수 있고, “py.exe -2.6-32”는 32비트 Python 2.6 구현을 찾아 실행하도록 지정할 수 있습니다. Python 2.x 구현을 -3 플래그와 함께 실행하려면 명령줄이 “py.exe -2 -3”과 비슷해야 합니다(물론 이 런처를 쓰지 않고 해당 버전의 Python을 직접 실행할 수도 있습니다.) 이 기능은 셔뱅 처리와 함께 사용할 수 없다는 점에 유의하십시오. 셔뱅 줄을 검사할 파일과 이 인자는 둘 다 첫 번째 인자여야 하므로 서로 배타적이기 때문입니다.
그 밖의 모든 인자는 변경 없이 자식 Python 프로세스에 전달됩니다.
프로세스 실행
런처는 대화형으로 작업하는 Python 개발자를 위한 몇 가지 편의 기능을 제공합니다. 예를 들어 명령줄 인자 없이 런처를 시작하면 기본 Python이 명령줄 인자 없이 실행됩니다. 또한 특정 Python 버전을 대화형으로 실행할 수 있도록 명령줄 인자를 지원합니다. 하지만 이러한 편의 기능이 스크립트 실행이라는 본래 목적을 해쳐서는 안 되며, 원하면 쉽게 피할 수 있어야 합니다.
런처는 실제 인터프리터를 시작하기 위해 하위 프로세스를 생성합니다. 그 근거는 아래의 논의를 참고하십시오.
논의
런처가 System32 디렉터리가 아니라 Windows 디렉터리에 설치된다는 점이 의외일 수 있습니다. 그 이유는 64비트 시스템에서 실행되는 32비트 프로세스의 Path에 System32 디렉터리가 포함되지 않기 때문입니다. 반면 Windows 디렉터리는 항상 경로에 포함되어 있습니다.
Windows 디렉터리에 설치되는 런처는 32비트 실행 파일입니다. 그래야 32비트 CPython 설치 프로그램이 32비트와 64비트 Windows 설치 모두에 같은 바이너리를 제공할 수 있습니다.
이상적으로는 런처 프로세스가 같은 프로세스 안에서 Python을 직접 실행하는 것이 좋습니다. 주된 이유는 런처 프로세스의 부모가 런처를 종료하면 Python 인터프리터도 함께 종료되게 할 수 있기 때문입니다. 런처가 Python을 하위 프로세스로 실행하는 경우, 런처의 부모가 런처를 종료해도 Python 프로세스는 영향을 받지 않습니다.
하지만 이 방식에는 여러 가지 현실적인 문제가 있습니다. Windows는 Unix의 execv* 계열 함수를 지원하지 않으므로, 이 방식은 런처가 Python DLL을 동적으로 로드하는 방법으로만 구현할 수 있는데, 그렇게 하면 여러 부작용이 생깁니다. 가장 심각한 부작용은 sys.executable의 값이 Python 구현이 아니라 런처를 가리키게 된다는 점입니다. 많은 Python 스크립트가 자식 프로세스를 실행하는 데 sys.executable 값을 사용하는데, 런처를 사용하면 이러한 스크립트가 예상대로 동작하지 않을 수 있습니다. 셔뱅 줄이 ‘#! /usr/bin/python3’인 “부모” 스크립트가 sys.executable을 통해 (셔뱅이 없는) 자식 스크립트를 실행하려 한다고 생각해 봅시다. 현재는 부모 스크립트를 실행하는 것과 정확히 같은 버전으로 자식이 실행됩니다. sys.executable이 런처를 가리킨다면 자식은 Python 2.x 버전으로 실행될 가능성이 높고, SyntaxError로 실패할 가능성이 높습니다.
또 다른 걸림돌은 앞서 설명한 “사용자 정의 명령” 기능을 사용하는 대체 Python 구현의 지원입니다. 이 경우에는 실행 중인 실행 파일에 명령을 동적으로 로드할 수 없습니다.
마지막 걸림돌은 앞서 설명한 64비트 및 32비트 프로그램에 관한 규칙입니다. 32비트 런처는 64비트 버전의 Python을 로드할 수 없으며, 그 반대도 마찬가지입니다.
이러한 점을 고려하여, 런처는 자식 프로세스에서 명령을 실행하고, 자식 프로세스가 실행되는 동안 살아 있다가, 자식이 반환한 것과 같은 종료 코드로 종료합니다. 런처가 종료되어도 자식이 종료되지 않는다는 우려를 해소하기 위해, Win32 Job API를 사용하여 부모가 종료되면 자식 프로세스도 자동으로 종료되도록 합니다(단, 그 자식 프로세스의 자식들은 현재와 마찬가지로 계속 실행됩니다.) 이 Windows API는 Windows XP 이상에서 사용할 수 있으므로, 이 런처는 Windows 2000 이하에서는 동작하지 않습니다.
참고 문헌
Copyright
This document has been placed in the public domain.