.. _pyodps_pack:
制作和使用三方包
=====================
制作三方包
----------
PyODPS 自 0.11.3 起提供了 ``pyodps-pack`` 命令行工具,用于制作符合 PyODPS 及 DataWorks PyODPS
节点标准的三方包。该工具在 PyODPS 安装时同步安装,位于 Python bin 路径(Linux / MacOS)或者 Scripts
路径(Windows)下,可在命令行下调用。该工具使用方法类似 ``pip`` 命令。你可以使用该工具将所有依赖项目\
制作成一个 ``.tar.gz`` 压缩包,其中包含所有依照 MaxCompute / DataWorks 环境编译并打包的项目依赖。\
如果你的项目有自行创建的 Python 包,也可以使用该工具进行打包。
准备工作
~~~~~~~~
Docker 模式
^^^^^^^^^^^
你需要安装 Docker 以顺利使用 Docker 模式运行 ``pyodps-pack``。 ``pyodps-pack`` 会自动调用已安装的
Docker,你不需要手动在 Docker 中运行 ``pyodps-pack``。对于 Linux 环境,可以参考 `Docker 官方文档
`_ 安装 Docker。对于 MacOS / Windows,个人开发者可以使用
`Docker Desktop `_ 。对于没有购买过授权的企业用户,推荐使用开源的
`Rancher Desktop `_ (
`中国内地镜像 `_
)。你也可以考虑使用 `minikube `_,
但需要一些额外的步骤,见 :ref:`这份文档 `。我们没有在其他 Docker 环境中测试 ``pyodps-pack`` ,不保证在这些环境中的可用性。
对于期望在版本较老的专有云中的 MaxCompute / DataWorks 使用 ``--legacy-image`` 选项打包的用户,在 Windows / MacOS
或者部分内核的 Linux 系统中可能出现无法打包的错误,请参考
`本文 `_
配置合适的打包环境。
对于 Windows 用户,可能你的 Docker 服务需要依赖 Windows 系统的 Server 服务才能启动,而 Server 服务由于安全问题在很多企业被禁止启动。
在遇到问题时,请改用 Linux 打包或者设法启用 Server 服务。Rancher Desktop 在 Windows 10 下可能无法使用 ``containerd``
作为容器引擎,可以尝试改用 ``dockerd`` ,具体参考 `该文档 `_
进行配置。
如果你的 MaxCompute / DataWorks 基于 ARM64 机型部署(通常是专有云),你需要额外增加 ``--arch aarch64`` 参数指定打包需要的架构。通常
Docker Desktop / Rancher Desktop 已经安装了跨平台打包所需的 ``binfmt`` 相关组件,你也可以使用命令
.. code-block:: bash
docker run --privileged --rm tonistiigi/binfmt --install arm64
安装相关的虚拟环境。该命令要求 Linux Kernel 版本高于 4.8,具体可以参考 `该页面 `_。
.. toctree::
:hidden:
:maxdepth: 1
pyodps-pack-minikube
无 Docker 模式
^^^^^^^^^^^^^^^
.. note::
我们建议在打包时,尽量使用 Docker 模式。非 Docker 模式仅用于 Docker 不可用的场景,且生成的包有可能不可用。
如果你安装 Docker 遇到困难,你可以尝试使用非 Docker 模式。使用方式为新增一个 ``--no-docker`` 参数。该模式需要你的 Python
环境中已经安装 pip。如果使用该模式出现错误,请改用 Docker 模式。Windows 用户需要安装 Git bash 以使用该模式,Git bash
包含在 `Git for Windows `_ 中。
打包所有依赖
~~~~~~~~~~~~
.. note::
MaxCompute 建议除非不得已,新项目请尽量使用 Python 3。我们不保证下面的打包步骤对 Python 2 的可用性。
旧项目在可能的情况下请尽量迁移到 Python 3 以减少后续维护的难度。
在 Linux 中使用下列命令时,请使用 ``sudo`` 调用 ``pyodps-pack`` 以保证 Docker 正常运行。
在 macOS 中使用下列命令时,\ **不建议**\ 使用 ``sudo``,这可能会导致无法预期的权限问题。
安装完 PyODPS 后,你可以使用下面的命令为 Python 3 打包 pandas 及所有 pandas 的依赖项:
.. code-block:: bash
pyodps-pack pandas
使用非 Docker 模式打包,可以用
.. code-block:: bash
pyodps-pack --no-docker pandas
需要指定版本时,可以使用
.. code-block:: bash
pyodps-pack pandas==1.2.5
经过一系列的打包步骤,工具会显示包中的所有依赖版本
::
Package Version
--------------- -------
numpy 1.21.6
pandas 1.2.5
python-dateutil 2.8.2
pytz 2022.6
six 1.16.0
并在当前目录中生成一个 ``packages.tar.gz`` 文件,其中包括上面列出的所有依赖项目。
如果你希望为 Python 2.7 打包,请确定你的包要在 MaxCompute 还是 DataWorks 中使用。如果你不确定你的包将在哪个环境中使用,
请参考 `这篇文章 `_ 。
如果要在 MaxCompute 中使用 Python 2.7 包,可以使用下面的打包命令:
.. code-block:: bash
pyodps-pack --mcpy27 pandas
如果生成的 Python 2.7 包要在 DataWorks 中使用,可以使用下面的打包命令:
.. code-block:: bash
pyodps-pack --dwpy27 pandas
打包自定义代码
~~~~~~~~~~~~~~
``pyodps-pack`` 支持打包使用 ``setup.py`` 或者 ``pyproject.toml`` 组织的用户自定义 Python project。如果你之前从未
接触过相关知识,可以参考 `这个链接 `_ 获取更多信息。
下面用基于 ``pyproject.toml`` 组织的项目举例介绍一下如何使用 ``pyodps-pack`` 打包。假定项目的目录结构如下:
::
test_package_root
├── test_package
│ ├── __init__.py
│ ├── mod1.py
│ └── subpackage
│ ├── __init__.py
│ └── mod2.py
└── pyproject.toml
其中 ``pyproject.toml`` 内容可能为
.. code-block:: toml
[project]
name = "test_package"
description = "pyodps-pack example package"
version = "0.1.0"
dependencies = [
"pandas>=1.0.5"
]
完成包的开发后,使用下面的命令可以将此包和所有依赖打包进 ``packages.tar.gz`` 文件中( ``path_to_package``
为 ``test_package_root`` 的上级路径):
.. code-block:: bash
pyodps-pack //test_package_root
打包 Git Repo 中的代码
~~~~~~~~~~~~~~~~~~~~~~
``pyodps-pack`` 支持打包远程 Git 代码仓库(例如 Github)中的代码。以 PyODPS 本身为例,可以使用下面的命令执行打包:
.. code-block:: bash
pyodps-pack git+https://github.com/aliyun/aliyun-odps-python-sdk.git
如果想要打包某个分支或者 Tag,可以使用
.. code-block:: bash
pyodps-pack git+https://github.com/aliyun/aliyun-odps-python-sdk.git@v0.11.2.2
如果打包前需要安装一些打包依赖(例如 ``cython``),可以使用 ``--install-requires`` 参数增加安装时依赖。
也可以编写一个格式与 ``requirements.txt`` 相同的 ``install-requires.txt`` 文件,并使用
``--install-requires-file`` 选项指定。例如,如果需要先安装 ``Cython`` 再打包 PyODPS,可以写
.. code-block:: bash
pyodps-pack \
--install-requires cython \
git+https://github.com/aliyun/aliyun-odps-python-sdk.git@v0.11.2.2
也可以创建一个 ``install-requires.txt`` 文件并编写:
::
cython>0.29
打包命令可以写成
.. code-block:: bash
pyodps-pack \
--install-requires-file install-requires.txt \
git+https://github.com/aliyun/aliyun-odps-python-sdk.git@v0.11.2.2
更复杂的例子:二进制依赖
~~~~~~~~~~~~~~~~~~~~~~~~
一部分包包含额外的二进制依赖,例如需要编译 / 安装的外部动态链接库。``pyodps-pack`` 提供了
``--run-before`` 参数用以指定打包前需要执行的步骤,该步骤中可以安装所需的二进制依赖。
我们用地理信息库 `GDAL `_ 来说明如何打包。
首先确定打包时需要安装的二进制依赖。根据 GDAL 3.6.0.1 在 `PyPI 上的文档 `_
,我们需要安装 3.6.0 以上版本的 libgdal。 `libgdal 的编译说明 `_
则指出,该包依赖 6.0 以上的 PROJ 包,这两个二进制包均使用 CMake 打包。据此,编写二进制包安装文件并保存为 ``install-gdal.sh``:
.. code-block:: bash
#!/bin/bash
set -e
cd /tmp
curl -o proj-6.3.2.tar.gz https://download.osgeo.org/proj/proj-6.3.2.tar.gz
tar xzf proj-6.3.2.tar.gz
cd proj-6.3.2
mkdir build && cd build
cmake ..
cmake --build .
cmake --build . --target install
cd /tmp
curl -o gdal-3.6.0.tar.gz http://download.osgeo.org/gdal/3.6.0/gdal-3.6.0.tar.gz
tar xzf gdal-3.6.0.tar.gz
cd gdal-3.6.0
mkdir build && cd build
cmake ..
cmake --build .
cmake --build . --target install
此后,使用 ``pyodps-pack`` 进行打包:
.. code-block:: bash
pyodps-pack --install-requires oldest-supported-numpy --run-before install-gdal.sh gdal==3.6.0.1
在某些情况下,二进制依赖被通过动态链接(例如使用 ``ctypes.cdll.LoadLibrary`` )引入到 Python
中。此时,你可以使用 ``--dynlib`` 参数指定需要包含在包中的二进制依赖路径(或者 /lib
下的包名),该依赖将被打包到 ``packages/dynlibs`` 路径下。例如,Python 库 ``unrar``
动态链接了 ``libunrar`` 这个二进制库,我们使用下面的 ``install-libunrar.sh``
代码编译和安装:
.. code-block:: bash
#!/bin/bash
curl -o unrar.tar.gz https://www.rarlab.com/rar/unrarsrc-6.0.3.tar.gz
tar xzf unrar.tar.gz
cd unrar
make -j4 lib
# 该步骤设置输出包 SONAME 为 libunrar.so,为 LoadLibrary 所必需
# 对于大部分二进制包,此步骤可能并非必需
patchelf --set-soname libunrar.so libunrar.so
make install-lib
此后,使用 ``pyodps-pack`` 进行打包:
.. code-block:: bash
pyodps-pack --run-before install-libunrar.sh --dynlib unrar unrar
在上述命令中, ``--dynlib`` 的值 ``unrar`` 省略了 lib 前缀, ``pyodps-pack`` 实际找到的是
``/lib/libunrar.so`` 。如果有多个动态链接库, ``--dynlib`` 可被指定多次。
由于动态链接库的复杂性,你可能需要在 import 你的三方库前手动加载动态链接库,例如
.. code-block:: python
import ctypes
ctypes.cdll.LoadLibrary("work/packages.tar.gz/packages/dynlibs/libunrar.so")
import unrar
对 ``LoadLibrary`` 路径的具体说明请参考 :ref:`Python UDF 使用三方包 `
中的说明。
命令详情
~~~~~~~~~
下面给出 ``pyodps-pack`` 命令的可用参数,可用于控制打包过程:
- ``-r``, ``--requirement ``
根据给定的依赖文件打包。该选项可被指定多次。
- ``-o``, ``--output ``
指定打包生成目标文件名,默认为 ``packages.tar.gz``。
- ``--install-requires - ``
指定打包时所需的 PyPI 依赖,可指定多个。这些依赖 **不一定** 会包含在最终的包中。
- ``--install-requires-file ``
指定打包时所需的 PyPI 依赖定义文件,可指定多个。这些依赖 **不一定** 会包含在最终的包中。
- ``--run-before ``
指定打包前需要执行的 Bash 脚本,通常可用于安装二进制依赖。
- ``-X``, ``--exclude ``
指定打包时需要从最终包删除的 PyPI 依赖。该选项可被指定多次。
- ``--no-deps``
指定打包时不包含指定项目的依赖项。
- ``--pre``
如果指定,则打包预发布和开发版本。默认情况下,只包含正式版本。
- ``--proxy ``
指定打包所用的代理服务器,以 scheme://[user:passwd@]proxy.server:port 这样的形式。
- ``--retries ``
指定每次连接时的最大重试次数(默认5次)。
- ``timeout ``
指定套接字超时时间(默认15秒)。
- ``-i``, ``--index-url ``
指定打包时所需的仓库索引 URL。如果缺省,会使用 ``pip config list`` 命令返回的 ``global.index-url``
值,该值通常配置在 ``pip.conf`` 配置文件中。
- ``--extra-index-url ``
指定除 ``--index-url`` 之外需要使用的仓库索引 URL,规则与 ``--index-url`` 类似。
- ``--trusted-host ``
指定打包时需要忽略证书问题的 HTTPS 域名。
- ``-l``, ``--legacy-image``
指定后,将使用 CentOS 5 镜像进行打包,这使得包可以被用在旧版专有云等环境中。
- ``--mcpy27``
指定后,将为 MaxCompute 内的 Python 2.7 制作三方包。如果启用,将默认 ``--legacy-image`` 选项开启。
- ``--dwpy27``
指定后,将为 DataWorks 内的 Python 2.7 制作三方包。如果启用,将默认 ``--legacy-image`` 选项开启。
- ``--prefer-binary``
指定后,将倾向于选择 PyPI 中包含二进制编译的旧版而不是仅有源码包的新版。
- ``--arch ``
指定目标包面向的硬件架构,目前仅支持 x86\_64 和 aarch64(或 arm64),默认为 x86\_64。如果你并不在专有云使用
MaxCompute 或 DataWorks,**不要指定这个参数**。
- ``--python-version ``
指定目标面向的 Python 版本,可使用 3.6 或者 36 表示 Python 3.6。如果你并不在专有云使用
MaxCompute 或 DataWorks,**不要指定这个参数**。
- ``--dynlib ``
指定后,将引入 .so 动态链接库,可以指定具体路径,也可以指定库名(包含或不包含 lib 前缀均可)。 ``pyodps-pack``
将在/lib、/lib64、/usr/lib、/usr/lib64中查找对应库,并置入包中 packages/dynlibs 下。你可能需要手动调用
``ctypes.cdll.LoadLibrary`` 在相应包路径引用这些库。
- ``--docker-args ``
指定在执行 Docker 命令时需要额外附加的参数。如有多个参数需用引号包裹,例如 ``--docker-args "--ip 192.168.1.10"``。
- ``--no-docker``
使用无 Docker 模式运行 ``pyodps-pack``。当依赖中存在二进制依赖,可能报错或导致包不可用。
- ``--no-merge``
下载或生成 Wheel 包后不生成 ``.tar.gz`` 包而是保留 ``.whl`` 文件。
- ``--skip-scan-pkg-resources``
在打包过程中不在包中扫描和解决 ``pkg_resources`` 的依赖,当依赖项较多时可加快打包速度。
- ``--find-vcs-root``
当需要打包的本地代码需要依赖 Git 等版本管理工具上的 Tag 作为版本信息(例如使用 ``setuptools_scm`` 管理版本号)\
且 Python 包根目录与代码根目录不一致时,该选项能自动向上找到版本管理工具中代码的根目录。
- ``--use-default-image-tag``
指定后,默认镜像将使用默认 tag,以避免重复下载镜像。
- ``--debug``
指定后,将输出命令运行的详细信息,用于排查问题。
除此之外,还有若干环境变量可供配置:
- ``DOCKER_PATH="path to docker installation"``
指定 Docker 可执行文件路径,路径下需要包括 ``docker`` 可执行文件。
- ``BEFORE_BUILD="command before build"``
指定打包前需要执行的命令。
- ``AFTER_BUILD="command after build"``
指定编译后生成 Tar 包前需要执行的命令。
- ``DOCKER_IMAGE="quay.io/pypa/manylinux2010_x86_64"``
自定义需要使用的 Docker Image。建议基于 ``pypa/manylinux`` 系列镜像定制自定义打包用 Docker Image。
使用三方包
----------
上传三方包
~~~~~~~~~~
使用三方包前,请确保你生成的包被上传到 MaxCompute Archive 资源。可以使用下面的代码上传资源。
需要注意的是,你需要将 packages.tar.gz 替换成你刚生成的包所在的路径和文件名:
.. code-block:: python
import os
from odps import ODPS
# 确保 ALIBABA_CLOUD_ACCESS_KEY_ID 环境变量设置为用户 Access Key ID,
# ALIBABA_CLOUD_ACCESS_KEY_SECRET 环境变量设置为用户 Access Key Secret,
# 不建议直接使用 Access Key ID / Access Key Secret 字符串
o = ODPS(
os.getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'),
os.getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
project='**your-project**',
endpoint='**your-endpoint**',
)
o.create_resource("test_packed.tar.gz", "archive", fileobj=open("packages.tar.gz", "rb"))
也可以使用 DataWorks 上传。具体步骤为:
1. 进入数据开发页面。
a. 登录 DataWorks 控制台。
b. 在左侧导航栏,单击工作空间列表。
c. 选择工作空间所在地域后,单击相应工作空间后的进入数据开发。
2. 鼠标悬停至新建图标,单击MaxCompute \> 资源 \> Archive
也可以展开业务流程目录下的目标业务流程,右键单击 MaxCompute,选择新建 \> 资源 \> Archive
3. 在新建资源对话框中,输入资源名称,并选择目标文件夹。
4. 单击点击上传,选择相应的文件进行上传。
5. 单击确定。
6. 单击工具栏中的提交图标,提交资源至调度开发服务器端。
更详细的细节请参考 `这篇文章 `_ 。
.. _pyodps_pack_udf:
在 Python UDF 中使用三方包
~~~~~~~~~~~~~~~~~~~~~~~~~
你需要对你的 UDF 进行修改以使用上传的三方包。具体地,你需要在 UDF 类的 ``__init__`` 方法中添加对三方包的引用,
然后再在UDF代码中(例如 evaluate / process 方法)调用三方包。
我们以实现 scipy 中的 psi 函数为例展示如何在 Python UDF 中使用三方包。首先使用下面的命令打包:
.. code-block:: bash
pyodps-pack -o scipy-bundle.tar.gz scipy
随后编写下面的代码,并保存为 ``test_psi_udf.py``:
.. code-block:: python
import sys
from odps.udf import annotate
@annotate("double->double")
class MyPsi(object):
def __init__(self):
# 如果依赖中包含 protobuf,需要添加下面这行语句,否则不需要
sys.setdlopenflags(10)
# 将路径增加到引用路径
sys.path.insert(0, "work/scipy-bundle.tar.gz/packages")
def evaluate(self, arg0):
# 将 import 语句保持在 evaluate 函数内部
from scipy.special import psi
return float(psi(arg0))
对上面的代码做一些解释。
1. 当依赖中包含 protobuf 时,需要为 ``__init__`` 函数增加 ``sys.setdlopenflags(10)`` ( ``pyodps-pack``
打包过程中会提示),该设置可以避免三方包和 MaxCompute 间相关的版本冲突。
2. ``__init__`` 函数中将 ``work/scipy-bundle.tar.gz/packages`` 添加到 ``sys.path``,
因为 MaxCompute 会将所有 UDF 引用的 Archive 资源以资源名称为目录解压到 ``work`` 目录下,而 ``packages`` 则是
``pyodps-pack`` 生成包的子目录。如果你需要通过 ``LoadLibrary`` 引入 ``--dynlib``
参数引入的动态链接库,也可以在此处引用。
3. 将对 scipy 的 import 放在 evaluate 函数体内部的原因是三方包仅在执行时可用,当
UDF 在 MaxCompute 服务端被解析时,解析环境不包含三方包,函数体外的三方包 import 会导致报错。
随后需要将 ``test_psi_udf.py`` 上传为 MaxCompute Python 资源,以及将 ``scipy-bundle.tar.gz`` 上传为 Archive
资源。此后,创建 UDF 名为 ``test_psi_udf``,引用上面两个资源文件,并指定类名为 ``test_psi_udf.MyPsi``。
利用 PyODPS 完成上述步骤的代码为
.. code-block:: python
import os
from odps import ODPS
# 确保 ALIBABA_CLOUD_ACCESS_KEY_ID 环境变量设置为用户 Access Key ID,
# ALIBABA_CLOUD_ACCESS_KEY_SECRET 环境变量设置为用户 Access Key Secret,
# 不建议直接使用 Access Key ID / Access Key Secret 字符串
o = ODPS(
os.getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'),
os.getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
project='**your-project**',
endpoint='**your-endpoint**',
)
bundle_res = o.create_resource(
"scipy-bundle.tar.gz", "archive", fileobj=open("scipy-bundle.tar.gz", "rb")
)
udf_res = o.create_resource(
"test_psi_udf.py", "py", fileobj=open("test_psi_udf.py", "rb")
)
o.create_function(
"test_psi_udf", class_type="test_psi_udf.MyPsi", resources=[bundle_res, udf_res]
)
使用 MaxCompute Console 上传的方法为
.. code-block:: sql
add archive scipy-bundle.tar.gz;
add py test_psi_udf.py;
create function test_psi_udf as test_psi_udf.MyPsi using test_psi_udf.py,scipy-bundle.tar.gz;
完成上述步骤后,即可使用 UDF 执行 SQL:
.. code-block:: sql
set odps.pypy.enabled=false;
set odps.isolation.session.enable=true;
select test_psi_udf(sepal_length) from iris;
在 PyODPS DataFrame 中使用三方包
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PyODPS DataFrame 支持在 execute / persist 时使用 libraries 参数使用上面的第三方库。
下面以 map 方法为例,apply / map_reduce 方法的过程类似。
首先,用下面的命令打包 scipy:
.. code-block:: bash
pyodps-pack -o scipy-bundle.tar.gz scipy
假定我们的表名为 ``test_float_col`` ,内容只包含一列 float 值:
::
col1
0 3.75
1 2.51
计算 psi(col1) 的值,可以编写下面的代码:
.. code-block:: python
import os
from odps import ODPS, options
def psi(v):
from scipy.special import psi
return float(psi(v))
# 如果 Project 开启了 Isolation,下面的选项不是必需的
options.sql.settings = {"odps.isolation.session.enable": True}
# 确保 ALIBABA_CLOUD_ACCESS_KEY_ID 环境变量设置为用户 Access Key ID,
# ALIBABA_CLOUD_ACCESS_KEY_SECRET 环境变量设置为用户 Access Key Secret,
# 不建议直接使用 Access Key ID / Access Key Secret 字符串
o = ODPS(
os.getenv('ALIBABA_CLOUD_ACCESS_KEY_ID'),
os.getenv('ALIBABA_CLOUD_ACCESS_KEY_SECRET'),
project='**your-project**',
endpoint='**your-endpoint**',
)
df = o.get_table("test_float_col").to_df()
# 直接执行并取得结果
df.col1.map(psi).execute(libraries=["scipy-bundle.tar.gz"])
# 保存到另一张表
df.col1.map(psi).persist("result_table", libraries=["scipy-bundle.tar.gz"])
如果希望在整个代码执行过程中使用相同的三方包,可以设置全局选项:
.. code-block:: python
from odps import options
options.df.libraries = ["scipy-bundle.tar.gz"]
此后即可在 DataFrame 执行时用到相关的三方包。
在 DataWorks 中使用三方包
~~~~~~~~~~~~~~~~~~~~~~
DataWorks PyODPS 节点预装了若干三方包,同时提供了 ``load_resource_package`` 方法用以引用其他的包,
具体使用方式可参考 :ref:`这里 `。
手动上传和使用三方包
----------------
.. note::
以下内容仅作为维护旧项目或者旧环境的参考,新项目建议直接使用 ``pyodps-pack`` 打包。
部分旧项目可能使用了之前的方式使用三方包,即手动上传所有依赖的 Wheel 包并在代码中引用,或者使用了不支持二进制包的旧版 MaxCompute
环境,本章节为这部分场景准备。下面以在 map 中使用 python_dateutil 为例说明使用三方包的步骤。
首先,我们可以在 Linux bash 中使用 ``pip download`` 命令,下载包以及其依赖到某个路径。
这里下载后会出现两个包:six-1.10.0-py2.py3-none-any.whl和python_dateutil-2.5.3-py2.py3-none-any.whl
(这里注意需要下载支持 Linux 环境的包,建议直接在 Linux 下调用该命令。)
.. code-block:: shell
pip download python-dateutil -d /to/path/
然后我们分别把两个文件上传到ODPS资源
.. code-block:: python
# 这里要确保资源名的后缀是正确的文件类型
odps.create_resource('six.whl', 'file', file_obj=open('six-1.10.0-py2.py3-none-any.whl', 'rb'))
odps.create_resource('python_dateutil.whl', 'file', file_obj=open('python_dateutil-2.5.3-py2.py3-none-any.whl', 'rb'))
现在我们有个DataFrame,只有一个string类型字段。
.. code:: python
>>> df
datestr
0 2016-08-26 14:03:29
1 2015-08-26 14:03:29
全局配置使用到的三方库:
.. code:: python
>>> from odps import options
>>>
>>> def get_year(t):
>>> from dateutil.parser import parse
>>> return parse(t).strftime('%Y')
>>>
>>> options.df.libraries = ['six.whl', 'python_dateutil.whl']
>>> df.datestr.map(get_year)
datestr
0 2016
1 2015
或者,通过立即运行方法的 ``libraries`` 参数指定:
.. code:: python
>>> def get_year(t):
>>> from dateutil.parser import parse
>>> return parse(t).strftime('%Y')
>>>
>>> df.datestr.map(get_year).execute(libraries=['six.whl', 'python_dateutil.whl'])
datestr
0 2016
1 2015
PyODPS 默认支持执行纯 Python 且不含文件操作的第三方库。在较新版本的 MaxCompute 服务下,PyODPS
也支持执行带有二进制代码或带有文件操作的 Python 库。这些库名必须拥有一定的后缀,可根据下表判断
============== ================ ====================================================================================================================
平台 Python 版本 可用的后缀
-------------- ---------------- --------------------------------------------------------------------------------------------------------------------
RHEL 5 x86\_64 Python 2.7 cp27-cp27m-manylinux1_x86_64
RHEL 5 x86\_64 Python 3.7 cp37-cp37m-manylinux1_x86_64
RHEL 7 x86\_64 Python 2.7 cp27-cp27m-manylinux1_x86_64, cp27-cp27m-manylinux2010_x86_64, cp27-cp27m-manylinux2014_x86_64
RHEL 7 x86\_64 Python 3.7 cp37-cp37m-manylinux1_x86_64, cp37-cp37m-manylinux2010_x86_64, cp37-cp37m-manylinux2014_x86_64
RHEL 7 ARM64 Python 3.7 cp37-cp37m-manylinux2014_aarch64
============== ================ ====================================================================================================================
所有的 whl 包都需要以 archive 格式上传,whl 后缀的包需要重命名为 zip。同时,作业需要开启
``odps.isolation.session.enable`` 选项,或者在 Project 级别开启 Isolation。下面的例子展示了如何上传并使用
scipy 中的特殊函数:
.. code-block:: python
# 对于含有二进制代码的包,必须使用 Archive 方式上传资源,whl 后缀需要改为 zip
odps.create_resource('scipy.zip', 'archive', file_obj=open('scipy-0.19.0-cp27-cp27m-manylinux1_x86_64.whl', 'rb'))
# 如果 Project 开启了 Isolation,下面的选项不是必需的
options.sql.settings = { 'odps.isolation.session.enable': True }
def psi(value):
# 建议在函数内部 import 第三方库,以防止不同操作系统下二进制包结构差异造成执行错误
from scipy.special import psi
return float(psi(value))
df.float_col.map(psi).execute(libraries=['scipy.zip'])
对于只提供源码的二进制包,可以在 Linux Shell 中打包成 Wheel 再上传,Mac 和 Windows 中生成的 Wheel
包无法在 MaxCompute 中使用:
.. code-block:: shell
python setup.py bdist_wheel