MaxCompute lets you use third-party packages in a Python user-defined function (UDF), such as the NumPy package, packages that require compilation, or those that depend on a dynamic-link library. This topic shows you how to use them.
Background information
You can use third-party packages in a Python UDF in the following scenarios:
Use the NumPy package (Python 3 UDFs)
You must change the filename extension of the NumPy package, upload the package using the MaxCompute client, and then register the function, which you can then call from your Python 3 UDF.
Use a third-party package that requires compilation
In an environment compatible with MaxCompute, you must compile the setup.py script in the third-party package to generate a wheel package and change the package's filename extension. Then, use the MaxCompute client to upload the package and register the function, which you can then call from your Python UDF.
Use a third-party package that depends on a dynamic-link library
You must compile an .so library file from the source code of the third-party package, generate a wheel package, and then change the package's filename extension. Then, use the MaxCompute client to upload the package and the .so library file, and register the function, which you can then call from your Python UDF.
Prerequisites
Before you begin, make sure that the following requirements are met:
Python is installed. Python 3 is recommended.
The MaxCompute client is installed and configured. For more information, see Install and configure the MaxCompute client.
For packages that require compilation, ensure that pip, setuptools, and wheel are installed. You can run the
pip install setuptoolscommand to install setuptools and run thepip install wheelcommand to install wheel.If you use GDAL 3.0 or later, make sure that PROJ 6 is installed.
If you use Docker to compile a third-party package, make sure that Docker is installed. For more information, see the Docker installation documentation.
Use the NumPy package (Python 3 UDFs)
MaxCompute's Python 2 environment includes NumPy by default. To use the NumPy package in a Python 3 UDF, you must manually upload the package by following these steps:
Taking version 1.19.2 as an example, go to the PyPI page. In the Download files section, download the NumPy package whose filename ends with cp37-cp37m-manylinux1_x86_64.whl.
NoteLoading may fail if you use a package with a different filename extension. If you want to use a different version, go to the Navigation section in the upper-left corner of the PyPI page and click Release history to view historical versions.
Change the filename extension of the downloaded NumPy package to .zip.
Example: numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.zip.
Use the MaxCompute client to upload the NumPy package to a MaxCompute project. For more information about how to upload a resource, see Resource operations.
Sample command:
ADD ARCHIVE D:\Downloads\numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.zip -f;Write a Python UDF script and save it as a .py file.
The following is a sample Python UDF script saved as import_numpy.py:
from odps.udf import annotate @annotate("->string") class TryImport(object): # The class name is TryImport. def __init__(self): import sys sys.path.insert(0, 'work/numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.zip') # The NumPy package. You only need to replace the package name that follows work/. def evaluate(self): import numpy return "import succeed"Use the MaxCompute client to upload the import_numpy.py script as a resource to your MaxCompute project.
Sample command:
ADD PY D:\Desktop\import_numpy.py -f;Use the uploaded import_numpy.py script and NumPy package to register a user-defined function through the MaxCompute client. For more information about how to register a function, see Function operations.
Assume that the registered user-defined function is named numpy and the function resource is located in the test_project project. Sample command:
CREATE FUNCTION numpy AS 'import_numpy.TryImport' USING 'test_project/resources/import_numpy.py,numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.zip';NoteWhen you register the function, you must add the NumPy package, such as numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.zip, to the resource list.
After registering the function, call it from your SQL statements. Ensure you enable Python 3 for execution. For more information, see Python 3 UDF development specifications and workflow.
Use packages that require compilation
If a third-party package is a TAR.GZ archive from PyPI or a source code package from GitHub, the root directory of the decompressed package may contain a setup.py file. Before you can use this type of package, you must compile the setup.py file to generate a wheel package in an environment that is compatible with MaxCompute. Then, upload the resource and register the function. You can then call the third-party package using a Python UDF. For more information about uploading a resource and registering a function, see Use the NumPy package (Python 3 UDFs).
Third-party packages run in a Linux environment on MaxCompute. To avoid compatibility issues, compile them in a Linux environment rather than on Windows.
If you use a Windows environment, we recommend that you use the corresponding version of Python (
/opt/python/cp27-cp27m/bin/pythonor/opt/python/cp37-cp37m/bin/python3) to build a wheel package in thequay.io/pypa/manylinux2010_x86_64Docker container.
If you use a Linux environment, check the following points to ensure compatibility:
You must use a compatible Python version. In the command-line interface (CLI) of your system, run the following command to check the compatibility of your Python version:
python -c "import wheel.pep425tags; print(wheel.pep425tags.get_abi_tag())"If
cp27morcp37mis returned, the Python version meets the compatibility requirements.If
cp27muorcp37muis returned, the Python version does not meet the compatibility requirements. You must run the./configure --enable-unicode=ucs2command in the CLI to set the Python encoding to UCS-2.
If there are dependencies on C or C++ code, you must use a compatible GNU Compiler Collection (GCC) version.
NoteWe recommend that you use GCC 4.9.2 or earlier. If you use a GCC version later than 4.9.2, the .so files in the generated wheel package may be incompatible with the MaxCompute environment.
Once your environment meets the compatibility requirements, follow these steps to generate a wheel package from a setup.py file:
Extract the third-party package to a local directory. In the CLI of your system, switch to the folder where the setup.py file is located.
For example, if you download the GDAL-3.2.0.zip package and extract it, the setup.py file is in the D:\Downloads\GDAL-3.2.0 directory. Sample command:
cd D:\Downloads\GDAL-3.2.0In the CLI, run the following command to check whether bdist_wheel is in the output.
Sample command:
python setup.py --help-commandIn the CLI, run the following command to compile and generate a wheel package.
python setup.py bdist_wheelNoteThe wheel package is in the dist directory.
Use packages with dynamic-link libraries
Some third-party Python packages may depend on other dynamic-link libraries in addition to Python libraries. This section uses GDAL 3.0.4 as an example to describe how to use the quay.io/pypa/manylinux2010_x86_64 Docker container to compile the relevant .so library files and generate a wheel package that can be used in MaxCompute. You will then use these generated files to upload as resources and register the function, which allows you to call the package from a Python UDF. For more information about how to upload a resource and register a function, see Use the NumPy package (Python 3 UDFs).
Make sure that Docker is installed before you proceed. For more information about Docker operations, see the Docker documentation.
To use a third-party package that depends on an .so library file in a Python UDF, follow these steps:
View the dependencies. You can view the dependencies in the Dependencies section of the PyPI page.
For example, the dependencies of GDAL 3.0.4 are as follows.
Dependencies - libgdal (3.0.4 or greater) and header files (gdal-devel) - numpy (1.0.0 or greater) and header files (numpy-devel) (not explicitly required, but many examples and utilities will not work without it)NoteAs shown above, the dependencies include libgdal and numpy. libgdal is obtained by compiling the GDAL source code in a container, and numpy is obtained by downloading the NumPy package from the PyPI page or in a Docker container.
Download the NumPy package.
You can download the NumPy package using one of the following methods:
On the PyPI page, go to the Download files section and download the NumPy package whose filename ends with cp37-cp37m-manylinux1_x86_64.whl.
NoteIf you are using a Python 2 environment, you must download a compatible NumPy package. In the Navigation area on the left side of the PyPI page, click Release history, select version 1.16.6 or earlier, and download the package whose filename ends with cp27-cp27m-manylinux1_x86_64.whl.
In the quay.io/pypa/manylinux2010_x86_64 Docker container, run the
/opt/python/cp37-cp37m/bin/pip download numpy -d ./command to download the NumPy package to the current directory.
Compile the .so library file.
Download the GDAL 3.0.4 source code and extract it to a local directory.
Use Docker to pull the quay.io/pypa/manylinux2010_x86_64 container image and enter the interactive terminal mode.
Sample commands:
docker pull quay.io/pypa/manylinux2010_x86_64 docker run -it quay.io/pypa/manylinux2010_x86_64 /bin/bashCopy the GDAL 3.0.4 source code to the container.
Sample command:
docker cp ./gdal-3.0.4 <CONTAINER ID>:/opt/source/To obtain the CONTAINER ID, see docker ps.
Compile GDAL 3.0.4 in the container. For more information, see BuildingOnUnix.
Sample commands:
# You must specify the installation directory of PROJ 6 in the configure option. ./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 --versionThe following errors may occur during compilation:
configure: error: PROJ 6 symbols not found: GDAL 3.0 and later versions depend on PROJ 6. You must download and install PROJ 6.fatal error: zlib.h: No such file or directory: Run theyum install zlib-develcommand and compile again.
Use the docker cp command to copy the libgdal.so and libproj.so files from the container to your local machine. These are located in the lib folder of their respective installation directories. Ensure you copy the actual files, not their symbolic links.
Create a GDAL wheel package in the container. For more information, see BuildingOnUnix.
Sample commands:
# If NumPy is required, you must install NumPy first. /opt/python/cp37-cp37m/bin/pip install numpy # Switch to the directory of the GDAL source code. cd swig/python # A wheel package is generated in the dist directory. Example: GDAL-3.0.4-cp37-cp37m-linux_x86_64.whl /opt/python/cp37-cp37m/bin/python setup.py bdist_wheelBased on the generated .so library files, wheel package, or NumPy package, upload the resources and register the function to use the third-party package in a Python UDF. For more information about how to upload resources and register a function, see Use the NumPy package (Python 3 UDFs).
Note the following points:
When you upload resources, you must upload libgdal.so and libproj.so as FILE resources, and upload numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.zip and GDAL-3.0.4-cp37-cp37m-linux_x86_64.zip as ARCHIVE resources.
When you register the function, you must add libgdal.so, libproj.so, numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.zip, and GDAL-3.0.4-cp37-cp37m-linux_x86_64.zip to the function's resource list.
The following is sample code for the Python UDF:
NoteThis example uses Python 3 code and must be run in a Python 3 environment. If you need to run it in a Python 2 environment, take note of the usage of the
get_cache_fileparameter. For more information, see Reference resources.# 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') # The compiled GDAL package. You only need to replace the package name that follows work/. sys.path.insert(0, 'work/numpy-1.19.2-cp37-cp37m-manylinux1_x86_64.zip') # The NumPy package. You only need to replace the package name that follows 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"NoteIf you encounter a runtime error that libgdal.so.26 or libproj.so.15 cannot be found, rename the uploaded libgdal.so and libproj.so files to libgdal.so.26 and libproj.so.15, respectively.