封裝打包Python腳本

一、前言

封裝打包Python的好處,節省了安裝各類各樣包依賴的問題,同時能夠增強咱們代碼隱私的安全性,這裏個人演示環境是Python3.6 ,CentOS7的系統,同時打包工具採用pyinstaller。python

二、環境準備

2.1 Python共享so模塊

默認Python模塊是私有的,咱們想打包就須要將咱們的so模塊變爲共享的,那麼咱們須要執行兩個操做便可。linux

  1. 從新編譯Python,加入編譯參數 --enable-shared
  2. 將so共享庫加入到系統之中
[root@c7-work-1 ~]# cat /etc/ld.so.conf.d/python3.6.conf   
/usr/local/python3.6.5-shared/lib
[root@c7-work-1 ~]# ldconfig

完成上面操做便可了。安全

2.2 安裝pyinstaller

pip install pyinstaller

2.3 準備工程

這裏咱們只須要準備工程和相關依賴包,而且安裝成功便可,程序能正常跑則沒問題。bash

這裏根據本身經驗來便可了,沒啥技巧。編輯器

三、代碼調整

默認狀況下,咱們打包後有些代碼原有的獲取方式會有些轉變。工具

3.1 運行時的環境

當應用程序運行時,可能須要訪問如下三個常規位置中的任何位置的數據文件:測試

  • 捆綁在一塊兒的數據文件;
  • 用戶的配置文件;
  • 用戶工做目錄中的文件

在Python中咱們須要注意提防以下:ui

P1: 使用__file__sys._MEIPASSspa

當程序未凍結時,標準Python變量__file__是如今正在執行的腳本的完整路徑。當捆綁的應用程序啓動時,引導加載程序會設置sys.frozen屬性並將絕對路徑存儲到bundle文件夾中sys._MEIPASS。對於單文件夾捆綁包,這是該文件夾的路徑,不管用戶在哪裏放置它。對於單文件包,這是 引導加載程序建立的臨時文件夾的路徑(請參閱單文件程序的工做原理)。_MEIxxxxxx命令行

P2: 使用sys.executablesys.argv[0]

當正常的Python腳本運行時,sys.executable是執行程序的路徑,即Python解釋器。在一個凍結的應用sys.executable程序中,也是執行程序的路徑,但這不是Python; 它是單文件應用程序中的引導加載程序或單文件夾應用程序中的可執行文件。這爲您提供了一種可靠的方法來查找用戶實際啓動的凍結可執行文件。

sys.argv[0]是用戶命令中使用的名稱或相對路徑。它多是相對路徑或絕對路徑,具體取決於平臺以及應用程序的啓動方式。

下面是一個演示代碼,有興趣能夠執行下看看效果:

#!/usr/bin/python3
import sys, os
frozen = 'not'
if getattr(sys, 'frozen', False):
        # we are running in a bundle
        frozen = 'ever so'
        bundle_dir = sys._MEIPASS
else:
        # we are running in a normal Python environment
        bundle_dir = os.path.dirname(os.path.abspath(__file__))
print( 'we are',frozen,'frozen')
print( 'bundle dir is', bundle_dir )
print( 'sys.argv[0] is', sys.argv[0] )
print( 'sys.executable is', sys.executable )
print( 'os.getcwd is', os.getcwd() )

3.2 調整項目代碼

根據上面的規則,着重調整一下代碼。

原代碼:

BASE_PATH = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))

New代碼:

if getattr(sys, 'frozen', False):
    BASE_PATH = sys._MEIPASS
else:
    BASE_PATH = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))

只須要啓動入口腳本調整便可,其餘腳本無需調整

四、打包封裝

打包:

pyinstaller thunder_predict.spec

如何生成 *.spec文件呢,能夠這裏直接pyinstaller pyscript便可,而後在修改調整;下面提供個人實例文件:

# -*- mode: python -*-

block_cipher = None


a = Analysis(['predict/thunder_predict.py'],
             pathex=['.'],
             binaries=[],
             datas=[('model', 'model'), ('dataset', 'dataset')],
             hiddenimports=['cython','sklearn','sklearn.ensemble','sklearn.neighbors.typedefs',
                            'sklearn.neighbors.quad_tree','sklearn.tree._utils','scipy._lib.messagestream'],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          [],
          exclude_binaries=True,
          name='thunder_predict',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          console=True )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               name='thunder_predict')

五、打包So文件庫

咱們爲何要打包成爲so文件呢?

Python有py、pyc、pyw、pyo、pyd等文件格式。

其中,pyc是二進制文件。但很容易被反編譯。

pyw也不行,只是隱藏命令行界面而已,能夠做爲入口腳本。

pyo和pyc差很少,也容易被反編譯。

最後剩下pyd格式。pyd格式是D語言(C/C++綜合進化版本)生成的二進制文件,實際也會是dll文件。該文件目前位置沒找到能夠被反編譯的消息,只能被反彙編。Sublime text編輯器也是使用該格式。

5.1 打包So模塊

打包生成so文件:

python3 ../utils/build_so.py build_ext --inplace

清理無用的文件:

rm -fr build main.c main.py

測試(成功測試成功):

[root@c7-work-1 thunder]# python3 predict/thunder_predict.py 01:dXBsb2FkRGF0YTE1MzI2Nzk4MzQzNDI= 199
success

Cython腳本內容:

from distutils.core import setup
from Cython.Build import cythonize

"""
python build_so.py build_ext --inplace
"""

setup(
  name = 'main',
  ext_modules = cythonize("main.py"),
)

5.2 從新打包整個

命令以下:

[root@c7-work-1 thunder]# pyinstaller thunder_predict.spec 
36 INFO: PyInstaller: 3.4
37 INFO: Python: 3.6.5
................ 省略一下信息

咱們檢查一下是否已經將咱們的So庫文件打包。

[root@c7-work-1 thunder]# ls dist/thunder_predict/main.cpython-36m-x86_64-linux-gnu.so 
dist/thunder_predict/main.cpython-36m-x86_64-linux-gnu.so

完美。

5.3 完美之餘有些略顯通俗的坑

Q: pyinstaller利用獲取腳本中import來來打包對應的庫,若是你打包成so文件了還能夠?

A: 這裏我經過Python PKG的方式,將so會差異的庫放入到__init__.py中,就能夠解決這個問題。

相關文章
相關標籤/搜索