原程式執行良好,Pyinstaller封裝後執行出錯 的分析

阿薛發表於2019-02-16

前言

昨天決定分享一下最近寫的exhentai爬蟲程式,參考了這篇文章,看了下里面幾個常見打包軟體的簡介表格(可惜沒nuitka)
因為是給小白使用者使用,做成單個檔案形式,只能在Pyinstaller和py2exe之間選擇
去各自官網看了下,發現py2exe很久沒更新了,對python3新版本的支援也不是太好,就決定用Pyinstaller來封裝/打包
這裡略過Pyinstaller的安裝和使用,重點說明症狀,分析過程,解決辦法和教訓

症狀

  1. 打包成功

  2. 執行程式時出現如下報錯

C:UsersxoxoPycharmProjectsHentai_Spider>main
Traceback (most recent call last):
  File "main.py", line 6, in <module>
    from config import args
  File "<frozen importlib._bootstrap>", line 2237, in _find_and_load
  File "<frozen importlib._bootstrap>", line 2226, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 1200, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 1129, in _exec
  File "C:Python34libsite-packagesPyInstallerloaderpyimod03_importers.py",
 line 631, in exec_module
    exec(bytecode, module.__dict__)
  File "config.py", line 11, in <module>
    parser.add_argument(`-sd`, `--savedir`, default=config[`ARGS`][`SAVEDIR`])
  File "configparser.py", line 936, in __getitem__
KeyError: `ARGS`
[7836] Failed to execute script main

C:UsersxoxoPycharmProjectsHentai_Spider>

分析過程

0 . StackOverflow搜了下發現沒啥經驗可供參考……
1 . main.py程式是完全可以跑通的,從未出現過這個報錯,所以確定問題出在封裝過程中
2 . 報錯資訊上看,是(封裝後的程式)執行config.py時出了問題
3 . 通過控制變數排除法(過程比較繁瑣,不一一列舉),確定是在讀取config.ini時出的錯,把賦值config_path這裡改成確定的路徑即可正常執行

程式碼如下:

config = configparser.ConfigParser()
config_path = os.path.join(os.path.dirname(__file__), CONFIG_FILE)
config.read(config_path)

順便解釋一下,我的設計意圖是讓config.py去讀取其所在目錄下的配置檔案,main.py和config.py在同一目錄

4 . 由於CONFIG_FILE是常量(檔名),那最終就懷疑到os.path.dirname(__file__)部分
5 . 在賦值config_path =這句後加上print(os.path.dirname(__file__),看看封裝後這裡的file會到哪個目錄,發現變成了C:UsersxoxoAppDataLocalTemp\_MEI92882

C:UsersxoxoPycharmProjectsHentai_Spider>main
C:UsersxoxoAppDataLocalTemp\_MEI92882
Traceback (most recent call last):
  File "main.py", line 6, in <module>
    from config import args
  File "<frozen importlib._bootstrap>", line 2237, in _find_and_load
  File "<frozen importlib._bootstrap>", line 2226, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 1200, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 1129, in _exec
  File "C:Python34libsite-packagesPyInstallerloaderpyimod03_importers.py",
 line 631, in exec_module
    exec(bytecode, module.__dict__)
  File "config.py", line 12, in <module>
    parser.add_argument(`-sd`, `--savedir`, default=config[`ARGS`][`SAVEDIR`])
  File "configparser.py", line 936, in __getitem__
KeyError: `ARGS`
[9296] Failed to execute script main

C:UsersxoxoPycharmProjectsHentai_Spider>

6 . 可以看到config.py雖然封裝前和main.py在一個資料夾下,但被封裝後,執行時就處於臨時資料夾,這時候取其自身所在目錄的動作就有問題

解決方案

main.py來抓目錄資訊,再傳給config.py(中函式)

教訓

  1. 封裝程式沒有聰明到能理解設計意圖,所以寫程式碼的得更聰明點

  2. 如果某專案可能會被封裝,讓被封裝的那個程式來抓取目錄資訊並傳遞

相關文章