Skip to content

Commit 69b0ddb

Browse files
timsaucerclaude
andcommitted
fix(udf): name property reports FFI capsule name, not the ignored ctor arg
Previously `ScalarUDF.name`, `AggregateUDF.name`, and `WindowUDF.name` cached the `name` argument passed to `__init__`. The FFI-capsule branch discards that argument and constructs from the capsule directly, so the cached value was wrong — e.g. `udf(IsNullUDF()).name` returned `"<class 'datafusion_ffi_example.IsNullUDF'>"` (the factory-synthesized string from `from_pycapsule`) rather than the capsule's own `my_custom_is_null`. Expose a `name` getter on `PyScalarUDF`, `PyAggregateUDF`, `PyWindowUDF` that forwards to the underlying `UDF::name()`, and have each Python property delegate to it. Same path for Python-defined and FFI-imported UDFs, no stale cache. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent f25aa87 commit 69b0ddb

4 files changed

Lines changed: 36 additions & 9 deletions

File tree

crates/core/src/udaf.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,4 +368,9 @@ impl PyAggregateUDF {
368368
fn __repr__(&self) -> PyResult<String> {
369369
Ok(format!("AggregateUDF({})", self.function.name()))
370370
}
371+
372+
#[getter]
373+
fn name(&self) -> &str {
374+
self.function.name()
375+
}
371376
}

crates/core/src/udf.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,4 +261,9 @@ impl PyScalarUDF {
261261
fn __repr__(&self) -> PyResult<String> {
262262
Ok(format!("ScalarUDF({})", self.function.name()))
263263
}
264+
265+
#[getter]
266+
fn name(&self) -> &str {
267+
self.function.name()
268+
}
264269
}

crates/core/src/udwf.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,11 @@ impl PyWindowUDF {
273273
fn __repr__(&self) -> PyResult<String> {
274274
Ok(format!("WindowUDF({})", self.function.name()))
275275
}
276+
277+
#[getter]
278+
fn name(&self) -> &str {
279+
self.function.name()
280+
}
276281
}
277282

278283
#[derive(Debug)]

python/datafusion/user_defined.py

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ def __init__(
132132
133133
See helper method :py:func:`udf` for argument details.
134134
"""
135-
self._name = name
136135
if hasattr(func, "__datafusion_scalar_udf__"):
137136
self._udf = df_internal.ScalarUDF.from_pycapsule(func)
138137
return
@@ -144,8 +143,13 @@ def __init__(
144143

145144
@property
146145
def name(self) -> str:
147-
"""Return the registered name of this UDF."""
148-
return self._name
146+
"""Return the registered name of this UDF.
147+
148+
For UDFs imported via the FFI capsule protocol, this is the
149+
name the capsule itself reports — not the ``name`` argument
150+
passed to the constructor (which is ignored on the FFI path).
151+
"""
152+
return self._udf.name
149153

150154
def __repr__(self) -> str:
151155
"""Print a string representation of the Scalar UDF."""
@@ -400,7 +404,6 @@ def __init__(
400404
See :py:func:`udaf` for a convenience function and argument
401405
descriptions.
402406
"""
403-
self._name = name
404407
if hasattr(accumulator, "__datafusion_aggregate_udf__"):
405408
self._udaf = df_internal.AggregateUDF.from_pycapsule(accumulator)
406409
return
@@ -427,8 +430,13 @@ def __init__(
427430

428431
@property
429432
def name(self) -> str:
430-
"""Return the registered name of this UDAF."""
431-
return self._name
433+
"""Return the registered name of this UDAF.
434+
435+
For UDAFs imported via the FFI capsule protocol, this is the
436+
name the capsule itself reports — not the ``name`` argument
437+
passed to the constructor (which is ignored on the FFI path).
438+
"""
439+
return self._udaf.name
432440

433441
def __repr__(self) -> str:
434442
"""Print a string representation of the Aggregate UDF."""
@@ -833,7 +841,6 @@ def __init__(
833841
See :py:func:`udwf` for a convenience function and argument
834842
descriptions.
835843
"""
836-
self._name = name
837844
if hasattr(func, "__datafusion_window_udf__"):
838845
self._udwf = df_internal.WindowUDF.from_pycapsule(func)
839846
return
@@ -843,8 +850,13 @@ def __init__(
843850

844851
@property
845852
def name(self) -> str:
846-
"""Return the registered name of this UDWF."""
847-
return self._name
853+
"""Return the registered name of this UDWF.
854+
855+
For UDWFs imported via the FFI capsule protocol, this is the
856+
name the capsule itself reports — not the ``name`` argument
857+
passed to the constructor (which is ignored on the FFI path).
858+
"""
859+
return self._udwf.name
848860

849861
def __repr__(self) -> str:
850862
"""Print a string representation of the Window UDF."""

0 commit comments

Comments
 (0)