zipapp — 管理可執行的 Python zip 歸檔檔案

在 3.5 版本中新增。

原始碼: Lib/zipapp.py


此模組提供了用於管理包含 Python 程式碼的 zip 檔案建立的工具,這些 zip 檔案可以被 Python 直譯器直接執行。該模組提供了一個 命令列介面 和一個 Python API

基本示例

以下示例顯示瞭如何使用 命令列介面 從包含 Python 程式碼的目錄建立可執行歸檔檔案。執行時,該歸檔檔案將執行歸檔檔案中模組 myappmain 函式。

$ 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>

向歸檔檔案寫入一個 __main__.py 檔案,該檔案執行 mainfnmainfn 引數應具有 “pkg.mod:fn” 的形式,其中 “pkg.mod” 是歸檔檔案中的包/模組,“fn” 是給定模組中的可呼叫物件。 __main__.py 檔案將執行該可呼叫物件。

複製歸檔檔案時,不能指定 --main

-c, --compress

使用 deflate 方法壓縮檔案,減小輸出檔案的大小。預設情況下,檔案在歸檔檔案中以未壓縮方式儲存。

複製歸檔檔案時,--compress 無效。

在 3.7 版本中新增。

--info

顯示嵌入在歸檔檔案中的直譯器,用於診斷目的。在這種情況下,任何其他選項都將被忽略,並且 SOURCE 必須是歸檔檔案,而不是目錄。

-h, --help

列印簡短的使用訊息並退出。

Python API

該模組定義了兩個便捷函式

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

source 建立應用程式歸檔檔案。源可以是以下任何一種

  • 目錄的名稱,或引用目錄的 類路徑物件,在這種情況下,將從該目錄的內容建立新的應用程式歸檔檔案。

  • 現有應用程式歸檔檔案的名稱,或引用此類檔案的 類路徑物件,在這種情況下,該檔案將被複制到目標(修改它以反映為 interpreter 引數給出的值)。檔名應包括 .pyz 副檔名(如果需要)。

  • 以位元組模式開啟以進行讀取的檔案物件。該檔案的內容應為應用程式歸檔檔案,並且假定檔案物件位於歸檔檔案的開頭。

target 引數確定將寫入結果歸檔檔案的位置

  • 如果它是檔案的名稱,或 類路徑物件,則歸檔檔案將被寫入該檔案。

  • 如果它是一個開啟的檔案物件,則歸檔檔案將被寫入該檔案物件,該檔案物件必須以位元組模式開啟以進行寫入。

  • 如果省略目標(或 None),則源必須是一個目錄,並且目標將是與源同名的檔案,並新增 .pyz 副檔名。

interpreter 引數指定將執行歸檔檔案的 Python 直譯器的名稱。它作為 “shebang” 行寫入歸檔檔案的開頭。在 POSIX 上,這將由作業系統解釋,在 Windows 上,它將由 Python 啟動器處理。省略 interpreter 會導致不寫入 shebang 行。如果指定了直譯器,並且目標是檔名,則將設定目標檔案的可執行位。

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)

返回歸檔檔案開頭 #! 行中指定的直譯器。如果沒有 #! 行,則返回 Nonearchive 引數可以是檔名或以位元組模式開啟以進行讀取的類檔案物件。假設它位於歸檔檔案的開頭。

示例

將一個目錄打包到歸檔檔案中,並執行它。

$ 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>

要替換現有歸檔檔案上的 shebang 行,請使用 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())

指定直譯器

請注意,如果指定直譯器然後分發應用程式歸檔檔案,則需要確保所用的直譯器是可移植的。Windows 的 Python 啟動器支援 POSIX #! 行的大多數常見形式,但還有其他問題需要考慮

  • 如果使用“/usr/bin/env python”(或“python”命令的其他形式,如“/usr/bin/python”),則需要考慮使用者可能將 Python 2 或 Python 3 作為其預設版本,並編寫程式碼以在兩個版本下工作。

  • 如果使用顯式版本,例如“/usr/bin/env python3”,則應用程式將無法在沒有該版本的使用者上執行。(如果您的程式碼不相容 Python 2,這可能是您想要的)。

  • 沒有辦法說“python X.Y 或更高版本”,因此請注意不要使用像“/usr/bin/env python3.4”這樣的精確版本,因為例如,對於 Python 3.5 的使用者,您需要更改 shebang 行。

通常,您應該使用“/usr/bin/env python2”或“/usr/bin/env python3”,具體取決於您的程式碼是為 Python 2 還是 3 編寫的。

使用 zipapp 建立獨立應用程式

使用 zipapp 模組,可以建立自包含的 Python 程式,這些程式可以分發給只需要在其系統上安裝合適的 Python 版本的終端使用者。這樣做的關鍵是將應用程式的所有依賴項與應用程式程式碼一起捆綁到歸檔檔案中。

建立獨立歸檔檔案的步驟如下

  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
    

這將生成一個獨立的exe檔案,該檔案可以在任何具有可用相應直譯器的計算機上執行。有關詳細資訊,請參閱 指定直譯器。它可以作為單個檔案傳送給使用者。

在 Unix 上,myapp.pyz 檔案本身是可執行的。如果希望使用“普通”命令名,可以重新命名檔案以刪除 .pyz 副檔名。在 Windows 上,由於 Python 直譯器在安裝時註冊了 .pyz.pyzw 副檔名,因此 myapp.pyz[w] 檔案是可執行的。

注意事項

如果您的應用程式依賴於包含 C 擴充套件的軟體包,則無法從 zip 檔案執行該軟體包(這是一個作業系統限制,因為可執行程式碼必須存在於檔案系統中才能供作業系統載入程式載入)。在這種情況下,您可以將該依賴項從 zip 檔案中排除,並要求使用者安裝它,或者將其與 zip 檔案一起傳送,並在 __main__.py 中新增程式碼以將包含未壓縮模組的目錄包含到 sys.path 中。在這種情況下,您需要確保為目標架構傳送適當的二進位制檔案(並可能選擇要新增到 sys.path 的正確版本,基於使用者的機器)。

Python Zip 應用程式歸檔檔案格式

自 2.6 版本以來,Python 就可以執行包含 __main__.py 檔案的 zip 檔案。為了讓 Python 執行,應用程式歸檔檔案只需是一個包含 __main__.py 檔案的標準 zip 檔案,該檔案將作為應用程式的入口點執行。與任何 Python 指令碼一樣,指令碼的父級(在這種情況下為 zip 檔案)將被放置在 sys.path 上,因此可以從 zip 檔案匯入其他模組。

zip 檔案格式允許在 zip 檔案前新增任意資料。zip 應用程式格式使用此功能在檔案前新增一個標準的 POSIX “shebang” 行(#!/path/to/interpreter)。

因此,正式的 Python zip 應用程式格式為

  1. 一個可選的 shebang 行,包含字元 b'#!',後跟一個直譯器名稱,然後是一個換行符(b'\n')字元。直譯器名稱可以是作業系統 “shebang” 處理或 Windows 上的 Python 啟動器接受的任何名稱。直譯器在 Windows 上應以 UTF-8 編碼,在 POSIX 上應以 sys.getfilesystemencoding() 編碼。

  2. 標準 zip 檔案資料,由 zipfile 模組生成。zip 檔案內容*必須*包含一個名為 __main__.py 的檔案(該檔案必須位於 zip 檔案的“根”目錄中 - 即,它不能位於子目錄中)。zip 檔案資料可以壓縮或未壓縮。

如果應用程式歸檔檔案具有 shebang 行,則可以在 POSIX 系統上設定可執行位,以允許直接執行它。

不需要使用此模組中的工具來建立應用程式歸檔檔案 - 該模組只是為了方便,但透過任何方式建立的上述格式的歸檔檔案都是 Python 可以接受的。