From Python Scripts to Windows Executable with cx_Freeze: 問題集

最近因為要把以前寫的東西打包成執行檔給同事使用,遇到了滿多問題,因此記錄在這邊。不得不說打包Python腳本這件事真的是很一波三折啊,希望這份記錄能夠減少同樣遇到問題的人們打轉的時間。

開發環境為 Windows 64-bit, Python 3.6 Anaconda。如果你是一開始就打定主意開發可執行檔,建議開個空白虛擬環境然後只安裝需要的 package 即可,避免最後打包時引入不必要的 package 導致檔案過大。

這次我選擇的是 cx_Freeze,但其他還有很多能夠將 Python 打包的工具,其比較和簡易的使用方法可以參考這篇[1]

下面是打包過程中遇到的錯誤和解決方法,提供參考:

KeyError: ‘TCL_Library’

因使用 Python 3.5 以上版本所導致。在 setup script 中 setup() 前設定環境變數即可[2][6]:

import os
os.environ['TCL_LIBRARY'] = r'Anaconda(Python)路徑\tcl\tcl8.6'
os.environ['TK_LIBRARY'] = r'Anaconda(Python)路徑\tcl\tk8.6'

ImportError: DLL load failed

Anaconda\DLLs 中找到 tcl86t.dlltk86t.dll ,手動移入 build 好的資料夾,或是在 setup script 中設定包含[2][7]:

include_files = [r'Anaconda(Python)路徑\DLLs\tcl86t.dll', r'Anaconda(Python)路徑\DLLs\tk86t.dll']
build_exe_options = {'include_files': include_files}
setup(...,
options={'build_exe': build_exe_options})

ModuleNotFoundError: No module named multiprocessing.pool

cx_Freeze在編譯時的檔案命名方式與原本程式碼中的命名相異[2],將lib\multiprocessing 裡的 Pool.pyc 手動改為小寫 pool.pyc 即可。或是設定重新命名亦可[3]:

os.rename('檔案位置\Pool.pyc', '檔案位置\pool.pyc')

ImportError: No module named ‘scipy.spatial.ckdtree’

與上面的問題相同,將 lib\scipy\spatial 中的 cKDTree.cp36-win_amd64.pyd 改為小寫 ckdtree.cp36-win_amd64.pyd 即可。

OSError: Could not get source code

inspect.py 沒辦法讀取到程式碼回傳,回溯後發現 inspect.py 要求的是 .py 檔,但打包後都是 .pyc 檔。找到原本所需的程式碼並手動移入對應的模組資料夾即可。我的情況是缺少了 lib\pycparser\plyparser.pylib\pycparser\c_parser.py

Module ‘lxml.etree’ has no attribute ‘fromstring’

引入 pdfminer 時產生的錯誤,懷疑是 lxml 沒有被完全包進去,另外指定引入就可以解決。

build_exe_options = {'packages': ['pdfminer', 'lxml']}
setup(...,
options={'build_exe': build_exe_options})

OSError: Cannot load native module ‘Crypto.Cipher._raw_ecb’

參考[5],解除安裝 pycryptodome 即可。

使用Multiprocessing

若有引進 multiprocessing 模組,在主檔案處需有以下語句[8]:

if __name__ == '__main__':
sys.stdout = io.StringIO() # 重新定向stdout和stderr
sys.stderr = io.StringIO() # 這兩句要在前面
multiprocessing.freeze_support() # 避免Runtime Error

希望大家順利解決問題!

Reference:

  1. https://blog.csdn.net/vivisoul/article/details/79510453
  2. https://zhuanlan.zhihu.com/p/27229694
  3. https://github.com/anthony-tuininga/cx_Freeze/issues/353
  4. https://cx-freeze.readthedocs.io/en/latest/distutils.html#distutils
  5. https://blog.csdn.net/u010664527/article/details/81094096
  6. https://stackoverflow.com/questions/35533803/keyerror-tcl-library-when-i-use-cx-freeze
  7. https://stackoverflow.com/questions/42323533/when-using-cx-freeze-and-tkinter-i-get-dll-load-failed-the-specified-module-c
  8. https://codertw.com/%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80/369599/
  9. https://jingsam.github.io/2015/12/31/multiprocessing.html