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 버전에 따라 다른 실행 파일을 사용하도록 하려면 다른 전략을 사용해야 합니다. 이를 위해 다른 운영 체제의 기존 관행을 차용합니다. 아래에서 설명하는 “셰뱅” 줄을 통해 스크립트가 필요한 Python 버전을 지정할 수 있게 합니다.
Unix 계열 운영 체제(이 PEP에서는 간단히 “Unix”라고 합니다)는 스크립트에서 해당 스크립트를 실행하는 데 사용할 실제 실행 파일을 지정하는 “셰뱅” 줄을 검사하여 스크립트를 실행 파일 이미지인 것처럼 실행할 수 있게 합니다. 이는 evecve(2) 매뉴얼 페이지 [1]에 자세히 설명되어 있으며, 이 기능에 대한 사용자 문서가 작성될 예정이지만 이 PEP의 목적상 해당 매뉴얼 페이지가 유효한 셰뱅 줄을 설명합니다.
또한 이러한 운영 체제는 잘 알려진 디렉터리에 Python 실행 파일에 대한 심볼릭 링크를 제공합니다. 예를 들어 많은 시스템에는 운영 체제 아래에 설치된 특정 Python 버전을 가리키는 /usr/bin/python 링크가 있습니다. 이러한 심볼릭 링크를 사용하면 Python이 실제로 컴퓨터의 어디에 설치되어 있는지와 관계없이 Python을 실행할 수 있습니다(예: 셰뱅 줄이나 PATH에 Python이 실제로 설치된 경로를 지정할 필요가 없습니다). PEP 394 ‘Unix 계열 시스템의 “python” 명령’은 특정 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 실행 파일을 가리키는 셰뱅 줄과, 접두사 없이 지정된 바이너리를 지원합니다.
예를 들어 ‘#! /usr/bin/python’이라는 셰뱅 줄은 상대적인 Windows 디렉터리 “\usr\bin”에 실행 파일이 있을 가능성이 낮더라도 작동해야 합니다. 이는 많은 스크립트가 하나의 셰뱅 줄을 사용하고 수정 없이 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”입니다. 명령이 이러한 문자열 중 하나로 시작하면 ‘virtual command’로 처리하고, 사용할 실행 파일을 찾기 위해 Python 버전 지정자(아래)의 규칙을 사용합니다.
- 그렇지 않으면 명령은 직접 실행할 준비가 된 것으로 간주됩니다. 즉, 인수가 선택적으로 뒤따르는 정규화된 경로(또는
PATH에 있는 실행 파일에 대한 참조)입니다. 문자열의 내용은 구문 분석되지 않으며, 스크립트 이름과 런처 명령줄 인자를 뒤에 추가한 후 Windows CreateProcess 함수에 직접 전달됩니다. 이는 상대 경로 이름과 확장자가 없는 실행 파일 참조가 처리되는 방식을 포함하여 CreateProcess가 사용하는 규칙이 적용된다는 의미입니다. 특히 Windows 명령 프로세서는 사용되지 않으므로 ‘.exe’ 이외의 확장자를 자동으로 추가하거나 배치 파일을 지원하는 것과 같은 명령 프로세서의 특수 규칙은 사용되지 않습니다.
‘virtual’ 셰뱅 줄을 사용하는 것이 권장됩니다. 이를 통해 여러 운영 체제와 동일한 운영 체제의 서로 다른 설치 환경에서 작동하는 이식 가능한 셰뱅 줄을 지정할 수 있기 때문입니다.
첫 번째 인자를 파일로 열 수 없거나 유효한 셰뱅 줄을 찾을 수 없으면 런처는 ‘#!python’이라는 셰뱅 줄을 찾은 것처럼 동작합니다. 즉, 기본 Python 인터프리터를 찾아 인자를 전달합니다. 그러나 유효한 셰뱅 줄을 찾았지만 해당 줄에 지정된 프로세스를 시작할 수 없는 경우 기본 인터프리터는 시작되지 않습니다. 지정된 자식 프로세스를 생성하지 못하면 런처가 적절한 메시지를 표시하고 특정 종료 코드로 종료됩니다.
설정 파일
런처는 두 개의 .ini 파일을 검색합니다 - 현재 사용자의 “애플리케이션 데이터” 디렉터리(즉, CSIDL_LOCAL_APPDATA를 인자로 Windows 함수 SHGetFolderPath를 호출했을 때 반환되는 디렉터리로, Vista 이상에서는 %USERPROFILE%\AppData\Local, XP에서는 %USERPROFILE%\Local Settings\Application Data)에 있는 py.ini와 런처와 같은 디렉터리에 있는 py.ini입니다. 동일한 .ini 파일이 런처의 ‘콘솔’ 버전(즉 py.exe)과 ‘윈도우’ 버전(즉 pyw.exe) 양쪽에 모두 사용됩니다.
“애플리케이션 디렉터리”에 지정된 설정이 실행 파일 옆의 것보다 우선하므로, 런처 옆의 .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” 한정자는 필요하지 않다는 점에 유의하십시오.
동일한 (주.부) Python 버전의 32비트와 64비트 구현이 모두 설치된 64비트 Windows에서는, 64비트 버전이 항상 우선됩니다. 이는 런처의 32비트와 64비트 구현 모두에서 마찬가지입니다 - 32비트 런처는 지정된 버전의 64비트 Python 설치본이 있으면 이를 실행하는 쪽을 선호합니다. 이는 PC에 어떤 버전이 설치되어 있는지만 알면, 설치된 순서(즉, 32비트 또는 64비트 버전의 Python과 그에 대응하는 런처 중 어느 쪽이 나중에 설치되었는지)와 무관하게 런처의 동작을 예측할 수 있도록 하기 위함입니다. 위에서 언급했듯이, 버전 지정자에 선택적으로 “-32” 접미사를 사용하여 이 동작을 변경할 수 있습니다.
명령에서 버전 한정자를 찾을 수 없는 경우, 환경 변수 PY_PYTHON을 설정하여 기본 버전 한정자를 지정할 수 있습니다 - 기본값은 “2”입니다. 이 값은 주 버전만(예: “2”) 지정할 수도 있고, 주.부 한정자(예: “2.6”), 심지어 주.부-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 디렉터리에 설치된다는 점이 의외일 수 있습니다. 그 이유는 System32 디렉터리가 64비트 시스템에서 실행되는 32비트 프로세스의 Path에 포함되지 않기 때문입니다. 하지만 Windows 디렉터리는 항상 path에 포함되어 있습니다.
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.