# `zipapp` — Manage executable Python zip archives

버전 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가 아카이브이면 반드시 출력 파일명을 지정해야 합니다 (그리고 이 경우, output은 source와 달라야 합니다).

-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) 메서드로 압축됩니다; 그렇지 않으면 파일이 압축되지 않은 상태로 저장됩니다. 기존 아카이브를 복사할 때는 이 인자가 효과가 없습니다.

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

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

  버전 3.7에서 변경: filter와 compressed 매개 변수를 추가했습니다.

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. 다음과 같이 응용 프로그램을 패키징하십시오:

       $ python -m zipapp -p "interpreter" myapp

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

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

### Making a Windows executable

On Windows, registration of the `.pyz` extension is optional, and furthermore, there are certain places that don’t recognise registered extensions “transparently” (the simplest example is that `subprocess.run(['myapp'])` won’t find your application - you need to explicitly specify the extension).

On Windows, therefore, it is often preferable to create an executable from the zipapp. This is relatively easy, although it does require a C compiler. The basic approach relies on the fact that zipfiles can have arbitrary data prepended, and Windows exe files can have arbitrary data appended. So by creating a suitable launcher and tacking the `.pyz` file onto the end of it, you end up with a single-file executable that runs your application.

A suitable launcher can be as simple as the following:

    #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);
    }

If you define the `WINDOWS` preprocessor symbol, this will generate a GUI executable, and without it, a console executable.

To compile the executable, you can either just use the standard MSVC command line tools, or you can take advantage of the fact that distutils knows how to compile Python source:

    >>> 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")

The resulting launcher uses the “Limited ABI”, so it will run unchanged with any version of Python 3.x. All it needs is for Python (`python3.dll`) to be on the user’s `PATH`.

For a fully standalone distribution, you can distribute the launcher with your application appended, bundled with the Python “embedded” distribution. This will run on any PC with the appropriate architecture (32 bit or 64 bit).

### 경고

There are some limitations to the process of bundling your application into a single file. In most, if not all, cases they can be addressed without needing major changes to your application.

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

2. If you are shipping a Windows executable as described above, you either need to ensure that your users have `python3.dll` on their PATH (which is not the default behaviour of the installer) or you should bundle your application with the embedded distribution.

3. The suggested launcher above uses the Python embedding API. This means that in your application, `sys.executable` will be your application, and not a conventional Python interpreter. Your code and its dependencies need to be prepared for this possibility. For example, if your application uses the `multiprocessing` module, it will need to call `multiprocessing.set_executable()` to let the module know where to find the standard Python interpreter.

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

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

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

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

1. An optional shebang line, containing the characters `b'#!'` followed by an interpreter name, and then a newline (`b'\n'`) character. The interpreter name can be anything acceptable to the OS “shebang” processing, or the Python launcher on Windows. The interpreter should be encoded in UTF-8 on Windows, and in `sys.getfilesystemencoding()` on POSIX.

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

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

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