1. 程式人生 > >Python3+PyInstall+Sciter的坎坷除錯路(報錯缺少dll、html等檔案)

Python3+PyInstall+Sciter的坎坷除錯路(報錯缺少dll、html等檔案)

1 除錯過程

用Python3.6+Sciter+PyCharm寫了一個py測試指令碼helloworld.py,該指令碼中只含有一條語句“import sciter”。在PyCharm中執行之,未報錯。

#helloworld.py

import sciter

然後將該指令碼用PyInstaller打包成exe檔案,打包程式pack.py如下:

#pack.py

from PyInstaller.__main__ import run

if __name__ == '__main__':
    opts = [
            # 字串前加“r”,防止字元轉義
            # r'--hidden-import=py4j.java_collections',\
            # 要打包的Python檔案
            r'D:\work\python\my_editor\helloworld\helloworld.py',\
            # -F, –onefile	打包一個單個檔案,如果你的程式碼都寫在一個.py檔案的話,可以用這個,如果是多個.py檔案就別用
            # '-F',\
            # -w:製作視窗程式,與之相對的-c意味製作命令列程式(預設)。--distpath指定打包的目的地路徑
            r'-w','--distpath=D:\work\python\my_editor\helloworld\dist',\
            # --workpath 指定工作路徑
            r'--workpath=D:\work\python\my_editor\helloworld\out',\
            # --specpath指定.spec檔案儲存路徑
            r'--specpath=D:\work\python\my_editor\helloworld\out',\
            # --icon 指定exe檔案的圖示
            r'--icon=D:\work\python\my_editor\ico\text.ico',\
            # --upx-dir 使用upx壓縮
            r'--upx-dir','upx393w',\
            # --add-data 指定要包含的資原始檔。如下,“C:\\Windows\\System32\\sciter.dll”為資原始檔原本所在路徑(source),“.”為相對於產生的exe檔案的路徑(destination)。在這裡“.”為同一目錄的意思。
            # r'--add-data', 'C:\\Windows\\System32\\sciter.dll;.', \
            # r'--add-data', 'D:\\work\\python\\my_editor\\helloworld\\pages\\word.html;pages'
            ]
    run(opts)

打包過程未報錯,但打包後執行exe檔案就出現問題了,如下圖,報錯“Failed to execute script helloworld”。

就這麼一句話,沒有其他任何的報錯資訊,很令人惱火。後來查了資料,發現在打包時,可以指定exe以命令列模式執行,就能看到報錯資訊,而非以當前的視窗模式執行。即將上述pack.py指令碼中的opts選項列表裡的“-w”改為“-c”。

再次打包執行exe檔案,發現了彈出了一個含有報錯資訊的視窗。但是視窗一閃即逝,根本看不清裡面的報錯資訊。這是因為程式丟擲異常退出了;即使不丟擲異常,視窗也會因程式執行完成而消失。於是將主程式helloworld.py的程式碼改為如下:

#helloworld.py

import traceback

#加上try-except語句,是為了防止程式在出現異常時奔潰退出。
try:
    import sciter

except Exception as e:
    #列印異常棧軌跡。
    traceback.print_exc()
    #使程式阻塞,防止程式執行完畢退出。
    a = input()

再次打包執行exe檔案,能看到報錯資訊了,如下圖。其中的核心資訊是“ImportError: C:\Windows\System32\sciter.dll was not found in PATH”,即缺少sciter.dll檔案。

這就很奇怪了,在PyCharm中執行時沒報這個錯啊!檢視PyInstaller官網,得知PyInstaller打包後的exe程式不能自動識別資原始檔(如dll、html、png等檔案)的路徑,需要在spec檔案中指定datas選項。比如下面的helloworld.spec片段,添加了sciter.dll檔案到與exe檔案同級的目錄(.)中;添加了word.html到比exe檔案所在目錄更深一層的目錄(pages)中(若是隻報錯缺少dll檔案,可不指定html檔案)。

#helloworld.spec片段

a = Analysis(['D:\\work\\python\\my_editor\\helloworld\\helloworld.py'],
             pathex=['D:\\work\\python\\my_editor\\helloworld\\out'],
             binaries=[],
             datas=[('C:\\Windows\\System32\\sciter.dll', '.'), ('D:\\work\\python\\my_editor\\helloworld\\pages\\word.html', 'pages')],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)

但每次執行完打包程式後,還要手動修改spec檔案,很是麻煩。通過除錯PyInstaller原始碼,找到了另一種方法,即在pack.py打包程式中的opts選項列表里加上兩個“--add-data選項”,如下:

#helloworld.py片段

opts = [
        """
        其他選項與上面的helloworld.py同,此處省略。
        """

        # --add-data 指定要包含的資原始檔。
        # “C:\\Windows\\System32\\sciter.dll”為資原始檔原本所在路徑(source)。
        # “.”為相對於exe檔案的路徑(destination),在這裡“.”為同一目錄的意思。
        # source路徑與destination路徑以英文狀態下分號“;”隔開。
        r'--add-data', 'C:\\Windows\\System32\\sciter.dll;.', \
        r'--add-data', 'D:\\work\\python\\my_editor\\helloworld\\pages\\word.html;pages'
       ]

再次打包執行exe檔案,執行成功!

2 要點總結

(1)除錯打包時,指定PyInstaller打包程式引數為“-c”(命令列模式),方便閱讀報錯資訊。等到除錯完成正式上線後再改為“-w”。

(2)通過捕捉異常和使程式阻塞,防止程式過早退出,以便於看清楚報錯資訊。

(3)PyInstaller打包後的exe程式不能自動識別資原始檔(如dll、html、png等檔案)的路徑,需要在spec檔案中指定datas選項,或者在PyInstaller打包程式引數中指定一個或多個“--add-data”選項。