ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

使用 psycopg2 时遇到的两个坑:从 pg_config 缺失到 whl 安装的排查记录

使用 psycopg2 时遇到的两个坑:从 pg_config 缺失到 whl 安装的排查记录

1. 从 pg_config not found 到 whl 装错:我踩过的两个 psycopg2 坑

如果你正在用 Python 连 PostgreSQL,大概率绕不开psycopg2这个库。它稳定、成熟、生态好,但安装过程对新手并不友好。我最近在给一台内网机器部署数据同步脚本时,连续踩了两个坑:一个是pg_config not found导致源码编译失败,另一个是离线安装 whl 包时选错了文件名,结果import psycopg2.extras直接报错。这两个问题在搜索引擎里能搜到大量零散答案,但很少有人把排查路径完整串起来。

这篇文章面向的是刚接触 PostgreSQL 的 Python 开发者、需要在离线环境部署服务的人,以及被pip install psycopg2卡住的运维同学。我会把两条排查路径都走一遍:先讲pg_config缺失时怎么定位和绕过,再讲 whl 包该怎么选、怎么装才不出问题。每一步都给出可复制的命令和最小验证脚本,你可以直接在自己的环境里复现。

需要说明的是,psycopg2和psycopg2-binary的区别不是本文重点,但会顺带讲清楚什么时候该用哪个。核心目标是让你在遇到报错时,能快速判断是缺编译依赖,还是包本身选错了。

2. pg_config 缺失的定位与绕过:pip 安装 psycopg2 报错排查

先说第一个坑。当你在干净的 Linux 或 macOS 上执行:

pip install psycopg2

大概率会看到类似这样的报错:

Error: pg_config executable not found. pg_config is required to build psycopg2 from source. Please add the directory containing pg_config to the PATH or specify the full executable path with the option: python setup.py build_ext --pg-config /path/to/pg_config build ... or with the pg_config option in 'setup.cfg'.

这个报错的本质是:psycopg2的源码包在安装时需要编译 C 扩展,而编译过程依赖 PostgreSQL 的客户端开发库,pg_config就是这套开发库提供的配置工具。它负责告诉编译器头文件在哪、库文件在哪。系统里只装了 PostgreSQL 服务端或者完全没装开发包时,这个可执行文件就不存在。

第一步是确认它到底在不在。执行:

which pg_config pg_config --version

如果两条命令都没有输出,说明确实缺失。这时候有两条路:装开发库,或者换用免编译的包。

装开发库的命令因系统而异。Debian/Ubuntu 系:

sudo apt-get update sudo apt-get install -y libpq-dev

CentOS/RHEL 系:

sudo yum install -y postgresql-devel

macOS 用 Homebrew:

brew install postgresql

装完之后再执行which pg_config,通常能看到/usr/bin/pg_config或/usr/local/bin/pg_config。如果还是找不到,可能是 PATH 没包含它,可以手动指定:

export PATH=$PATH:/usr/pgsql-14/bin

注意版本号要换成你实际安装的。这一步做完,再跑pip install psycopg2一般就能编译成功。

但生产环境往往不允许随便装系统包,尤其是内网机器。这时候更实际的做法是改用psycopg2-binary。它是预编译好的 wheel 包,不需要pg_config,安装命令就是:

pip install psycopg2-binary

官方文档对它的定位很明确:适合快速上手和开发环境,不建议在生产环境长期依赖。原因是它把 libpq 等库静态打包进去了,可能和系统里的 PostgreSQL 版本产生不一致,某些边界场景下会出现连接卡住、游标异常等问题。我自己的经历是,早期某个 2.7.x 版本在 server-side cursor 上确实遇到过读库卡死,升级到新版本后缓解了。所以如果你只是本地开发、写脚本、做数据迁移,psycopg2-binary完全够用;如果是长期运行的生产服务,还是老老实实装libpq-dev再编译psycopg2。

这里给一个最小连接验证脚本,装完任意一个包后都可以跑:

import psycopg2 conn = psycopg2.connect( host="127.0.0.1", port=5432, dbname="postgres", user="postgres", password="your_password" ) cur = conn.cursor() cur.execute("SELECT version();") print(cur.fetchone()) cur.close() conn.close()

能打印出版本号,说明驱动和数据库之间的链路是通的。如果报ModuleNotFoundError,说明包没装进当前 Python 环境;如果报连接超时,那是网络或认证问题,和驱动本身无关。

3. whl 包选择与离线安装:psycopg2-binary 文件名里的 cp36 和 manylinux 怎么看

第二个坑出现在离线环境。内网机器不能联网,只能把 whl 文件拷进去手动安装。我下载了这样一个文件:

psycopg2_binary-2.8.6-cp36-cp36m-manylinux1_x86_64.whl

然后执行:

pip install psycopg2_binary-2.8.6-cp36-cp36m-manylinux1_x86_64.whl

安装过程没报错,但代码里一执行import psycopg2.extras就抛异常。后来换成另一种方式:

pip install psycopg2-binary --no-index -f ./

同样的文件,这次import psycopg2.extras正常了。差别在于,直接指定 whl 文件路径时,pip 可能没有正确处理包名和依赖元数据;而用--no-index -f ./让 pip 在本地目录里按包名解析,行为更接近联网安装。

要理解这个问题,得先看懂 whl 文件名。以psycopg2_binary-2.8.6-cp36-cp36m-manylinux1_x86_64.whl为例:

片段含义
psycopg2_binary包名,对应psycopg2-binary
2.8.6版本号
cp36适用的 CPython 版本,这里是 3.6
cp36mABI 标签,m 表示 pymalloc
manylinux1_x86_64平台标签,Linux 64 位

选 whl 时,cp36必须和你的 Python 版本匹配。用python --version确认,如果是 3.9,就要找cp39的文件。平台标签也要对,manylinux1_x86_64适用于大多数 Linux 发行版,macOS 则是macosx_10_9_x86_64之类。选错了会在安装时报is not a supported wheel on this platform。

离线安装的推荐命令是:

pip install psycopg2-binary --no-index --find-links=/path/to/wheels

或者简写:

pip install psycopg2-binary --no-index -f ./wheels

这样 pip 会扫描指定目录,按包名和版本约束挑选合适的 whl,而不是把某个文件当成孤立路径处理。如果你确实要指定单个文件,建议先确认文件名和包名一致,并且用pip install ./psycopg2_binary-2.8.6-cp36-cp36m-manylinux1_x86_64.whl这种带./的相对路径写法,避免被当成包名解析。

另外,离线环境还要注意依赖。psycopg2-binary本身依赖不多,但如果你用的是psycopg2源码包,它可能还需要libpq。可以用pip download在联网机器上把依赖一起下下来:

pip download psycopg2-binary -d ./wheels --python-version 36 --platform manylinux1_x86_64 --only-binary=:all:

然后把整个wheels目录拷到内网,再用--no-index -f安装。这样能最大程度复现联网安装的结果。

4. 验证请求与成功结果:最小连接脚本和 import 检查

装完之后不能只看 pip 的 Successfully installed,要实际验证。我习惯分两步:先验证模块导入,再验证数据库连接。

模块导入检查:

import psycopg2 import psycopg2.extras import psycopg2.extensions print("psycopg2 version:", psycopg2.__version__) print("extras ok:", psycopg2.extras.__name__)

如果psycopg2.extras报ImportError或ModuleNotFoundError,说明安装的包不完整,或者 whl 选错了。这时候回到第 3 节,检查文件名和安装方式。

数据库连接检查,用一个带异常捕获的脚本:

import psycopg2 from psycopg2 import OperationalError def check_conn(): try: conn = psycopg2.connect( host="127.0.0.1", port=5432, dbname="postgres", user="postgres", password="your_password", connect_timeout=5 ) cur = conn.cursor() cur.execute("SELECT 1;") result = cur.fetchone() print("connection ok, SELECT 1 =>", result) cur.close() conn.close() except OperationalError as e: print("connection failed:", e) if __name__ == "__main__": check_conn()

成功时会输出connection ok, SELECT 1 => (1,)。如果报could not connect to server,检查 PostgreSQL 是否启动、端口是否放行、pg_hba.conf是否允许该用户从该地址连接。如果报password authentication failed,那是密码或认证方式问题,和驱动无关。

再进一步,验证 server-side cursor 是否正常,因为这是psycopg2-binary早期版本容易出问题的地方:

import psycopg2 conn = psycopg2.connect( host="127.0.0.1", port=5432, dbname="postgres", user="postgres", password="your_password" ) cur = conn.cursor(name="my_cursor") cur.itersize = 1000 cur.execute("SELECT generate_series(1, 10000);") count = 0 for row in cur: count += 1 print("fetched rows:", count) cur.close() conn.close()

如果这个脚本能顺利跑完并输出 10000,说明 named cursor 工作正常。如果卡住不动,优先考虑升级psycopg2-binary到较新版本,或者改用源码编译的psycopg2。

5. 本篇常见错排查:401、local proxy failed、reading choices 与 OAuth 报错对照

这一节把安装和使用过程中常见的报错集中对照一下。虽然有些报错和驱动本身无关,但在实际排查时经常混在一起,容易误判。

ModuleNotFoundError: No module named 'psycopg2':包没装进当前解释器。用which python和pip -V确认两者是否指向同一环境。虚拟环境里要用python -m pip install。

pg_config executable not found:缺libpq-dev或postgresql-devel,或者 PATH 里没有pg_config。参考第 2 节。

is not a supported wheel on this platform:whl 的 cp 标签或平台标签和当前环境不匹配。用python -c "import sys; print(sys.version)"确认版本,重新下载对应文件。

ImportError: cannot import name 'extras':whl 安装方式不对,或者包被部分覆盖。用pip uninstall psycopg2 psycopg2-binary清理后,用--no-index -f重装。

psycopg2.OperationalError: could not connect to server:数据库没启动、端口不通、防火墙拦截。先用telnet host 5432或nc -zv host 5432测端口。

psycopg2.OperationalError: FATAL: password authentication failed:密码错误或pg_hba.conf认证方式不匹配。检查用户、密码、数据库名。

local proxy failed:这类报错通常出现在通过代理访问外部服务时,和 psycopg2 无关。如果你在配置某些 AI 编码工具或 API 客户端,遇到这个提示,先检查本地代理设置和网络环境。

401 Unauthorized:API Key 无效或过期。如果你在用 TaoToken 这类模型服务,去控制台重新生成 Key,并确认请求头里的 Authorization 格式正确。

Error reading choices:一般是接口返回格式和客户端预期不一致,常见于模型对话类接口。检查请求体里的 model 参数和返回结构。

OAuth token expired:令牌过期,重新走授权流程或刷新 token。

这些报错里,只有前五个和 psycopg2 直接相关。后面几个更多出现在 API 调用和工具链配置场景。排查时先分类,能省不少时间。

6. 从驱动安装到模型接入:TaoToken 在开发链路里的位置

psycopg2 解决的是 Python 和 PostgreSQL 之间的连接问题。而在实际项目里,数据库只是其中一环。你可能还需要调用大模型做数据清洗、生成 SQL、写迁移脚本,或者用 AI 编码工具辅助开发。这些场景下,API Key 管理、Base URL 配置、模型 ID 选择就是另一套需要排查的东西。

如果你在用 Claude Code、Cline、Codex 这类工具,配置通常涉及三个要素:Base URL、API Key、Model ID。以 TaoToken 为例,API 地址是https://taotoken.net/api,Key 在控制台的 API Keys 页面生成。配置片段可以写成这样:

{ "base_url": "https://taotoken.net/api", "api_key": "sk-你的Key", "model": "claude-sonnet-4-20250514" }

不同工具的配置文件路径不一样。Claude Code 一般看~/.claude/settings.json,Cline 在 VS Code 设置里,Codex 可能涉及auth.json。不管哪个工具,核心都是把 Base URL 指向https://taotoken.net/api,把 Key 填对,再选一个可用的 Model ID。

验证配置是否生效,最直接的方式是用模型对话页面发一条测试消息。如果返回正常,说明 Key 和网络都没问题。如果报 401,检查 Key 是否复制完整;如果报 model not found,检查 Model ID 拼写。

对于长期编码和 Agent 场景,可以考虑 Coding Plan,它更适合高频调用。如果只是偶尔验证模型效果,用模型对话就够了。接入文档里有各工具的具体配置步骤,遇到报错可以先对照文档排查。

回到 psycopg2 这条线,最后给一个实用建议:在requirements.txt里明确写psycopg2-binary==2.9.9这样的固定版本,避免不同环境装到不同版本。生产环境如果条件允许,还是用psycopg2源码编译,把libpq-dev写进 Dockerfile 的基础镜像里。离线环境则提前用pip download把 whl 和依赖一起打好包,用--no-index -f安装。这样下次再遇到pg_config缺失或者 whl 导入异常,你就能直接定位到是哪一步出了问题。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进