Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 441 – Python ZIP 애플리케이션 지원 개선

Author:
Daniel Holth <dholth at gmail.com>, Paul Moore <p.f.moore at gmail.com>
Discussions-To:
Python-Dev message
Status:
Final
Type:
Standards Track
Created:
30-Mar-2013
Python-Version:
3.5
Post-History:
30-Mar-2013, 01-Apr-2013, 16-Feb-2015
Resolution:
Python-Dev message

Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

Python ZIP 애플리케이션 지원 개선

Python은 버전 2.6 [1]부터 디렉터리나 ZIP 형식 아카이브를 스크립트로 실행할 수 있었습니다. 첫 번째 인자로 zip 파일이나 디렉터리를 주어 호출하면 인터프리터는 해당 디렉터리를 sys.path에 추가하고 __main__ 모듈을 실행합니다. 이러한 아카이브는 단일 파일 스크립트로 배포해야 하지만 여러 모듈의 모음으로 작성해야 할 만큼 복잡한 소프트웨어를 배포하는 데 매우 좋은 방법입니다.

이 기능은 마땅히 받아야 할 만큼 널리 쓰이지 못하고 있습니다. 주된 이유는 Python 2.6 [2]의 일부로 홍보되지 않아 비교적 알려지지 않았기 때문입니다. 또한 Windows 설치 프로그램이 이 형식의 파일을 런처와 연결할 파일 확장자를 (.py 외에는) 등록하지 않기 때문이기도 합니다.

이 PEP는 이 기능을 다시 알리고, .pyz 및 .pyzw 확장자를 각각 “Python ZIP 애플리케이션”과 “창 모드 Python ZIP 애플리케이션”으로 정의하며, 이 형식을 관리하기 위한 간단한 도구를 제공함으로써 이러한 문제를 해결할 것을 제안합니다.

새로운 Python ZIP 애플리케이션 확장자

“Python Zip 애플리케이션”이라는 용어는 Python이 직접 실행할 수 있는 형태로 Python 코드를 담고 있는 zip 형식 아카이브를 가리키는 공식 용어로 사용됩니다(구체적으로는 아카이브의 루트 디렉터리에 __main__.py 파일이 있어야 합니다). .pyz 확장자는 이러한 파일과 공식적으로 연결됩니다.

Python 3.5 설치 프로그램은 .pyz 및 .pyzw “Python Zip 애플리케이션”을 플랫폼 런처와 연결하여 실행할 수 있도록 합니다. .pyz 아카이브는 콘솔 애플리케이션이고 .pyzw 아카이브는 창 모드 애플리케이션입니다. 이 구분은 앱을 실행할 때 콘솔이 나타나야 하는지를 나타냅니다.

Unix에서는 .pyz 확장자와 “Python Zip 애플리케이션”이라는 이름이 (MIME 타입 데이터베이스에?) 등록된다면 이상적일 것입니다. 그러나 이러한 연결은 이 PEP의 범위를 벗어납니다.

Python Zip 애플리케이션의 앞부분에는 올바른 Python 인터프리터를 가리키는 #! 줄과 선택적인 설명을 덧붙일 수 있습니다:

#!/usr/bin/env python3
#  Python application packed with zipapp module
(binary contents of archive)

Unix에서는 이를 통해 운영 체제가 표준 “셔뱅(shebang)” 지원을 거쳐 올바른 인터프리터로 파일을 실행할 수 있습니다. Windows에서는 Python 런처가 셔뱅 지원을 구현합니다.

그러나 Python 인터프리터에 파일 이름을 직접 전달하여 .pyz 애플리케이션을 실행하는 방법은 언제나 가능합니다.

배경 설명을 덧붙이면, ZIP 아카이브는 파일 끝으로부터의 상대 오프셋을 담은 푸터(footer)를 갖도록 정의되어 있습니다. 따라서 ZIP 아카이브는 다른 어떤 파일의 끝에 이어 붙여도 유효한 상태를 유지합니다. 이 기능은 완전히 표준적인 것이며, 자동 압축 해제 ZIP 아카이브와 bdist_wininst 설치 프로그램 형식이 바로 이 방식으로 동작합니다.

최소한의 도구: zipapp 모듈

이 PEP는 또한 이러한 아카이브를 다루기 위한 모듈을 포함할 것을 제안합니다. 이 모듈은 Python zip 애플리케이션 아카이브를 다루기 위한 함수들과, 아카이브를 생성하고 조작하기 위한 명령줄 인터페이스(python -m zipapp 명령을 통해)를 포함할 것입니다.

Python Zip 애플리케이션을 관리하기 위한 더 완전한 도구는 PyPI의 서드파티 애플리케이션으로 만들어지기를 권장합니다. 현재 이러한 도구로는 pyzzer [5]와 pex [6]가 있습니다.

모듈 인터페이스

zipapp 모듈은 다음 함수들을 제공할 것입니다:

create_archive(source, target=None, interpreter=None, main=None)

source로부터 애플리케이션 아카이브를 생성합니다. source는 다음 중 어느 것이든 될 수 있습니다:

  • 디렉터리의 이름. 이 경우 해당 디렉터리의 내용으로부터 새 애플리케이션 아카이브가 생성됩니다.
  • 기존 애플리케이션 아카이브 파일의 이름이며, 이 경우 해당 파일이 대상으로 복사됩니다. 필요하다면 파일 이름에 .pyz 또는 .pyzw 확장자가 포함되어야 합니다.
  • 바이트 모드에서 읽기용으로 열린 파일 객체입니다. 파일의 내용은 애플리케이션 아카이브여야 하며, 파일 객체는 아카이브의 시작 위치에 놓여 있다고 가정합니다.

target 인자는 결과 아카이브가 기록될 위치를 결정합니다:

  • 파일 이름이면 아카이브가 해당 파일에 기록됩니다.
  • 열린 파일 객체이면 아카이브가 해당 파일 객체에 기록되며, 이 파일 객체는 바이트 모드에서 쓰기용으로 열려 있어야 합니다.
  • 대상이 생략되면(또는 None이면) 소스는 디렉터리여야 하며, 대상은 소스와 같은 이름에 .pyz 확장자가 추가된 파일이 됩니다.

interpreter 인자는 아카이브를 실행할 Python 인터프리터의 이름을 지정합니다. 이것은 아카이브 시작 부분에 “셔뱅(shebang)” 줄로 기록됩니다. Unix에서는 OS가 이를 해석하고, Windows에서는 Python 런처가 이를 처리합니다. 인터프리터를 지정하는 interpreter 인자를 생략하면 셔뱅 줄이 기록되지 않습니다. 인터프리터가 지정되고 대상이 파일 이름이면 대상 파일의 실행 비트가 설정됩니다.

main 인자는 아카이브의 메인 프로그램으로 사용될 호출 가능 객체의 이름을 지정합니다. 이 인자는 소스가 디렉터리이고 소스에 __main__.py 파일이 아직 없는 경우에만 지정할 수 있습니다. main 인자는 “pkg.module:callable” 형식이어야 하며, 아카이브는 “pkg.module”을 임포트하고 주어진 호출 가능 객체를 인자 없이 실행하는 방식으로 실행됩니다. 소스가 디렉터리이고 __main__.py 파일을 포함하지 않는 경우 main 인자를 생략하면 오류입니다. 그렇지 않으면 결과 아카이브를 실행할 수 없기 때문입니다.

source 또는 target에 파일 객체를 지정한 경우, create_archive를 호출한 후 이를 닫는 것은 호출자의 책임입니다.

기존 아카이브를 복사할 때 제공되는 파일 객체에는 read 및 readline 메서드, 또는 write 메서드만 있으면 됩니다. 디렉터리로부터 아카이브를 생성할 때 대상이 파일 객체이면 이 객체는 zipfile.ZipFile 클래스에 전달되므로, 해당 클래스가 요구하는 메서드를 제공해야 합니다.

get_interpreter(archive)

archive의 셔뱅 줄에 지정된 인터프리터를 반환합니다. 셔뱅이 없으면 이 함수는 None을 반환합니다. archive 인자는 파일 이름이거나 바이트 모드에서 읽기용으로 열린 파일류 객체일 수 있습니다.

명령줄 사용법

zipapp 모듈은 python -m 플래그로 실행할 수 있습니다. 명령줄 인터페이스는 다음과 같습니다:

python -m zipapp directory [options]

    Create an archive from the given directory.  An archive will
    be created from the contents of that directory.  The archive
    will have the same name as the source directory with a .pyz
    extension.

    The following options can be specified:

    -o archive / --output archive

        The destination archive will have the specified name.  The
        given name will be used as written, so should include the
        ".pyz" or ".pyzw" extension.

    -p interpreter / --python interpreter

        The given interpreter will be written to the shebang line
        of the archive.  If this option is not given, the archive
        will have no shebang line.

    -m pkg.mod:fn / --main pkg.mod:fn

        The source directory must not have a __main__.py file. The
        archiver will write a __main__.py file into the target
        which calls fn from the module pkg.mod.

명령줄 인터페이스의 동작은 zipapp.create_archive()의 동작과 일치합니다.

또한 명령줄 인터페이스를 사용하여 기존 아카이브를 다룰 수도 있습니다:

python -m zipapp app.pyz --show

    Displays the shebang line of an archive.  Output is of the
    form

        Interpreter: /usr/bin/env
    or
        Interpreter: <none>

    and is intended for diagnostic use, not for scripts.

python -m zipapp app.pyz -o newapp.pyz [-p interpreter]

    Copy app.pyz to newapp.pyz, modifying the shebang line based
    on the -p option (as for creating an archive, no -p option
    means remove the shebang line).  Specifying a destination is
    mandatory.

    In-place modification of an archive is *not* supported, as the
    risk of damaging archives is too great for a simple tool.

앞서 언급했듯이 아카이브는 표준 zip 파일이므로, 표준 ZIP 유틸리티나 Python의 zipfile 모듈로 압축을 풀 수 있습니다. 이러한 이유로 아카이브의 내용을 나열하거나 압축을 푸는 인터페이스는 제공되지 않으며 필요하지도 않습니다.

FAQ

표준 ZIP 유틸리티가 맨 앞에 있는 #!를 처리할 수 있다고 확신합니까?
물론입니다. zipfile 명세는 zip 파일 앞에 임의의 데이터를 덧붙이는 것을 허용합니다. 이 기능은 “자체 압축 해제 zip” 프로그램에서 흔히 사용됩니다. 사용하시는 아카이브 프로그램이 이를 처리하지 못한다면, 그것은 해당 아카이브 프로그램의 버그입니다.
zipapp은 zipfile 모듈을 아주 얇게 감싼 래퍼에 불과하지 않습니까?
그렇습니다. 다른 도구를 사용해 Python zip 애플리케이션 아카이브를 직접 빌드하고 싶다면, 그렇게 만든 아카이브도 똑같이 잘 동작합니다. zipapp 모듈은 편의를 위한 것일 뿐, 그 이상은 아닙니다.
왜 그냥 .zip이나 .py 확장자를 사용하지 않습니까?
사용자는 .zip 파일이 아카이브 도구로 열리기를 기대하고, .py 파일에는 읽을 수 있는 텍스트가 들어 있기를 기대합니다. 두 가지 모두 이 사용 사례에서는 혼란을 줄 수 있습니다.
이것은 기존 패키지 형식과 비교하여 어떻습니까?
sdist, bdist, wheel 형식은 기존 Python 설치 환경에 설치할 모듈을 패키징하기 위해 설계되었습니다. 이 형식들은 설치하지 않고 사용하도록 의도된 것이 아닙니다. 실행 가능한 zip 형식은 설치할 필요 없이 독립적으로 사용하도록 특별히 설계되었습니다. 사실상 이들은 독립 실행형 Python 스크립트의 다중 파일 버전입니다.

거부된 제안

셔뱅 줄을 위한 편의 값

흔히 쓰이는 인터프리터 값에 대해 “편의” 형식을 두는 것이 가치가 있겠습니까? 예를 들어, -p 3이 -p "/usr/bin/env python3"와 같은 의미를 갖도록 하는 것입니다. 이렇게 하면 흔한 경우에 입력량을 크게 줄일 수 있을 뿐만 아니라, “다른” 플랫폼에서 셔뱅이 처리되는 복잡한 방식을 이해하고 싶지 않거나 이해할 필요가 없는 사람들에게 크로스 플랫폼 선택지를 제공할 수 있습니다.

단점은 약어를 어떻게 해석해야 할지 명확하지 않다는 것입니다. 예를 들어, “3”은 “/usr/bin/env python3”, “/usr/bin/python3”, “python3” 중 무엇을 의미해야 합니까, 아니면 다른 무언가를 의미해야 합니까? 또한 “/usr/bin/env python”(사용 가능한 아무 버전의 파이썬)이라는 핵심적인 경우에 대한 명확한 약식 표현이 없으므로, 지나치게 제한적인 셔뱅 줄을 가진 스크립트가 작성되는 결과로 쉽게 이어질 수 있습니다.

전반적으로 이점보다 문제점이 더 많아 보이므로, 검토 대상에서 제외되었습니다.

.pyz를 미디어 타입으로 등록하기

Unix 확장자 데이터베이스에 .pyz 확장자를 등록해야 한다는 제안이 있었습니다 [3]. Windows 설치 프로그램이 확장자를 등록하는 것에 상응하는 조치로서 이렇게 하는 것은 타당하지만, .py 확장자는 미디어 타입 데이터베이스에 등재되어 있지 않습니다 [4]. .py 없이 .pyz만 등록하는 것은 합리적으로 보이지 않으므로, 이 아이디어는 이 PEP에서 제외되었습니다. 관심 있는 당사자가 향후에 .py와 .pyz 둘 다 등록되도록 추진할 수 있습니다.

기본 인터프리터

이 PEP의 첫 초안에서는 /usr/bin/env python을 기본 인터프리터로 사용하자고 제안했습니다. 많은 배포판에서 python 명령의 기본값이 Python 2이기 때문에 Unix 사용자들은 이 동작으로 인해 문제를 겪으며, 이 PEP가 기본적으로 Python 3을 우선해야 한다는 의견이 있습니다. 그러나 python3 명령을 사용하면 Windows 사용자에게 예상치 못한 동작이 발생할 수 있습니다. Windows에서는 사용자가 python 명령에 대한 런처의 기본 동작을 흔히 사용자 정의하지만, python3의 동작은 그에 맞게 수정되지 않을 수 있기 때문입니다.

그 결과 “모호함에 직면했을 때는 추측을 거부하라”라는 원칙을 적용하여, 명시적으로 요청하지 않는 한 아카이브에는 셔뱅 줄이 없습니다. Windows에서는 여전히 런처가 (기본 Python으로) 아카이브를 실행하며, Unix에서는 원하는 Python 인터프리터를 명시적으로 호출하여 아카이브를 실행할 수 있습니다.

셔뱅 줄을 관리하는 명령줄 도구

사용자가 기존 아카이브의 셔뱅 줄을 수정하거나, 단순히 현재 셔뱅 줄을 표시하기만 원할 수도 있다는 점은 충분히 생각할 수 있습니다. 이는 기존 도구로는 하기 까다롭습니다(zip 프로그램은 일반적으로 앞에 덧붙인 데이터를 완전히 무시하며, 텍스트 편집기는 바이너리 데이터가 포함된 파일을 편집하는 데 어려움을 겪을 수 있습니다).

zipapp 모듈은 셔뱅 줄을 처리하는 함수를 제공하지만, 해당 기능에 대한 명령줄 인터페이스는 포함하지 않습니다. 그렇게 만든 인터페이스가 지나치게 복잡해지거나 혼란을 줄 우려 없이 이를 제공할 방법이 명확하지 않기 때문입니다. 셔뱅 줄을 변경해야 하는 경우는 드물 것으로 예상됩니다.

참조 구현

참조 구현은 http://bugs.python.org/issue23491 에 있습니다.

참고 문헌

이 PEP에 대한 논의는 python-dev 메일링 리스트에서 이루어졌으며, 해당 스레드는 https://mail.python.org/pipermail/python-dev/2015-February/138277.html 에서 시작합니다.