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

在 3.5 版本加入。

原始碼: Lib/zipapp.py


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

基本示例

以下示例展示瞭如何使用命令列介面從包含 Python 程式碼的目錄建立可執行歸檔。執行時,歸檔將執行歸檔中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>

向歸檔寫入一個 __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 後關閉它。

複製現有歸檔時,提供的檔案物件只需具有 readreadlinewrite 方法。從目錄建立歸檔時,如果目標是檔案物件,它將傳遞給 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 上,myapp.pyz[w] 檔案之所以可執行,是因為 Python 直譯器在安裝時註冊了 .pyz.pyzw 副檔名。

注意事項

如果您的應用程式依賴於包含 C 擴充套件的包,則該包無法從 zip 檔案中執行(這是作業系統的限制,因為可執行程式碼必須存在於檔案系統中才能由作業系統載入器載入)。在這種情況下,您可以將該依賴項從 zipfile 中排除,並要求您的使用者安裝它,或者將其與 zipfile 一起分發,並在您的 __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. 標準 zipfile 資料,由 zipfile 模組生成。zipfile 內容 必須 包含一個名為 __main__.py 的檔案(該檔案必須位於 zipfile 的“根”目錄中 - 即,它不能位於子目錄中)。zipfile 資料可以壓縮或未壓縮。

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

不要求使用此模組中的工具來建立應用程式歸檔 - 該模組只是一個便利工具,但以任何方式建立的上述格式的歸檔都可被 Python 接受。