Administrator
发布于 2026-07-27 / 2 阅读
0

Glue Python Shell 升 3.9 后 import pg 报 ModuleNotFoundError:PyGreSQL 预装消失的解法与 C 扩展 wheel 编译配方

Glue Python Shell 升 3.9 后 import pg 报 ModuleNotFoundError:PyGreSQL 预装消失的解法与 C 扩展 wheel 编译配方

内容性质:解决方案(solution)。基于中国区真实 Glue Python Shell 2.0/Python 3.9 环境实测。编译配方部分对任何"链接系统库的 C 扩展"通用。可指导客户。

概述

Glue Python Shell 作业从 Python 3.6 升级到 3.9 后,代码中 import pg(PyGreSQL 库)报错:

File ".../pygresql_redshift_common.py", line 1, in <module>    import pg
ModuleNotFoundError: No module named 'pg'

一句话结论:Python 3.6 运行时预装了 PyGreSQL 5.0.6,Python 3.9 的 analytics 库集把它移除了(改为预装 psycopg2 2.9.3 与 redshift-connector 2.0.907)。3.6 时代 import pg 靠的是预装库这个隐式依赖,升级后依赖消失,需要客户自带或改代码。

值得注意的是,官方文档"Python shell 作业提供自有库"一节的 redshift_module 示例代码正是 import pg 的写法——照该示例写的历史代码在 3.9 下都会踩到这个问题。

根因确认方法

官方文档的预装库对照表(Python 3.6 列 vs Python 3.9 analytics 列):
https://docs.amazonaws.cn/glue/latest/dg/add-job-python.html#python-shell-supported-library

关键行:

Python 3.6Python 3.9 (analytics)
PyGreSQL5.0.6(无)
psycopg2(无)2.9.3
redshift-connector(无)2.0.907

只要代码 import pg / import pgdb 且作业从 3.6 升上来,即可断定是本问题。

解法一(长期推荐):改用 3.9 预装驱动

把连接逻辑从 pg 迁到预装的 redshift_connector(Amazon 官方 Redshift 驱动)或 psycopg2,之后不需要自带任何 wheel。以 redshift_connector 为例:

# 原 PyGreSQL 写法
import pg
conn = pg.connect(dbname="host=%s port=%s dbname=%s user=%s password=%s" % (...))
res = conn.query("select ...")          # 直接返回结果

# 迁移后(DB-API 2.0 风格)
import redshift_connector
conn = redshift_connector.connect(host=host, port=int(port), database=db_name,
                                  user=user, password=password)
cursor = conn.cursor()
cursor.execute("select ...")
res = cursor.fetchall()                  # execute + fetch 两步

迁移注意点(客户最易踩):

  • port 必须是 int——Glue 作业参数取出来是字符串,直接传会报错;
  • PyGreSQL 的 conn.query() 直接返回结果,DB-API 需要 cursor.execute() + fetchall()
  • DB-API 默认不自动提交事务,有写入需 conn.commit()conn.autocommit = True(PyGreSQL 经典接口没有这层)。

解法二:自带 PyGreSQL wheel(不改代码)

为什么不能直接 pip 下载

PyPI 上 PyGreSQL 所有版本都只发布 Windows wheel(win32/win_amd64)与源码包,没有任何 Linux wheel(可查任一版本的 Files 页,如 https://pypi.org/project/PyGreSQL/5.2.5/#files )。它是 C 扩展、链接 libpq,Linux 环境必须从源码编译——纯内网客户自己装不上,需要编译交付。

版本怎么选

版本判定原因
5.0.6(3.6 预装同款)官方支持最高到 Python 3.7,无 cp39 支持
5.2.55.2.x 起正式支持 3.9;5.2.5 是 5.x 最后一版,完整保留经典 pg/pgdb 双接口,客户代码零修改
6.xAPI 有破坏性调整,客户代码要改(要改代码不如直接走解法一)

编译配方(manylinux2014 容器,实测一次成功)

docker run --rm -v $PWD/pgbuild:/io quay.io/pypa/manylinux2014_x86_64 bash -c '
  yum install -y postgresql-devel                                # libpq 头文件
  /opt/python/cp39-cp39/bin/pip wheel PyGreSQL==5.2.5 --no-deps -w /io/raw
  auditwheel repair /io/raw/*.whl -w /io/fixed'

产物:pygresql-5.2.5-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl(约 3.7 MB)。

配方要点(通用于其他链接系统库的 C 扩展):

  • auditwheel repair 是关键——把 wheel 动态链接的系统库(本例 libpq 及其依赖 .so)复制进 wheel 并改 rpath,目标环境无需装 postgresql 客户端;
  • 基线选 manylinux2014(glibc 2.17):Glue Python Shell 3.9 运行时是 Amazon Linux 2 / glibc 2.26,2.17 基线一定兼容。不要在 glibc > 2.26 的系统(如 AL2023)上裸编译
  • manylinux2014 官方镜像内置 cp39(/opt/python/cp39-cp39/),不要用 Glue 官方 Docker 镜像(其 Python 版本与 Python Shell 全不匹配);
  • PyGreSQL 的 sdist 无 Python 依赖声明,产出 wheel 无 Requires-Dist纯内网 pip 安装零联网,无需做剥依赖处理(对比 s3fs 等有依赖声明的包)。
  • 宿主机若是 RHEL9 系(docker 实为 podman),挂载卷需加 :z 做 SELinux relabel,否则容器内写挂载目录报 PermissionError。

真实环境验证结果

GlueVersion 2.0 + PythonVersion 3.9 + library-set=analytics + --extra-py-files 挂该 wheel,探测作业 SUCCEEDED:

  • Successfully installed pygresql-5.2.5,全程无 pip 联网(日志无 Collecting / 大写 Downloading);
  • import pg / import pgdb 正常,pg.version = 5.2.5pg.DBpg.connect 等经典接口齐全;
  • 对不可达地址 pg.connect() 返回干净的 libpq 网络错误(could not connect to server),证明捆绑的 .so 加载正常、无 ABI 问题。

客户侧使用:wheel 上传自己的 S3 → 追加进 --extra-py-files(英文逗号分隔)→ 其余配置不变。

关键判断点

  • 作业运行成功import 报 ModuleNotFoundError,除了"egg 被 3.9 静默忽略"这个常见原因外,还要想到预装库集变化——对照官方预装表看该库是否只在 3.6 列存在。
  • 客户问"为什么不用原来的 5.0.6":PyPI Files 页即是证据(5.0.6 预编译包最高 cp37;5.2.2 起才有 cp39)。
  • 给客户交付前必须在真实 Glue 环境实测(探测作业跑 import + 连接冒烟),本地 venv 装得上不代表 Glue 装得上。

参考