Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
0d8b7e6
WIP: nanobind cutover — build system + umbrella + renames (not yet bu…
evertlammerts Jun 26, 2026
ad7e3ca
WIP nanobind cutover (2): object wrappers, casters, more renames
evertlammerts Jun 26, 2026
c7deaa8
WIP nanobind cutover (3): numpy nb::ndarray port + .cast<>() + renames
evertlammerts Jun 26, 2026
c189ee8
nanobind: fix class_ holders, pybind11:: stragglers, pyconnection_def…
evertlammerts Jun 26, 2026
cd76893
nanobind: conversions, iteration ref->value, tuple/list building, cap…
evertlammerts Jun 26, 2026
f4f818d
nanobind: module-init macros, args/kwargs annotation rules, init->new…
evertlammerts Jun 26, 2026
033ef66
nanobind: Value(py::str) -> cast, int_ explicit casts, .none() on con…
evertlammerts Jun 26, 2026
20be65a
nanobind: register_exception shim, exception translator, tuple builds…
evertlammerts Jun 26, 2026
f09ca7e
nanobind: more str/bytes/Identifier conversions, tuple iteration, typ…
evertlammerts Jun 26, 2026
78b1cc0
nanobind: capsule.data, py::args binding fixes (Project/FunctionExpre…
evertlammerts Jun 26, 2026
965e81a
nanobind: .none() on return_type bound-type arg
evertlammerts Jun 26, 2026
f80cb9e
nanobind: fix null py::str()/py::int_() default-construction in excep…
evertlammerts Jun 26, 2026
c9f99f2
nanobind: fix accessor->wrapper reinterpret crashes (Series/Index/lis…
evertlammerts Jun 26, 2026
262c70a
nanobind: fix FrameLocalsProxy (PEP 667) replacement-scan bad_cast; r…
evertlammerts Jun 26, 2026
1b115b6
nanobind: __exit__ pointer-self + .none() args
evertlammerts Jun 26, 2026
0a23723
nanobind: enum-instance acceptance in STRING_INT caster; .none() on j…
evertlammerts Jun 26, 2026
9286f8c
nanobind: TransformPyConfigDict str-ify values; filesystem timestamp …
evertlammerts Jun 26, 2026
fe4fb74
nanobind: DuckDBPyType::TryConvert helper to restore implicit type co…
evertlammerts Jun 26, 2026
82c6ebf
nanobind: UDF signature mappingproxy->dict; TryConvert for UDF parame…
evertlammerts Jun 26, 2026
06bb706
nanobind: numpy __version__ string->tuple conversion in UDF path
evertlammerts Jun 26, 2026
eb9f87a
nanobind: custom shared_ptr<DuckDBPyType> caster (keep convert flag f…
evertlammerts Jun 26, 2026
d35c15c
nanobind: simplify DuckDBPyType from_cpp (no type_hook)
evertlammerts Jun 26, 2026
a65adc0
nanobind: UDF kind via enum .name; TryConvert clears PyErr
evertlammerts Jun 26, 2026
5cb74bd
nanobind: fix py::str(accessor) reinterpret bug (wrap in py::object s…
evertlammerts Jun 26, 2026
1475be6
nanobind: .none() on ConstantExpression value (no-default py::object …
evertlammerts Jun 26, 2026
1d0bd68
nanobind: DuckDBPyExpression convert-flag caster + None handling (kil…
evertlammerts Jun 28, 2026
c72040e
fix cmakelists
evertlammerts Jun 28, 2026
976dc5b
Fix smart pointer issues
evertlammerts Jun 29, 2026
0f57ddf
fix pandas
evertlammerts Jun 29, 2026
18692be
long tail fixes
evertlammerts Jun 29, 2026
fc677e2
remove Py 3.10 support
evertlammerts Jun 29, 2026
208082a
fix for msvc
evertlammerts Jun 29, 2026
c551c77
fix deployment target for python 3.11
evertlammerts Jun 29, 2026
6b87a2e
fix None on expressions
evertlammerts Jun 30, 2026
f436f65
weakrefs work again
evertlammerts Jun 30, 2026
cd43e38
fix asan issues
evertlammerts Jun 30, 2026
6324191
tuple field assignment wrapper
evertlammerts Jun 30, 2026
1231037
reorg of files and PyUtil extraction
evertlammerts Jun 30, 2026
5c67d68
rename
evertlammerts Jun 30, 2026
8ae5c46
bulk cleanup
evertlammerts Jun 30, 2026
2983c92
fix format
evertlammerts Jun 30, 2026
8d3e3c2
Fix ~6x regression in numpy columnar LIST/ARRAY conversion
evertlammerts Jul 1, 2026
3c4528f
fix regressions and get on par with main
evertlammerts Jul 1, 2026
38eaa4c
pre-commit fixes
evertlammerts Jul 1, 2026
40c1b32
trim
evertlammerts Jul 1, 2026
3cc4286
Merge main (#519); superseded by the nanobind re-implementation
evertlammerts Jul 1, 2026
501a3fc
ruff fixes
evertlammerts Jul 1, 2026
a875747
improve allocation for numpy
evertlammerts Jul 1, 2026
75a6489
review fixes
evertlammerts Jul 1, 2026
91bdd3a
bump submodule and fix drift
evertlammerts Jul 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/code_quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,4 @@ jobs:

- name: pre-commit (--all-files)
run: |
uvx pre-commit run --show-diff-on-failure --color=always --all-files
uvx --python 3.12 pre-commit run --show-diff-on-failure --color=always --all-files
5 changes: 2 additions & 3 deletions .github/workflows/packaging_wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: [ cp314 ]
python: [ cp311, cp314 ]
platform:
- { os: windows-2022, arch: amd64, cibw_system: win }
- { os: windows-11-arm, arch: ARM64, cibw_system: win }
Expand Down Expand Up @@ -127,7 +127,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python: [ cp310, cp311, cp312, cp313 ]
python: [ cp311, cp312, cp313 ]
platform:
- { os: windows-2025, arch: amd64, cibw_system: win }
- { os: windows-11-arm, arch: ARM64, cibw_system: win }
Expand All @@ -143,7 +143,6 @@ jobs:
- { minimal: true, python: cp312 }
- { minimal: true, python: cp313 }
- { minimal: true, platform: { arch: universal2 } }
- { python: cp310, platform: { os: windows-11-arm, arch: ARM64 } }
runs-on: ${{ matrix.platform.os }}
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
Expand Down
11 changes: 7 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This is the **production** duckdb-python client — the `duckdb` package on PyPI
- **Package name**: `duckdb`
- **Bindings**: pybind11
- **Build backend**: `duckdb_packaging.build_backend` (custom wrapper around scikit-build-core)
- **Supported Python**: 3.10, 3.11, 3.12, 3.13, 3.14
- **Supported Python**: 3.11, 3.12, 3.13, 3.14
- **Free-threaded Python**: not supported in this client. A separate prototype client based on DuckDB's C API targets free-threading, Stable ABI, and multi-interpreter support.

## IMPORTANT: build before running anything
Expand Down Expand Up @@ -115,7 +115,7 @@ uv sync --no-build-isolation -v --reinstall -p 3.11
uv sync --no-build-isolation -v --reinstall -p 3.14
```

Supported: `3.10`, `3.11`, `3.12`, `3.13`, `3.14`. Do **not** use free-threaded variants (`3.13t`, `3.14t`) — the production client does not support them.
Supported: `3.11`, `3.12`, `3.13`, `3.14`. Do **not** use free-threaded variants (`3.13t`, `3.14t`) — the production client does not support them.

### Build configuration reference

Expand Down Expand Up @@ -188,8 +188,11 @@ uv run ruff format src/ tests/
# Type checking (mypy — strict mode, see [tool.mypy] in pyproject.toml)
uv run mypy

# Pre-commit hooks (configured in .pre-commit-config.yaml)
uvx pre-commit run --all-files
# Pre-commit hooks (configured in .pre-commit-config.yaml). Install pinned to 3.12
# (cmakelang crashes on 3.14; keeps hooks off the build interpreter):
uv tool install --python 3.12 pre-commit
pre-commit install # git hook, runs on commit
pre-commit run --all-files # run across the tree
```

## Debugging
Expand Down
51 changes: 42 additions & 9 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.29)

project(duckdb_py LANGUAGES CXX)
project(duckdb_python LANGUAGES CXX)

# Always use C++17
set(CMAKE_CXX_STANDARD 17)
Expand Down Expand Up @@ -35,8 +35,26 @@ endif()
# ────────────────────────────────────────────
# Dependencies
# ────────────────────────────────────────────
# PyBind11
find_package(pybind11 REQUIRED CONFIG)
# nanobind (requires Python to be located first; pybind11 used to do this
# internally)
find_package(
Python
COMPONENTS Interpreter Development.Module NumPy
REQUIRED)
# Nanobind ships its CMake config inside site-packages/nanobind/cmake, so
# find_package() can't discover it unless we set it. (scikit-build-core does
# this as well)
if(NOT nanobind_ROOT)
execute_process(
COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
OUTPUT_STRIP_TRAILING_WHITESPACE
OUTPUT_VARIABLE nanobind_ROOT)
endif()
find_package(nanobind CONFIG REQUIRED)
# Build nanobind's core support library up front so the object libraries below
# (which include nanobind headers via the umbrella) compile against its include
# dirs + Python headers + flags.
nanobind_build_library(nanobind-static)

# DuckDB
include(cmake/duckdb_loader.cmake)
Expand All @@ -49,26 +67,42 @@ duckdb_add_library(duckdb_target)

# Bundle in INTERFACE library
add_library(_duckdb_dependencies INTERFACE)
target_link_libraries(_duckdb_dependencies INTERFACE pybind11::pybind11
target_link_libraries(_duckdb_dependencies INTERFACE nanobind-static
duckdb_target)
# Also add include directory
target_include_directories(
_duckdb_dependencies
INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/duckdb_py/include>
)
INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src/include>)

# We link duckdb_static. Without this define, duckdb.h marks C API symbols
# __declspec(dllimport) on Windows, producing unresolvable __imp_* references at
# link time. No-op on non-Windows.
target_compile_definitions(_duckdb_dependencies INTERFACE DUCKDB_STATIC_BUILD)

# Optional AddressSanitizer instrumentation of the Python binding objects ONLY.
# Every binding object library consumes _duckdb_dependencies (for headers) and
# _duckdb links it, so adding the flag here instruments the bindings and links
# the ASAN runtime, while the engine target (duckdb_target, which does NOT
# consume this) stays uninstrumented and keeps hitting the sccache cache. ASAN's
# allocator is process-global, so heap errors involving the instrumented binding
# code are still caught. OFF by default; enable with -DDUCKDB_PY_ASAN=ON.
option(DUCKDB_PY_ASAN
"Instrument the Python binding objects with AddressSanitizer" OFF)
if(DUCKDB_PY_ASAN)
target_compile_options(
_duckdb_dependencies INTERFACE -fsanitize=address -fno-omit-frame-pointer
-g)
target_link_options(_duckdb_dependencies INTERFACE -fsanitize=address)
endif()

# ────────────────────────────────────────────
# Descend into the real DuckDB‑Python sources
# ────────────────────────────────────────────
add_subdirectory(src/duckdb_py)
add_subdirectory(src)

pybind11_add_module(
nanobind_add_module(
_duckdb
NB_STATIC
$<TARGET_OBJECTS:python_src>
$<TARGET_OBJECTS:python_arrow>
$<TARGET_OBJECTS:python_common>
Expand All @@ -77,7 +111,6 @@ pybind11_add_module(
$<TARGET_OBJECTS:python_native>
$<TARGET_OBJECTS:python_numpy>
$<TARGET_OBJECTS:python_pandas>
$<TARGET_OBJECTS:python_pybind11>
$<TARGET_OBJECTS:python_connection>
$<TARGET_OBJECTS:python_expression>
$<TARGET_OBJECTS:python_relation>
Expand Down
12 changes: 12 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,18 @@

See the [instructions on duckdb.org](https://duckdb.org/docs/stable/dev/building/python).

### Pre-commit hooks

Formatting and linting run through [pre-commit](https://pre-commit.com). Install it pinned to Python 3.12 (the `cmake-format` hook's `cmakelang` dependency crashes on 3.14) so the hooks stay independent of your build interpreter, which may be 3.13 or 3.14t:

```bash
uv tool install --python 3.12 pre-commit
pre-commit install # git hook, runs on `git commit`
pre-commit run --all-files # run across the tree
```

The same checks run in CI.

## General Guidelines

### **Did you find a bug?**
Expand Down
3 changes: 2 additions & 1 deletion duckdb/experimental/spark/_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,10 @@
from collections.abc import Callable, Iterable, Sized
from typing import Literal, TypeVar

from numpy import float32, float64, int32, int64, ndarray
from typing_extensions import Protocol, Self

from numpy import float32, float64, int32, int64, ndarray

F = TypeVar("F", bound=Callable)
T_co = TypeVar("T_co", covariant=True)

Expand Down
2 changes: 1 addition & 1 deletion external/duckdb
Submodule duckdb updated 65 files
+1 −1 extension/icu/icu-datefunc.cpp
+21 −11 extension/icu/icu-datesub.cpp
+5 −0 scripts/parser/grammar_types.yml
+54 −0 src/function/scalar/geometry/geometry_functions.cpp
+1 −0 src/include/duckdb/common/multi_file/multi_file_states.hpp
+13 −3 src/include/duckdb/function/scalar_function.hpp
+11 −5 src/include/duckdb/main/client_context.hpp
+2 −0 src/include/duckdb/main/extension_entries.hpp
+94 −0 src/include/duckdb/main/parse_iterator.hpp
+78 −0 src/include/duckdb/main/statement_iterator.hpp
+54 −1 src/include/duckdb/optimizer/outer_join_simplification.hpp
+12 −0 src/include/duckdb/parser/parser.hpp
+6 −1 src/include/duckdb/parser/peg/inlined_grammar.gram
+6 −1 src/include/duckdb/parser/peg/inlined_grammar.hpp
+14 −0 src/include/duckdb/parser/peg/transformer/peg_transformer.hpp
+0 −4 src/include/duckdb/storage/statistics/geometry_stats.hpp
+2 −0 src/main/CMakeLists.txt
+121 −71 src/main/client_context.cpp
+11 −1 src/main/connection.cpp
+185 −0 src/main/parse_iterator.cpp
+68 −0 src/main/statement_iterator.cpp
+408 −97 src/optimizer/outer_join_simplification.cpp
+33 −1 src/optimizer/pushdown/pushdown_get.cpp
+5 −0 src/optimizer/statistics/expression/propagate_cast.cpp
+4 −3 src/optimizer/type_pushdown.cpp
+61 −52 src/parser/parser.cpp
+6 −1 src/parser/peg/grammar/statements/select.gram
+37 −0 src/parser/peg/transformer/transform_generated.cpp
+19 −0 src/parser/peg/transformer/transform_select.cpp
+8 −16 src/planner/filter/expression_filter.cpp
+15 −10 src/planner/filter/table_filter_bloom_function.cpp
+5 −1 src/planner/filter/table_filter_dynamic_function.cpp
+5 −1 src/planner/filter/table_filter_optional_function.cpp
+12 −7 src/planner/filter/table_filter_prefix_range_function.cpp
+5 −1 src/planner/filter/table_filter_selectivity_optional_function.cpp
+0 −113 src/storage/statistics/geometry_stats.cpp
+1 −0 test/api/CMakeLists.txt
+335 −0 test/api/test_parse_statement_iterator.cpp
+5 −1 test/extension/loadable_extension_demo.cpp
+1 −1 test/optimizer/CMakeLists.txt
+72 −0 test/optimizer/filter_pushdown_alias.cpp
+232 −0 test/optimizer/outer_join_simplification.test
+2 −3 test/optimizer/pushdown/csv_type_pushdown.test
+2 −2 test/sql/copy/csv/test_copy_gzip_large_block.test_slow
+9 −9 test/sql/copy/parquet/parquet_expression_filter_pruning.test
+27 −0 test/sql/extensions/load_use_extension_one_query.test
+137 −0 test/sql/function/timestamp/test_icu_datediff.test
+1 −1 test/sql/logging/enable_logging_file_no_path.test
+1 −1 test/sql/parallelism/interquery/concurrent_drop_schema_macro_table.test_slow
+48 −0 test/sql/parser/fetch_first.test
+3 −2 test/sql/pivot/multi_statement_parsing.cpp
+1 −1 test/sql/settings/errors_as_json.test
+2 −2 test/sql/storage/compression/alp/alp_corrupted_file.test
+2 −2 test/sql/storage/compression/alp/alp_corrupted_offsets.test
+2 −2 test/sql/storage/compression/alp/alp_corrupted_vector_size.test
+2 −2 test/sql/storage/compression/alprd/alprd_corrupted_dict_size.test
+1 −1 test/sql/storage/types/variant/variant_case_sensitive_fields_shredded.test
+2 −2 test/sql/storage/types/variant/variant_parquet_shredding_bug.test_slow
+4 −4 test/sql/storage/vacuum/vacuum_rebuild_indexes_attach.test_slow
+1 −1 test/sql/storage/vacuum/vacuum_rebuild_indexes_attach_overrides_global.test_slow
+1 −1 test/sql/types/geo/geometry_shred_deletes.test
+2 −2 test/sql/types/geo/geometry_shred_more.test
+3 −3 test/sql/types/struct/test_tuple.test
+1 −1 test/sqlite/sqllogic_command.cpp
+7 −2 tools/shell/shell.cpp
17 changes: 13 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ dynamic = ["version"]
description = "DuckDB in-process database"
readme = "README.md"
keywords = ["DuckDB", "Database", "SQL", "OLAP"]
requires-python = ">=3.10.0"
requires-python = ">=3.11.0"
classifiers = [
"Development Status :: 5 - Production/Stable",
"License :: OSI Approved :: MIT License",
Expand All @@ -25,7 +25,6 @@ classifiers = [
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
Expand Down Expand Up @@ -63,8 +62,12 @@ build-backend = "duckdb_packaging.build_backend"
backend-path = ["./"]
requires = [
"scikit-build-core>=0.11.4",
"pybind11[global]>=2.6.0",
"nanobind>=2.0",
"setuptools_scm>=8.0",
# numpy C API headers (PyArray_Empty in the result path). Building against numpy 2.x yields a
# binary compatible with numpy >=1.19 AND 2.x at runtime (numpy 2.0 backward-compat), so the
# unpinned runtime numpy range is preserved. Build-time only; the runtime numpy dep is unchanged.
"numpy>=2.0",
]

[tool.scikit-build]
Expand Down Expand Up @@ -246,6 +249,7 @@ test = [ # dependencies used for running tests
"pytest-reraise",
"pytest-timeout",
"pytest-timestamper",
"pytest-xdist", # parallel test execution (-n auto); without this `uv sync --reinstall` prunes a manual install
"coverage",
"gcovr; sys_platform != 'win32' or platform_machine != 'ARM64'",
"gcsfs; sys_platform != 'win32' or platform_machine != 'ARM64'",
Expand Down Expand Up @@ -294,7 +298,7 @@ pypi = [ # dependencies used by the pypi cleanup script
build = [
"cmake>=3.29.0",
"ninja>=1.10",
"pybind11[global]>=2.6.0",
"nanobind>=2.0",
"scikit_build_core>=0.11.4",
"setuptools_scm>=8.0",
]
Expand Down Expand Up @@ -474,6 +478,11 @@ before-build = ["yum install -y ccache"]

[tool.cibuildwheel.macos]
before-build = ["brew install ccache"]
# nanobind uses C++17 aligned new/delete (std::align_val_t), which the runtime only provides on macOS 10.13+.
# cp311's framework defaults to a 10.9 deployment target (used for the x86_64 slice of x86_64/universal2
# wheels), so nanobind fails to compile there; cp312+ frameworks already target 10.13+. Pin 10.14 so every CPython
# version builds (arm64 slices are 11.0 regardless).
environment = { MACOSX_DEPLOYMENT_TARGET = "10.14" }

[tool.cibuildwheel.windows]
before-build = ["choco install ccache"]
2 changes: 1 addition & 1 deletion src/duckdb_py/CMakeLists.txt → src/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# this is used for clang-tidy checks
add_subdirectory(pyrelation)
add_subdirectory(pyexpression)
add_subdirectory(pybind11)
add_subdirectory(numpy)
add_subdirectory(native)
add_subdirectory(jupyter)
Expand All @@ -25,6 +24,7 @@ add_library(
pyrelation.cpp
pyresult.cpp
pystatement.cpp
pyutil.cpp
python_dependency.cpp
python_import_cache.cpp
python_replacement_scan.cpp
Expand Down
File renamed without changes.
Loading
Loading