MaxCompute支援您在Python UDF中引用第三方包,例如Numpy包、需要編譯的第三方包或依賴動態連結程式庫的第三方包。本文為您介紹如何通過Python UDF引用第三方包。
背景資訊
通過Python UDF使用第三方包支援的情境如下:
您需要修改Numpy包的尾碼格式,基於MaxCompute用戶端上傳Numpy包,並註冊函數。函數註冊成功後即可通過Python 3 UDF調用。
您需要在與MaxCompute相容的環境下,對第三方資源套件中的setup.py指令碼進行編譯產生WHEEL包,並修改尾碼格式。基於MaxCompute用戶端上傳包,並註冊函數。函數註冊成功後即可通過Python UDF調用。推薦使用Linux環境,Windows使用者推薦使用Docker。
您需要基於第三方包的源碼編譯so連結庫,然後編譯產生WHEEL包,並修改尾碼格式。基於MaxCompute用戶端上傳包和so連結庫檔案,並註冊函數。函數註冊成功後即可通過Python UDF調用。
前提條件
在執行操作前,請確認已完成如下操作:
已安裝Python環境。推薦使用Python 3。
已安裝並配置MaxCompute用戶端。用戶端配置詳情請參見安裝並配置MaxCompute用戶端。
如果您通過Python UDF使用需要編譯的第三方包,請確認已安裝pip、setuptools(通過
pip install setuptools
安裝)和wheel(通過pip install wheel
安裝)。如果您使用的第三方包為GDAL 3.0及以上版本,請確認已安裝PROJ 6。
如果您通過Docker編譯第三方包,請確認已安裝Docker,詳情請參見Docker安裝文檔。
使用Numpy包(Python 3 UDF)
您可以通過MaxCompute內建的Python 3環境使用Numpy包。MaxCompute內建的Python 2環境預設安裝了Numpy,不需要手動上傳Numpy包。通過Python 3 UDF使用Numpy包的步驟如下:
以1.19.2版本為例,在PyPI頁面的Download files地區,單擊檔案名稱尾碼為cp37-cp37m-manylinux1_x86_64.whl的NumPy包進行下載。
說明其它尾碼名稱的包可能會載入失敗。如果您需要選擇其他版本,在PyPI頁面左上方的Navigation地區,單擊Release history即可查看歷史版本。
修改下載的Numpy包尾碼為ZIP格式。
例如numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.zip。
通過MaxCompute用戶端上傳NumPy包至MaxCompute專案空間。上傳資源詳情請參見資源操作。
命令樣本如下:
ADD ARCHIVE D:\Downloads\numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.zip -f;
編寫Python UDF指令碼,儲存為PY格式檔案。
假設此處儲存的指令碼名稱為import_numpy.py。Python UDF指令碼樣本如下:
from odps.udf import annotate @annotate("->string") class TryImport(object): #類名為TryImport。 def __init__(self): import sys sys.path.insert(0, 'work/numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.zip') #Numpy包,您只需要替換work/後邊的包名即可。 def evaluate(self): import numpy return "import succeed"
通過MaxCompute用戶端將import_numpy.py指令碼以資源形式上傳至MaxCompute專案空間。
命令樣本如下:
ADD PY D:\Desktop\import_numpy.py -f;
使用上傳的import_numpy.py指令碼及Numpy包,通過MaxCompute用戶端註冊自訂函數。註冊函數詳情請參見函數操作。
假設註冊的自訂函數名為numpy,函數資源所在專案為test_project。命令樣本如下:
CREATE FUNCTION numpy AS 'import_numpy.TryImport' USING 'test_project/resources/import_numpy.py,numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.zip';
說明註冊函數時資源清單裡需要加上Numpy包,例如numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.zip。
完成註冊後您即可編寫SQL語句調用建立的自訂函數。執行SQL語句時需要開啟 Python3,詳情請參見UDF開發規範與通用流程(Python3)。
使用需要編譯的第三方包
如果第三方包是PyPI頁面中格式為TAR.GZ的壓縮包,或從GitHub下載的源碼包,這些包解壓後的根目錄下有時會存在setup.py檔案。在使用這種類型的第三方包前,您需要先在與MaxCompute相容的環境下將setup.py編譯產生WHEEL包,然後再執行上傳資源及註冊函數操作,即可通過Python UDF調用第三方包。上傳資源及註冊函數操作請參見使用Numpy包(Python 3 UDF)。
由於第三方包是運行在Linux環境的,推薦您使用Linux環境編譯第三方包,使用Windows環境編譯會存在包不相容的問題。
如果您使用Windows環境,推薦在Docker的quay.io/pypa/manylinux2010_x86_64鏡像容器中使用對應版本的Python(
/opt/python/cp27-cp27m/bin/python
或/opt/python/cp37-cp37m/bin/python3
)編譯產生WHEEL包。
以Linux環境為例,確保環境相容的關注點如下:
需要使用相容的Python版本。在系統的命令列視窗執行如下命令,檢查環境Python版本。
python -c "import wheel.pep425tags; print(wheel.pep425tags.get_abi_tag())"
如果傳回值為
cp27m
或cp37m
,表示Python版本滿足相容性。如果傳回值為
cp27mu
或cp37mu
,表示Python版本不滿足相容性。您需要在系統的命令列視窗執行./configure --enable-unicode=ucs2
命令配置Python編碼格式為UCS2。
如果涉及C或C++代碼依賴,需要使用相容的GCC(GNU Compiler Collection)版本。
說明推薦您使用GCC 4.9.2及以下版本,GCC版本高於4.9.2時,編譯產生的WHEEL包中的SO類型檔案可能與MaxCompute環境不相容。
確認環境滿足相容性要求後,以Linux系統為例,通過setup.py產生WHEEL包的步驟如下:
將第三方包解壓到本地,在系統的命令列視窗,切換路徑至setup.py檔案所在檔案夾。
例如,下載的包為GDAL-3.2.0.zip,解壓後setup.py檔案所在路徑為D:\Downloads\GDAL-3.2.0,命令樣本如下:
cd D:\Downloads\GDAL-3.2.0
在系統的命令列視窗,執行如下命令查看返回結果中是否有bdist_wheel。
命令樣本如下:
python setup.py --help-command
在系統的命令列視窗,執行如下命令編譯產生WHEEL包。
python setup.py bdist_wheel
說明WHEEL包在dist目錄下。
使用依賴動態連結程式庫的第三方包
部分Python第三方包除了依賴Python庫,可能還會依賴其它動態連結程式庫的依賴。以GDAL 3.0.4為例,為您介紹如何使用Docker的quay.io/pypa/manylinux2010_x86_64鏡像容器,編譯相關的so連結庫,並編譯產生可以在MaxCompute上使用的WHEEL包。基於產生的so連結庫檔案、WHEEL包或Numpy包,再執行上傳資源及註冊函數操作,即可通過Python UDF調用第三方包。上傳資源及註冊函數操作請參見使用Numpy包(Python 3 UDF)。
請確認您已安裝Docker後再執行後續步驟。Docker操作詳情請參見Docker文檔。
通過Python UDF使用依賴so連結庫的第三方包的操作步驟如下:
查看依賴項。您可以在PyPI頁面的Dependencies地區查看依賴項。
例如GDAL 3.0.4的依賴項如下。
說明在圖示中,依賴項包括libgdal和numpy。libgdal需要在鏡像容器中通過編譯GDAL源碼得到,numpy需要在PyPI頁面或通過Docker鏡像容器下載NumPy包。
下載Numpy包。
您可以選擇如下兩種方式之一下載Numpy包:
在PyPI頁面的Download files地區,單擊檔案名稱尾碼為cp37-cp37m-manylinux1_x86_64.whl的NumPy包進行下載。
說明需要注意的是,如果您的Python環境為Python 2,Numpy包需要在PyPI頁面左側的Navigation地區,單擊Release history,選擇1.16.6及之前版本,且尾碼為cp27-cp27m-manylinux1_x86_64.whl的包進行下載。
在Docker的quay.io/pypa/manylinux2010_x86_64鏡像容器中,執行
/opt/python/cp37-cp37m/bin/pip download numpy -d ./
命令下載NumPy包到目前的目錄。
編譯so連結庫。
下載GDAL 3.0.4源碼並解壓到本地。
使用Docker下載quay.io/pypa/manylinux2010_x86_64鏡像容器,進入終端輸入模式。
命令樣本如下:
docker pull quay.io/pypa/manylinux2010_x86_64 docker run -it quay.io/pypa/manylinux1_x86_64 /bin/bash
上傳GDAL 3.0.4源碼到鏡像容器中。
在鏡像容器中編譯GDAL 3.0.4,操作詳情請參見BuildingOnUnix。
命令樣本如下:
# configure選項中需要指定安裝PROJ 6的位置。 ./configure --prefix=/path/to/install/prefix --with-proj=/path/to/install/proj6/prefix make make install export PATH=/path/to/install/prefix/bin:$PATH export LD_LIBRARY_PATH=/path/to/install/prefix/lib:$LD_LIBRARY_PATH export GDAL_DATA=/path/to/install/prefix/share/gdal # Test gdalinfo --version
編譯過程中可能出現如下報錯:
configure: error: PROJ 6 symbols not found
:GDAL 3.0及以上版本依賴PROJ 6,需要您下載安裝PROJ 6。fatal error: zlib.h: No such file or directory
:改用yum install zlib-devel
命令編譯。
使用Docker下載命令將兩個so連結庫(非軟連結)下載到本機,在GDAL、PROJ 6安裝目錄的lib檔案夾下擷取libgdal.so和libproj.so。
在鏡像容器中製作GDAL WHEEL包。操作詳情請參見BuildingOnUnix。
命令樣本如下:
# 如果需要numpy支援,要先安裝numpy。 /opt/python/cp37-cp37m/bin/pip install numpy # 切換到GDAL源碼目錄下。 cd swig/python # 產生WHEEL包,在dist目錄下。WHEEL包樣本:GDAL-3.0.4-cp37-cp37m-linux_x86_64.whl /opt/python/cp37-cp37m/bin/python setup.py bdist_wheel
基於產生的so連結庫檔案、WHEEL包或Numpy包,再執行上傳資源及註冊函數操作,即可實現通過Python UDF使用第三方包。上傳資源及註冊函數操作請參見使用Numpy包(Python 3 UDF)。
需要注意的是:
在上傳資源時,您需要以FILE資源形式上傳libgdal.so和libproj.so,以ARCHIVE資源形式上傳numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.zip和GDAL-3.0.4-cp37-cp37m-linux_x86_64.zip。
在註冊函數時,您需要在函數資源清單中添加libgdal.so、libproj.so、numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.zip和GDAL-3.0.4-cp37-cp37m-linux_x86_64.zip。
Python UDF程式碼範例如下:
說明本樣本為Python 3代碼,請在Python 3環境中執行。如果您需要在Python 2環境執行,請注意
get_cache_file
參數的使用方法,詳情可參見引用資源。# coding: utf-8 from odps.udf import annotate from odps.distcache import get_cache_file def include_file(file_name): import os, sys so_file = get_cache_file(file_name, 'b') with open(so_file.name, 'rb') as fp: content=fp.read() so = open(file_name, "wb") so.write(content) so.flush() so.close() @annotate("->string") class TryImport(object): def __init__(self): import sys include_file('libgdal.so.26') include_file('libproj.so.15') sys.path.insert(0, 'work/GDAL-3.0.4-cp37-cp37m-linux_x86_64.zip') #編譯後的GDAL包,您只需要替換work/後邊的包名即可。 sys.path.insert(0, 'work/numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.zip') #Numpy包,您只需要替換work/後邊的包名即可。 def evaluate(self): from osgeo import gdal from osgeo import ogr from osgeo import osr from osgeo import gdal_array from osgeo import gdalconst return "import succeed"
說明運行時如果報錯找不到libgdal.so.26、libproj.so.15,您需要修改libgdal.so、libproj.so為libgdal.so.26、libproj.so.15。