⌘+k ctrl+k
1.3 (稳定版)
搜索快捷键 cmd + k | ctrl + k
Python

DuckDB Python 包位于 GitHub 上的主 DuckDB 源代码中,在 /tools/pythonpkg/ 文件夹下。它使用 pybind11 来创建与 DuckDB 的 Python 绑定。

先决条件

对于本页描述的所有内容,我们做出以下假设:

  1. 您拥有 DuckDB 源代码的可用副本(包括 Git 标签),并且从源代码的根目录运行命令。
  2. 您在一个专用的虚拟环境中安装了合适的 Python。

1. DuckDB 仓库

确保您已经检出了 DuckDB 源代码并且位于其根目录。例如:

git clone https://github.com/duckdb/duckdb
...
cd duckdb

如果您已经 fork 了 DuckDB,那么在未拉取标签的情况下构建 Python 包时可能会遇到问题。

# Check your remotes
git remote -v

# If you don't see upstream [email protected]:duckdb/duckdb.git, then add it
git remote add upstream git@github.com:duckdb/duckdb.git

# Now you can pull & push the tags
git fetch --tags upstream
git push --tags

2. Python 虚拟环境

对于此处描述的所有内容,您将需要一个合适的 Python 安装。虽然您理论上可以使用系统 Python,但我们强烈建议您使用 Python 虚拟环境。虚拟环境可以隔离依赖项,并且根据您使用的工具,您可以控制使用哪个 Python 解释器。这样您就不会用项目所需的各种包来污染您的系统级 Python。

虽然我们在下面的示例中使用了 Python 内置的 venv 模块,并且理论上这可能(或可能不!)对您有效,但我们也强烈建议使用 astral uv(或 Poetry、conda 等)之类的工具,它允许您同时管理 Python 解释器版本和虚拟环境。

按如下方式创建并激活虚拟环境:

# Create a virtual environment in the .venv folder (in the duckdb source root)
python3 -m venv --prompt duckdb .venv

# Activate the virtual env
source .venv/bin/activate

确保您的虚拟环境中有一个足够新版本的 pip

# Print pip's help
python3 -m pip install --upgrade pip

如果失败并显示 No module named pip 且您使用 uv,则运行

# Install pip
uv pip install pip

从源代码构建

下面是从源代码构建 Python 库的一些选项,可选择包含或不包含调试符号,并使用默认或自定义的扩展集。如果您在构建 DuckDB 主库时遇到问题,请务必查阅 DuckDB 构建文档

默认发布版、调试构建版或云存储

以下将使用默认扩展集(json、parquet、icu 和 core_functions)构建包。

发布版本构建

GEN=ninja BUILD_PYTHON=1 make release

调试版本构建

GEN=ninja BUILD_PYTHON=1 make debug

验证

python3 -c "import duckdb; print(duckdb.sql('SELECT 42').fetchall())"

添加扩展

在考虑静态链接扩展之前,您应该知道 Python 包目前对链接的扩展处理得不是很好。如果您不是真正需要将扩展内置,那么建议是坚持在运行时安装它们。请参阅 tools/pythonpkg/duckdb_extension_config.cmake 以获取与 Python 包一起构建的默认扩展列表。任何其他扩展都应被视为有问题。

话虽如此,如果您确实想尝试一下,方法如下。

有关构建 DuckDB 扩展的更多详细信息,请参阅文档

DuckDB 构建过程遵循以下逻辑来构建扩展:

  1. 首先,组成可能包含在构建中的完整扩展集。
  2. 然后,组成应从构建中排除的完整扩展集。
  3. 通过从包含的扩展集中减去排除的扩展集,来组装最终要编译的扩展集。

以下机制会添加到包含的扩展集中

机制 语法 / 示例
默认启用的内置扩展 extension/extension_config.cmake (约 30 个内置扩展)
默认启用的 Python 包扩展 tools/pythonpkg/duckdb_extension_config.cmake (json;parquet;icu)
分号分隔的包含列表 DUCKDB_EXTENSIONS=fts;tpch;json
标志 BUILD_TPCH=1, BUILD_JEMALLOC=1, BUILD_FTS=1, …
预设 BUILD_ALL_EXT=1 - 构建所有内部扩展
BUILD_ALL_IT_EXT=1 - 构建内部扩展
BUILD_ALL_OOT_EXT=1 - 构建所有外部扩展
自定义配置文件 DUCKDB_EXTENSION_CONFIGS=path/to/my.cmake
仅核心覆盖
仅在 DISABLE_BUILTIN_EXTENSIONS=1 时相关
CORE_EXTENSIONS=httpfs;fts

以下机制会添加到排除的扩展集中

机制 语法 / 示例
分号分隔的跳过列表 SKIP_EXTENSIONS=parquet;jemalloc
标志 DISABLE_PARQUET=1, DISABLE_CORE_FUNCTIONS=1, …
“无内置”开关
抛弃 *所有* 静态链接的扩展,除了 core_functions。使用 CORE_EXTENSIONS=… 将子集重新列入白名单。
DISABLE_BUILTIN_EXTENSIONS=1

显示所有已安装的扩展

python3 -c "import duckdb; print(duckdb.sql('SELECT extension_name, installed, description FROM duckdb_extensions();'))"

开发环境

本节将引导您完成以下步骤

  • 为开发创建 CMake 配置文件
  • 使用 lldb 调试 Python 扩展代码

您可以在 CLI 或 IDE 中执行此操作。下面的文档显示了 CLion 的配置,但您也应该能够使其与其他 IDE(如 VSCode)一起工作。

从 CLI 调试

运行此命令以配置在 CLI 上调试所需的 CMake 配置文件

GEN=ninja BUILD_PYTHON=1 PYTHON_DEV=1 make debug

这将处理以下事项:

  • 使用调试符号构建主 DuckDB 库和 Python 库。
  • 生成一个 compile-commands.json 文件,其中包含 CPython 和 pybind11 头文件,以便智能感知和 clang-tidy 检查在您的 IDE 中工作。
  • 在您的虚拟环境中安装所需的 Python 依赖项。

构建完成后,进行一次健全性检查以确保一切正常。

python3 -c "import duckdb; print(duckdb.sql('SELECT 42').fetchall())"

调试

基本方法是使用虚拟环境的 Python 解释器和您的脚本启动 lldb,然后设置断点并运行您的脚本。例如,给定一个名为 dataframe.df 的脚本,其内容如下:

import duckdb
print(duckdb.sql("select * from range(1000)").df())

以下应该可以工作:

lldb -- .venv/bin/python3 my_script.py
# Set a breakpoint
(lldb) br s -n duckdb::DuckDBPyRelation::FetchDF
Breakpoint 1: no locations (pending).
WARNING:  Unable to resolve breakpoint to any actual locations.
# The above warning is harmless - the library hasn't been imported yet

# Run the script
(lldb) r
...
    frame #0: 0x000000013025833c duckdb.cpython-310-darwin.so`duckdb::DuckDBPyRelation::FetchDF(this=0x00006000012f8d20, date_as_object=false) at pyrelation.cpp:808:7
   805   }
   806
   807   PandasDataFrame DuckDBPyRelation::FetchDF(bool date_as_object) {
-> 808     if (!result) {
   809       if (!rel) {
   810         return py::none();
   811       }
Target 0: (python3) stopped.

在 IDE / CLion 中调试

您应该能够在支持 lldb 的 IDE 中进行调试。以下是 CLion 的说明,但您可以将这些设置复制到您喜欢的 IDE 中。

配置 CMake 调试配置文件

以下 CMake 配置文件通过生成 compile-commands.json 文件来启用智能感知和 clang-tidy,以便您的 IDE 知道如何检查源代码,并确保 Python 包将在您的 Python 虚拟环境中构建和安装。

Settings -> Build, Execution, Deployment -> CMake 下,添加一个配置文件并按如下设置字段:

  • 名称: Debug
  • 构建类型: Debug
  • 生成器: Ninja
  • CMake 选项 (单行)
    -DCMAKE_PREFIX_PATH=$CMakeProjectDir$/.venv;$CMAKE_PREFIX_PATH
    -DPython3_EXECUTABLE=$CMakeProjectDir$/.venv/bin/python3
    -DBUILD_PYTHON=1
    -DPYTHON_DEV=1
    

为调试创建运行配置

Run -> Edit Configurations… 下,创建一个新的 CMake Application。使用以下值:

  • 名称: Python Debug
  • 目标: 所有目标
  • 可执行文件: [ABS_PATH_TO_YOUR_VENV]/bin/python3 (注意:这是一个软链接,有时 IDE 可能会尝试跟随它并填充实际可执行文件的路径,但这将不起作用)
  • 程序参数: $FilePath$
  • 工作目录: $ProjectFileDir$
  • 启动前: 构建 (此项应已设置)

这应该足够了:保存并关闭。

现在您可以在 C++ 文件中设置断点。然后,在编辑器中打开您的 Python 脚本,并使用此配置以调试模式运行 Python Debug

开发和存根文件

duckdb-stubs 中的 *.pyi 存根是手动维护的。与连接相关的存根是使用 tools/pythonpkg/scripts/ 中的专用脚本生成的。

  • generate_connection_stubs.py
  • generate_connection_wrapper_stubs.py

这些存根对于许多 IDE 中的自动完成功能很重要,因为基于静态分析的语言服务器无法自省 duckdb 的二进制模块。

要验证存根是否与实际实现匹配

python3 -m pytest tests/stubs

如果您向 DuckDB Python API 添加新方法,您需要手动将相应的类型提示添加到存根文件中。

py::objects 和 py::handles 是什么?

它们是 pybind11 提供的类,pybind11 是我们用来管理与 Python 环境交互的库。py::handle 是对原始 PyObject* 的直接封装,不管理任何引用。py::object 与 py::handle 类似,但它可以处理引用计数。

我说可以是因为它不必这样做,使用 py::reinterpret_borrow<py::object>(...) 我们可以创建一个不拥有所有权的 py::object,这本质上只是一个 py::handle,但如果原型要求 py::object,则不能使用 py::handle。

py::reinterpret_steal<py::object>(...) 创建一个拥有所有权的 py::object,这将增加 Python 对象的引用计数,并在 py::object 超出作用域时减少引用计数。

当直接与返回 PyObject* 的 Python 函数交互时,例如 PyDateTime_DATE_GET_TZINFO,您通常应该将调用封装在 py::reinterpret_steal 中以取得返回对象的所有权。

故障排除

Pip 失败并显示 No names found, cannot describe anything

如果您已经 fork 了 DuckDB,那么在未拉取标签的情况下构建 Python 包时可能会遇到问题。

# Check your remotes
git remote -v

# If you don't see upstream [email protected]:duckdb/duckdb.git, then add it
git remote add upstream git@github.com:duckdb/duckdb.git

# Now you can pull & push the tags
git fetch --tags upstream
git push --tags

使用 httpfs 扩展构建失败

当同时包含 httpfs 扩展和 Python 包时,在 OSX 上构建会失败。

ld: library not found for -lcrypto
clang: error: linker command failed with exit code 1 (use -v to see invocation)
error: command '/usr/bin/clang++' failed with exit code 1
ninja: build stopped: subcommand failed.
make: *** [release] Error 1

链接 httpfs 扩展是有问题的。如果可以的话,请在运行时安装它。

导入 DuckDB 失败并显示 symbol not found in flat namespace

如果您看到类似这样的错误

ImportError: dlopen(/usr/bin/python3/site-packages/duckdb/duckdb.cpython-311-darwin.so, 0x0002): symbol not found in flat namespace '_MD5_Final'

……那么您可能尝试链接了一个有问题的扩展。如上所述:tools/pythonpkg/duckdb_extension_config.cmake 包含了与 Python 包一起构建的默认扩展列表。任何其他扩展都可能导致问题。

Python 失败并显示 No module named 'duckdb.duckdb'

如果您在 tools/pythonpkg 目录下尝试 import duckdb,您可能会看到

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "/duckdb/tools/pythonpkg/duckdb/__init__.py", line 4, in <module>
    import duckdb.functional as functional
  File "/duckdb/tools/pythonpkg/duckdb/functional/__init__.py", line 1, in <module>
    from duckdb.duckdb.functional import (
ModuleNotFoundError: No module named 'duckdb.duckdb'

这是因为 Python 是从 duckdb 目录(即 tools/pythonpkg/duckdb/)导入的,而不是从已安装的包中导入的。您应该从不同的目录启动解释器。