DuckDB function modules#

DuckPlus 1.2 introduces domain-organised DuckDB function helpers so IDEs and documentation can point at real Python modules instead of generated registries. The helpers live under :mod:duckplus.functions and bind to the typed expression namespaces at import time via decorator side effects.

Importing the aggregate helpers#

The approximation aggregates ship in :mod:duckplus.functions.aggregate.approximation. Importing the package exposes helper functions directly from Python modules while keeping the registration side effects explicit:

from duckplus import functions

# Side-effect modules register helpers onto the typed aggregate namespace.
functions.approx_count_distinct
functions.histogram_filter

The :mod:duckplus.functions barrel re-exports the helpers and publishes a :pydataattr:duckplus.functions.SIDE_EFFECT_MODULES tuple so tests and tooling can verify which modules must be imported to register additional helpers. Downstream code can iterate over the tuple to ensure every decorator executes before relying on the helpers.【F:duckplus/functions/init.py†L5-L40】

import importlib
from duckplus import functions

for module_name in functions.SIDE_EFFECT_MODULES:
    importlib.import_module(module_name)

Working within a domain package#

Each domain package mirrors the public helpers it exposes. For aggregates, the :mod:duckplus.functions.aggregate barrel imports the approximation module and re-exports the helpers so callers can choose between duckplus.functions and duckplus.functions.aggregate imports while tests still assert the side-effect modules execute. The tuple published as :pydataattr:duckplus.functions.aggregate.SIDE_EFFECT_MODULES records every module with registration side effects.【F:duckplus/functions/aggregate/init.py†L1-L40】

Ordering-sensitive aggregates such as :func:duckplus.functions.aggregate.ordering.first follow the same pattern: the dedicated module defines the helpers with decorators, the aggregate barrel imports the module for its registration side effects, and the top-level :mod:duckplus.functions package re-exports the helpers for convenience.【F:duckplus/functions/aggregate/ordering.py†L1-L116】【F:duckplus/functions/init.py†L5-L40】

Central tendency helpers such as :func:duckplus.functions.aggregate.mode.mode and :func:duckplus.functions.aggregate.median.median are implemented in their own modules so documentation and introspection surface the decorator-backed helpers rather than generated methods. The aggregate barrel loads both :mod:duckplus.functions.aggregate.mode and :mod:duckplus.functions.aggregate.median alongside the other side-effect modules and re-exports :func:duckplus.functions.mode and :func:duckplus.functions.median for callers who import from the package root.【F:duckplus/functions/aggregate/mode.py†L1-L132】【F:duckplus/functions/aggregate/median.py†L1-L118】【F:duckplus/functions/aggregate/init.py†L1-L110】【F:duckplus/functions/init.py†L5-L52】

String concatenation helpers live in :mod:duckplus.functions.aggregate.string, which registers :func:duckplus.functions.aggregate.string.string_agg and its FILTER variant on the varchar aggregate namespace while the barrels re-export :func:duckplus.functions.string_agg for convenience. Tests assert the helper docstrings and module provenance so IDEs surface the decorator-backed implementations instead of generated wrappers.【F:duckplus/functions/aggregate/string.py†L1-L136】【F:duckplus/functions/aggregate/init.py†L1-L120】【F:duckplus/functions/init.py†L5-L48】【F:tests/test_function_import_barrels.py†L6-L94】

Bitstring aggregation helpers live in :mod:duckplus.functions.aggregate.bitstring, which registers :func:duckplus.functions.aggregate.bitstring.bitstring_agg and its FILTER variant on the generic aggregate namespace while the barrels re-export :func:duckplus.functions.bitstring_agg. Tests pin the new module inside the barrel SIDE_EFFECT_MODULES tuple and confirm the typed namespace exposes the decorator-backed docstrings so downstream tooling resolves the Python implementations instead of generated wrappers.【F:duckplus/functions/aggregate/bitstring.py†L1-L126】【F:duckplus/functions/aggregate/init.py†L1-L120】【F:duckplus/functions/init.py†L5-L48】【F:tests/test_function_import_barrels.py†L6-L94】【F:tests/test_typed_function_namespace.py†L201-L231】

Summation helpers now live in :mod:duckplus.functions.aggregate.summation, which defines :func:duckplus.functions.aggregate.summation.sum (and its FILTER variant) for both generic and numeric namespaces alongside the :func:duckplus.functions.aggregate.summation.product aggregate for numeric expressions. The aggregate and top-level barrels import the module so the helpers register at import time, and regression tests assert the side-effect tuple lists the module while the typed namespaces expose the decorator-backed docstrings.【F:duckplus/functions/aggregate/summation.py†L1-L236】【F:duckplus/functions/aggregate/init.py†L1-L170】【F:duckplus/functions/init.py†L5-L60】【F:tests/test_function_import_barrels.py†L6-L120】【F:tests/test_typed_function_namespace.py†L200-L260】

Regression and covariance helpers live in :mod:duckplus.functions.aggregate.regression, which registers the covar_* and regr_* aggregates directly onto the numeric namespace with decorator-backed metadata. The aggregate barrels import the module explicitly so side-effect tests pin its presence, and typed-namespace checks validate the docstrings for the regression helpers.【F:duckplus/functions/aggregate/regression.py†L1-L390】【F:duckplus/functions/aggregate/init.py†L1-L190】【F:duckplus/functions/init.py†L5-L92】【F:tests/test_function_import_barrels.py†L1-L120】【F:tests/test_typed_function_namespace.py†L200-L320】

Average helpers live in :mod:duckplus.functions.aggregate.averages, which registers :func:duckplus.functions.aggregate.averages.avg and :func:duckplus.functions.aggregate.averages.mean (plus their FILTER variants) onto the generic and numeric aggregate namespaces. The aggregate barrel and top-level :mod:duckplus.functions package import the module so the helpers register at import time, while the regression tests assert the side-effect tuple lists the new module and that the typed namespaces surface the decorator-backed docstrings.【F:duckplus/functions/aggregate/averages.py†L1-L184】【F:duckplus/functions/aggregate/init.py†L1-L200】【F:duckplus/functions/init.py†L5-L60】【F:tests/test_function_import_barrels.py†L6-L140】【F:tests/test_typed_function_namespace.py†L200-L360】

Extremum-by-value helpers live in :mod:duckplus.functions.aggregate.extremum_by_value, which registers :func:duckplus.functions.aggregate.extremum_by_value.max_by and :func:duckplus.functions.aggregate.extremum_by_value.min_by (plus their FILTER variants) across blob, varchar, numeric, and generic namespaces. The aggregate barrels import the module explicitly so side-effect coverage stays testable, and regression tests assert that the decorator-backed docstrings—including the ANY[] overload metadata—originate from the Python implementation instead of the generated namespace.【F:duckplus/functions/aggregate/extremum_by_value.py†L1-L272】【F:duckplus/functions/aggregate/init.py†L1-L160】【F:duckplus/functions/init.py†L5-L76】【F:tests/test_function_import_barrels.py†L1-L84】【F:tests/test_typed_function_namespace.py†L248-L356】

Basic extrema helpers live in :mod:duckplus.functions.aggregate.extrema, which registers :func:duckplus.functions.aggregate.extrema.max and :func:duckplus.functions.aggregate.extrema.min (plus FILTER variants) across boolean, blob, varchar, numeric, and generic aggregate namespaces. The aggregate and top-level barrels import the module for its registration side effects, and tests assert both the barrel re-exports and the typed namespace provenance so callers resolve the decorator-backed overload metadata instead of the generated wrappers.【F:duckplus/functions/aggregate/extrema.py†L1-L302】【F:duckplus/functions/aggregate/init.py†L1-L140】【F:duckplus/functions/init.py†L5-L72】【F:tests/test_function_import_barrels.py†L1-L94】【F:tests/test_typed_function_namespace.py†L150-L360】

Quantile helpers live in :mod:duckplus.functions.aggregate.quantiles, which registers :func:duckplus.functions.aggregate.quantiles.quantile, :func:duckplus.functions.aggregate.quantiles.quantile_disc, and :func:duckplus.functions.aggregate.quantiles.quantile_cont (plus FILTER variants) onto the generic aggregate namespace. The aggregate barrels import the module so percentile helpers register at import time, and regression tests pin the decorator-backed docstrings to the Python implementation rather than the generated namespace.【F:duckplus/functions/aggregate/quantiles.py†L1-L223】【F:duckplus/functions/aggregate/init.py†L1-L140】【F:tests/test_function_import_barrels.py†L6-L120】【F:tests/test_typed_function_namespace.py†L212-L264】

List aggregation helpers live in :mod:duckplus.functions.aggregate.list, which registers :func:duckplus.functions.aggregate.list.list and its FILTER variant onto the generic aggregate namespace with the DuckDB overload metadata captured directly in Python code. The aggregate barrels expose the helpers alongside the other side-effect modules, and regression tests assert both the re-export provenance and the decorator-backed docstrings so IDEs surface the new module.【F:duckplus/functions/aggregate/list.py†L1-L120】【F:duckplus/functions/aggregate/init.py†L1-L148】【F:duckplus/functions/init.py†L5-L44】【F:tests/test_function_import_barrels.py†L6-L130】【F:tests/test_typed_function_namespace.py†L175-L245】

Map aggregation helpers live in :mod:duckplus.functions.aggregate.map, which registers :func:duckplus.functions.aggregate.map.map onto the generic aggregate namespace with the DuckDB overload metadata defined directly in Python. The aggregate barrel loads the module for its registration side effects while the top-level :mod:duckplus.functions package re-exports :func:duckplus.functions.map and tests assert the helper’s provenance and docstring so downstream tooling resolves the decorator-backed implementation.【F:duckplus/functions/aggregate/map.py†L1-L86】【F:duckplus/functions/aggregate/init.py†L1-L148】【F:duckplus/functions/init.py†L5-L44】【F:tests/test_function_import_barrels.py†L6-L140】【F:tests/test_typed_function_namespace.py†L200-L256】

Arg-extrema helpers for blob, varchar, numeric, and generic expressions now live in :mod:duckplus.functions.aggregate.arg_extrema. The module registers arg_max/arg_min (and their FILTER and _null variants) onto each aggregate namespace while preserving DuckDB’s overload metadata—including the ANY[] signatures—in pure Python definitions validated by the namespace tests.【F:duckplus/functions/aggregate/arg_extrema.py†L1-L438】【F:tests/test_typed_function_namespace.py†L248-L296】

The approximation module defines helpers with the :func:duckplus.functions._base.register_duckdb_function decorator. Each helper holds the DuckDB function signature, forwards through :func:duckplus.functions._base.invoke_duckdb_function, and returns the typed expression category expected by the aggregate namespace. All metadata lives in Python code so docstrings and language servers mirror the shipped behaviour without parsing generated registries.【F:duckplus/functions/aggregate/approximation.py†L1-L200】【F:duckplus/functions/_base.py†L1-L68】

When to add new modules#

Follow the approximation suite’s structure when migrating additional DuckDB helpers. Group related functions into new modules (for example, duckplus.functions.aggregate.quantiles now consolidates percentile helpers) and list them inside the package-level SIDE_EFFECT_MODULES tuple. This keeps imports explicit, makes side effects discoverable for tests, and ensures future releases continue to rely on direct Python definitions instead of runtime registries.