zipapp — 실행 가능한 파이썬 zip 아카이브 관리

버전 3.5에 추가.

소스 코드: Lib/zipapp.py


이 모듈은 파이썬 인터프리터가 직접 실행할 수 있는 파이썬 코드를 포함하는 zip 파일 생성을 관리하는 도구를 제공합니다. 이 모듈은 명령 줄 인터페이스파이썬 API를 모두 제공합니다.

기본 예

다음 예제는 명령 줄 인터페이스를 사용하여 파이썬 코드가 포함된 디렉터리에서 실행 가능 아카이브를 만드는 방법을 보여줍니다. 실행하면, 아카이브가 아카이브의 모듈 myapp에서 main 함수를 실행합니다.

$ python -m zipapp myapp -m "myapp:main"
$ python myapp.pyz
<output from myapp>

명령 줄 인터페이스

명령 줄에서 프로그램으로 호출될 때, 다음 형식이 사용됩니다:

$ python -m zipapp source [options]

source가 디렉터리면, source의 내용으로부터 아카이브를 만듭니다. source가 파일이면, 아카이브여야 하며, 대상 아카이브로 복사됩니다 (또는 –info 옵션이 지정되면 셔뱅(shebang) 줄의 내용이 표시됩니다).

다음과 같은 옵션이 이해됩니다:

-o <output>, --output=<output>

출력을 output이라는 이름의 파일에 씁니다. 이 옵션을 지정하지 않으면, 출력 파일명은 입력 source와 같고, 확장자 .pyz가 추가됩니다. 명시적인 파일명이 제공되면, 그대로 사용됩니다 (그래서 필요하면 .pyz 확장자를 포함해야 합니다).

source가 아카이브이면 반드시 출력 파일명을 지정해야 합니다 (그리고 이 경우, outputsource와 달라야 합니다).

-p <interpreter>, --python=<interpreter>

실행할 명령으로 interpreter를 지정하여 #! 줄을 아카이브에 추가합니다. 또한, POSIX에서, 아카이브를 실행 파일로 만듭니다. 기본값은 #! 줄을 쓰지 않고, 파일을 실행 파일로 만들지 않는 것입니다.

-m <mainfn>, --main=<mainfn>

아카이브에 mainfn을 실행하는 __main__.py 파일을 씁니다. mainfn 인자는 “pkg.mod:fn” 형식이어야 합니다. 여기서 “pkg.mod”는 아카이브의 패키지/모듈이며, “fn”은 주어진 모듈에 있는 콜러블입니다. __main__.py 파일은 그 콜러블을 실행합니다.

아카이브를 복사할 때 --main을 지정할 수 없습니다.

-c, --compress

디플레이트(deflate) 메서드로 파일을 압축하여, 출력 파일의 크기를 줄입니다. 기본적으로, 파일은 아카이브에 압축되지 않은 상태로 저장됩니다.

아카이브를 복사할 때 --compress는 효과가 없습니다.

버전 3.7에 추가.

--info

진단 목적으로, 아카이브에 내장된 인터프리터를 표시합니다. 이 경우, 다른 옵션은 무시되고 SOURCE는 디렉터리가 아닌 아카이브여야 합니다.

-h, --help

간단한 사용법 메시지를 인쇄하고 종료합니다.

파이썬 API

이 모듈은 두 개의 편의 함수를 정의합니다:

zipapp.create_archive(source, target=None, interpreter=None, main=None, filter=None, compressed=False)

source로 응용 프로그램 아카이브를 만듭니다. 소스는 다음 중 하나일 수 있습니다:

  • 디렉터리 이름, 또는 디렉터리를 참조하는 경로류 객체. 이 경우 해당 디렉터리의 내용으로 새 응용 프로그램 아카이브가 만들어집니다.

  • 기존 응용 프로그램 아카이브 파일의 이름, 또는 이러한 파일을 참조하는 경로류 객체. 이 경우 파일이 대상(target)으로 복사됩니다 (interpreter 인자에 제공된 값을 반영하도록 수정하면서). 필요하면, 파일 이름에 .pyz 확장자가 포함되어야 합니다.

  • 바이너리 모드에서 읽기로 열린 파일 객체. 파일의 내용은 응용 프로그램 아카이브여야 하며, 파일 객체는 아카이브의 시작 부분에 위치한 것으로 가정합니다.

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

  • 파일 이름이거나 경로류 객체이면, 아카이브가 그 파일에 기록됩니다.

  • 열린 파일 객체면, 그 파일 객체에 아카이브가 기록되며, 파일은 바이너리 모드로 쓰기로 열려 있어야 합니다.

  • target이 생략되면 (또는 None이면), 소스(source)는 디렉터리여야 하며 대상은 .pyz 확장자가 추가된 소스와 이름이 같은 파일이 됩니다.

interpreter 인자는 아카이브가 실행될 파이썬 인터프리터의 이름을 지정합니다. 아카이브 시작 부분에 “셔뱅(shebang)” 줄로 기록됩니다. POSIX에서는 이를 OS가 해석하고, 윈도우에서는 파이썬 런처가 이를 처리합니다. interpreter를 생략하면 셔뱅 줄이 기록되지 않습니다. interpreter가 지정되고 target이 파일명이면, target 파일의 실행 가능 비트가 설정됩니다.

main 인자는 아카이브의 메인 프로그램으로 사용될 콜러블의 이름을 지정합니다. 소스가 디렉터리이고 소스에 아직 __main__.py 파일이 없을 때만 지정할 수 있습니다. main 인자는 “pkg.module:callable” 형식이어야 하며 아카이브는 “pkg.module”을 임포트 하고 인자 없이 지정된 콜러블을 실행해서 실행됩니다. 소스가 디렉터리이고 __main__.py 파일을 포함하지 않을 때 main을 생략하는 것은 에러입니다, 그렇지 않으면 결과 아카이브가 실행 가능하지 않기 때문입니다.

선택적 filter 인자는 추가되는 파일의 (소스 디렉터리에 상대적인) 경로를 나타내는 Path 객체가 전달되는 콜백 함수를 지정합니다. 파일을 추가하려면 True를 반환해야 합니다.

선택적 compressed 인자는 파일을 압축할지를 결정합니다. True로 설정하면, 아카이브의 파일이 디플레이트(deflate) 메서드로 압축됩니다; 그렇지 않으면 파일이 압축되지 않은 상태로 저장됩니다. 기존 아카이브를 복사할 때는 이 인자가 효과가 없습니다.

sourcetarget에 파일 객체가 지정되면, create_archive를 호출한 후 파일 객체를 닫는 것은 호출자의 책임입니다.

기존 아카이브를 복사할 때, 제공된 파일 객체에는 readreadline 또는 write 메서드만 필요합니다. 디렉터리에서 아카이브를 만들 때, 대상이 파일 객체면 zipfile.ZipFile 클래스로 전달되며, 해당 클래스에 필요한 메서드를 제공해야 합니다.

버전 3.7에 추가: filtercompressed 인자를 추가했습니다.

zipapp.get_interpreter(archive)

아카이브 시작 부분에서 #! 줄에 지정된 인터프리터를 반환합니다. #! 줄이 없으면, None을 반환합니다. archive 인자는 파일명이나 바이너리 모드로 읽기로 열린 파일류 객체일 수 있습니다. 아카이브가 시작 부분에 위치한 것으로 가정합니다.

디렉터리를 아카이브로 패킹하고, 실행합니다.

$ python -m zipapp myapp
$ python myapp.pyz
<output from myapp>

create_archive() 함수를 사용하여 같은 작업을 수행할 수 있습니다:

>>> import zipapp
>>> zipapp.create_archive('myapp', 'myapp.pyz')

POSIX에서 응용 프로그램을 직접 실행할 수 있게 만들려면, 사용할 인터프리터를 지정하십시오.

$ python -m zipapp myapp -p "/usr/bin/env python"
$ ./myapp.pyz
<output from myapp>

기존 아카이브에서 셔뱅 줄을 바꾸려면, create_archive() 함수를 사용하여 수정된 아카이브를 만드십시오:

>>> import zipapp
>>> zipapp.create_archive('old_archive.pyz', 'new_archive.pyz', '/usr/bin/python3')

파일을 제자리에서 갱신하려면, BytesIO 객체를 사용하여 메모리에서 치환을 수행한 다음, 나중에 소스를 덮어씁니다. 파일을 제자리에서 덮어쓰면 에러로 인해 원본 파일을 손실할 위험이 있음에 유의하십시오. 이 코드는 이러한 에러에 대한 보호는 없지만, 프로덕션 코드는 이를 방지해야 합니다. 또한, 이 방법은 아카이브가 메모리에 올라올 수 있을 때만 작동합니다:

>>> import zipapp
>>> import io
>>> temp = io.BytesIO()
>>> zipapp.create_archive('myapp.pyz', temp, '/usr/bin/python2')
>>> with open('myapp.pyz', 'wb') as f:
>>>     f.write(temp.getvalue())

인터프리터 지정하기

인터프리터를 지정한 다음 응용 프로그램 아카이브를 배포한다면, 사용된 인터프리터가 이식성 있는지 확인할 필요가 있음에 유의하십시오. 윈도우 용 파이썬 런처는 가장 일반적인 POSIX #! 줄 형식을 지원하지만, 고려해야 할 다른 문제가 있습니다:

  • “/usr/bin/env python”(또는 “/usr/bin/python”과 같은 “python” 명령의 다른 형식)을 사용한다면, 사용자에게 기본적으로 파이썬 2나 파이썬 3이 있을 수 있음을 고려해야 하고, 두 버전에서 작동하도록 코드를 작성하십시오.

  • “/usr/bin/env python3”과 같이 명시적인 버전을 사용하면 해당 버전이 없는 사용자에게는 응용 프로그램이 작동하지 않습니다. (여러분의 코드를 파이썬 2 호환으로 만들지 않았다면 이것이 여러분이 원하는 것일 수 있습니다).

  • “파이썬 X.Y 이상”이라고 말할 방법이 없어서, “/usr/bin/env python3.4”처럼 정확한 버전을 사용할 때는 주의하십시오. 예를 들어 파이썬 3.5 사용자를 위해서는 셔뱅 줄을 변경해야 합니다.

일반적으로, 여러분의 코드가 파이썬 2나 3중 어느 것으로 작성되었는지에 따라 “/usr/bin/env python2”나 “/usr/bin/env python3”을 사용해야 합니다.

zipapp으로 독립형 응용 프로그램 만들기

zipapp 모듈을 사용하면, 시스템에 적합한 버전의 파이썬만 설치되어있는 최종 사용자에게 배포 할 수 있는 필요한 모든 것이 담긴 파이썬 프로그램을 만들 수 있습니다. 이 작업의 핵심은 응용 프로그램 코드와 함께 모든 응용 프로그램의 종속성을 아카이브에 묶는 것입니다.

독립형 아카이브를 만드는 단계는 다음과 같습니다:

  1. 정상적으로 디렉터리에 응용 프로그램을 만드십시오, 그러면 __main__.py 파일과 모든 지원 응용 프로그램 코드를 포함하는 myapp 디렉터리를 얻게 됩니다.

  2. pip을 사용하여 모든 응용 프로그램의 종속성을 myapp 디렉터리에 설치하십시오:

    $ python -m pip install -r requirements.txt --target myapp
    

    (이것은 requirements.txt 파일에 프로젝트 요구 사항이 있다고 가정합니다 - 그렇지 않으면, pip 명령 줄에 종속성을 수동으로 나열할 수 있습니다).

  3. 선택적으로, myapp 디렉터리에 pip이 만든 .dist-info 디렉터리를 삭제합니다. 이것들은 pip이 패키지를 관리하는 데 필요한 메타 데이터를 담고 있으면, pip을 더는 사용하지 않을 것이기 때문에 필요하지 않습니다 - 남겨두더라도 아무런 해를 끼치지는 않습니다.

  4. 다음과 같이 응용 프로그램을 패키징하십시오:

    $ python -m zipapp -p "interpreter" myapp
    

그러면 독립형 실행 파일이 생성되며, 사용 가능한 적절한 인터프리터가 있는 모든 시스템에서 실행할 수 있습니다. 자세한 내용은 인터프리터 지정하기를 참조하십시오. 단일 파일로 사용자에게 제공될 수 있습니다.

유닉스에서, myapp.pyz 파일은 그대로 실행 파일입니다. “평범한” 명령 이름을 선호하면, 파일 이름을 변경하여 .pyz 확장자를 제거할 수 있습니다. 윈도우에서, 파이썬 인터프리터가 설치될 때 .pyz.pyzw 파일 확장자를 등록한다는 점에서 myapp.pyz[w] 파일은 실행 파일입니다.

윈도우 실행 파일 만들기

윈도우에서, .pyz 확장자의 등록은 선택 사항입니다, 더군다나 등록된 확장자를 “투명하게” 인식하지 못하는 특정 장소가 있습니다 (가장 간단한 예는 subprocess.run(['myapp'])이 응용 프로그램을 찾지 못하는 것입니다 - 확장자를 명시적으로 지정해야 합니다) .

따라서 윈도우에서는, 종종 zipapp에서 실행 파일을 만드는 것이 좋습니다. C 컴파일러가 필요하지만, 비교적 쉽습니다. 기본 접근법은 zip 파일의 앞에 임의의 데이터가 추가될 수 있고, 윈도우 exe 파일에는 임의의 데이터가 뒤에 추가될 수 있다는 사실에 의존합니다. 따라서 적절한 런처를 만들고 .pyz 파일을 그 끝에 붙임으로써, 응용 프로그램을 실행하는 단일 파일 실행 파일이 생깁니다.

적합한 런처는 다음처럼 간단할 수 있습니다:

#define Py_LIMITED_API 1
#include "Python.h"

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#ifdef WINDOWS
int WINAPI wWinMain(
    HINSTANCE hInstance,      /* handle to current instance */
    HINSTANCE hPrevInstance,  /* handle to previous instance */
    LPWSTR lpCmdLine,         /* pointer to command line */
    int nCmdShow              /* show state of window */
)
#else
int wmain()
#endif
{
    wchar_t **myargv = _alloca((__argc + 1) * sizeof(wchar_t*));
    myargv[0] = __wargv[0];
    memcpy(myargv + 1, __wargv, __argc * sizeof(wchar_t *));
    return Py_Main(__argc+1, myargv);
}

WINDOWS 전처리기 기호를 정의하면, GUI 실행 파일을 만들고, 그렇지 않으면 콘솔 실행 파일을 만듭니다.

실행 파일을 컴파일하려면, 그냥 표준 MSVC 명령 줄 도구를 사용하거나 distutils가 파이썬 소스를 컴파일하는 방법을 알고 있다는 사실을 활용할 수 있습니다:

>>> from distutils.ccompiler import new_compiler
>>> import distutils.sysconfig
>>> import sys
>>> import os
>>> from pathlib import Path

>>> def compile(src):
>>>     src = Path(src)
>>>     cc = new_compiler()
>>>     exe = src.stem
>>>     cc.add_include_dir(distutils.sysconfig.get_python_inc())
>>>     cc.add_library_dir(os.path.join(sys.base_exec_prefix, 'libs'))
>>>     # First the CLI executable
>>>     objs = cc.compile([str(src)])
>>>     cc.link_executable(objs, exe)
>>>     # Now the GUI executable
>>>     cc.define_macro('WINDOWS')
>>>     objs = cc.compile([str(src)])
>>>     cc.link_executable(objs, exe + 'w')

>>> if __name__ == "__main__":
>>>     compile("zastub.c")

결과 런처는 “제한된 ABI”를 사용해서, 모든 버전의 파이썬 3.x에서 변경 없이 실행됩니다. 파이썬(python3.dll)이 사용자의 PATH에 있기만 하면 됩니다.

완전 독립형 배포를 위해서는, 응용 프로그램을 덧붙인 런처와 함께 파이썬 “내장(embedded)” 배포를 번들로 배포할 수 있습니다. 적절한 아키텍처(32비트나 64비트)를 갖는 모든 PC에서 실행됩니다.

경고

응용 프로그램을 단일 파일로 묶는 과정에는 몇 가지 제한이 있습니다. 전부는 아니더라도 대부분의 경우 응용 프로그램을 크게 변경하지 않고도 해결할 수 있습니다.

  1. 응용 프로그램이 C 확장을 포함하는 패키지에 의존하면, 해당 패키지는 zip 파일에서 실행할 수 없습니다 (이것은 OS 제한 사항인데, OS 로더가 로드 할 수 있으려면 실행 코드는 파일 시스템에 있어야만 합니다). 이 경우, zip 파일에서 해당 종속성을 제외하고, 사용자가 파일을 설치하도록 요구하거나, zip 파일과 함께 제공하고 __main__.py에 코드를 추가하여 sys.path에 압축 해제된 모듈을 포함하는 디렉터리를 포함할 수 있습니다. 이 경우, 대상 아키텍처에 적합한 바이너리를 제공해야 합니다 (그리고 아마도 사용자 컴퓨터를 기반으로 실행 시간에 sys.path에 추가할 올바른 버전을 선택해야 합니다).

  2. 위에서 설명한 대로 윈도우 실행 파일을 제공하는 경우, 사용자의 PATH에 python3.dll이 있는지 (설치 프로그램의 기본 동작이 아님) 확인하거나 응용 프로그램과 함께 내장 배포를 번들로 제공해야 합니다.

  3. 위에서 제안한 런처는 파이썬 내장(embedding) API를 사용합니다. 이는 여러분의 응용 프로그램에서, sys.executable이 일반적인 파이썬 인터프리터가 아니라 여러분의 응용 프로그램이 된다는 뜻입니다. 코드와 종속성은 이 가능성에 대비해야 합니다. 예를 들어, 응용 프로그램에서 multiprocessing 모듈을 사용하면, 표준 파이썬 인터프리터를 찾을 위치를 모듈에 알리기 위해 multiprocessing.set_executable()을 호출해야 합니다.

파이썬 Zip 응용 프로그램 아카이브 형식

파이썬은 버전 2.6부터 __main__.py 파일이 포함된 zip 파일을 실행할 수 있었습니다. 파이썬에서 실행되려면, 응용 프로그램 아카이브는 응용 프로그램의 진입점으로 실행될 __main__.py 파일이 포함된 표준 zip 파일이어야 합니다. 모든 파이썬 스크립트와 마찬가지로, 스크립트의 부모(이 경우 zip 파일)가 sys.path에 배치되므로 zip 파일에서 추가 모듈을 임포트 할 수 있습니다.

zip 파일 형식은 임의의 데이터를 zip 파일 앞에 추가할 수 있도록 합니다. zip 응용 프로그램 형식은 이 기능을 사용하여 표준 POSIX “셔뱅(shebang)” 줄을 파일 앞에 추가합니다 (#!/path/to/interpreter).

따라서 공식적으로 파이썬 zip 응용 프로그램 형식은 다음과 같습니다:

  1. 문자 b'#!', 인터프리터 이름, 개행 (b'\n') 문자를 포함하는 선택적 셔뱅(shebang) 줄. 인터프리터 이름은 OS “셔뱅” 처리가 허용하는 모든 것, 또는 윈도우의 파이썬 런처일 수 있습니다. 인터프리터는 윈도우에서는 UTF-8로, POSIX에서는 sys.getfilesystemencoding()으로 인코딩되어야 합니다.

  2. zipfile 모듈에 의해 생성된 표준 zip 파일 데이터. zipfile 내용은 반드시 __main__.py라는 파일을 포함해야 합니다 (zip 파일의 “루트”에 있어야 합니다 - 즉, 서브 디렉터리에 있을 수 없습니다). zip 파일 데이터는 압축되거나 그렇지 않을 수 있습니다.

응용 프로그램 아카이브에 셔뱅 줄이 있으면, POSIX 시스템에서 직접 실행될 수 있도록 실행 파일 비트를 설정할 수 있습니다.

이 모듈의 도구를 사용하여 응용 프로그램 아카이브를 만들 필요는 없습니다 - 모듈은 편의를 위한 것입니다. 하지만 어떤 방법으로 만들었든 위 형식의 아카이브는 파이썬에서 허용됩니다.