ibis icon indicating copy to clipboard operation
ibis copied to clipboard

feat: support for keyword-only arguments in scalar udfs

Open tswast opened this issue 4 months ago • 1 comments

Is your feature request related to a problem?

In BigQuery, st_buffer takes several arguments besides the radius property provided by the GeoBuffer op in ibis.

https://github.com/ibis-project/ibis/blob/db1a727b3c4c75e8e4af0f7ef3d0101d26d4450b/ibis/expr/operations/geospatial.py#L362

ST_BUFFER(
    geography,
    buffer_radius
    [, num_seg_quarter_circle => num_segments]
    [, use_spheroid => boolean_expression]
    [, endcap => endcap_style]
    [, side => line_side])

See: https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_buffer for details.

I'd like to workaround this by using @ibis_udf.scalar.builtin, but when I do so, I get an error from BigQuery:

Signature: ST_BUFFER(GEOGRAPHY, FLOAT64, [FLOAT64], [BOOL], [endcap => STRING], [side => STRING])
E               Positional argument at 5 is invalid because argument `endcap` can only be referred to by name at [7:3] [{'@type': 'type.googleapis.com/google.rpc.DebugInfo', 'detail': '[INVALID_INPUT] message=[INVALID_INPUT] message=QUERY_ERROR: [No matching signature for function ST_BUFFER\n  Argument types: GEOGRAPHY, INT64, FLOAT64, BOOL, STRING, STRING\n  Signature: ST_BUFFER(GEOGRAPHY, FLOAT64, [FLOAT64], [BOOL], [endcap => STRING], [side => STRING])

What is the motivation behind your request?

I'd like for bigframes.bigquery to have all possible SQL methods, translated to Python. https://github.com/googleapis/python-bigquery-dataframes/pull/1963 for a work-in-progress PR adding a few of these, if you're curious.

Describe the solution you'd like

I'd like keyword-only arguments in Python to get passed by keyword in SQL. So, the function in this case would look like this:

@ibis_udf.scalar.builtin
def st_buffer(
    geography: ibis_dtypes.Geography,
    buffer_radius: ibis_dtypes.Float64,
    *,
    num_seg_quarter_circle: ibis_dtypes.Float64,
    use_spheroid: ibis_dtypes.Boolean,
    endcap: ibis_dtypes.String,
    side: ibis_dtypes.String,
) -> ibis_dtypes.Geography:
    ...

https://github.com/ibis-project/ibis/blob/db1a727b3c4c75e8e4af0f7ef3d0101d26d4450b/ibis/backends/sql/compilers/base.py#L1263

What version of ibis are you running?

A fork of 10.3.0, I believe, but the relevant operator still has this limitation on HEAD

What backend(s) are you using, if any?

BigQuery

Code of Conduct

  • [x] I agree to follow this project's Code of Conduct

tswast avatar Aug 05 '25 18:08 tswast

I played a little around with this in BigFrames. I've had success getting SQLGlot to generate this syntax by passing args like this:

    def visit_GeoRegionStats(self, op, *, arg, raster_id, band, include, options):
        args = [arg, raster_id]
        if op.band:
            args.append(sge.Kwarg(this="band", expression=band))
        if op.include:
            args.append(sge.Kwarg(this="include", expression=include))
        if op.options:
            args.append(sge.Kwarg(this="options", expression=options))
        return sge.func("ST_REGIONSTATS", *args)

tswast avatar Oct 29 '25 19:10 tswast