From 968a198d03658f7b1ca427b54842bc2da1ea822c Mon Sep 17 00:00:00 2001 From: Stefan Wang <1fannnw@gmail.com> Date: Tue, 8 Sep 2026 12:46:11 -0700 Subject: [PATCH 1/4] GH-51228: [Python] Reject invalid tabular function registry Validate call_tabular_function registry inputs before casting them to the native FunctionRegistry pointer. Generated-by: GitHub Copilot CLI (Claude Opus 5) Signed-off-by: Stefan Wang <1fannnw@gmail.com> --- python/pyarrow/_compute.pyx | 2 ++ python/pyarrow/tests/test_compute.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/python/pyarrow/_compute.pyx b/python/pyarrow/_compute.pyx index 1c98bbfdea2c..323b2aa7da52 100644 --- a/python/pyarrow/_compute.pyx +++ b/python/pyarrow/_compute.pyx @@ -3516,6 +3516,8 @@ def call_tabular_function(function_name, args=None, func_registry=None): c_func_name = tobytes(function_name) if func_registry is None: c_func_registry = NULL + elif not isinstance(func_registry, FunctionRegistry): + raise TypeError("func_registry must be a FunctionRegistry") else: c_func_registry = (func_registry).registry if args is None: diff --git a/python/pyarrow/tests/test_compute.py b/python/pyarrow/tests/test_compute.py index 8b2ad2b333fc..100adad23cb0 100644 --- a/python/pyarrow/tests/test_compute.py +++ b/python/pyarrow/tests/test_compute.py @@ -25,6 +25,7 @@ import os import pytest import random +import subprocess import sys import textwrap @@ -246,6 +247,24 @@ def test_list_functions(): assert "add" in pc.list_functions() +def test_call_tabular_function_rejects_invalid_registry(): + code = """if 1: + import pyarrow.compute as pc + + try: + pc.call_tabular_function("", None, 1) + except TypeError as exc: + assert str(exc) == "func_registry must be a FunctionRegistry" + else: + raise AssertionError("expected TypeError") + """ + res = subprocess.run([sys.executable, "-c", code], + universal_newlines=True, stderr=subprocess.PIPE) + if res.returncode != 0: + print(res.stderr, file=sys.stderr) + res.check_returncode() + + def _check_get_function(name, expected_func_cls, expected_ker_cls, min_num_kernels=1): func = pc.get_function(name) From 2824914491bc939cacd99c09cfd59a6ae50078b5 Mon Sep 17 00:00:00 2001 From: 1fanwang <1fannnw@gmail.com> Date: Tue, 8 Sep 2026 15:37:10 -0700 Subject: [PATCH 2/4] GH-51228: Mark the subprocess test with pytest.mark.processes The test spawns a subprocess, which Emscripten does not support. Without the marker the test runs there anyway and fails. Generated-by: GitHub Copilot CLI (Claude Opus 5) Signed-off-by: 1fanwang <1fannnw@gmail.com> --- python/pyarrow/tests/test_compute.py | 1 + 1 file changed, 1 insertion(+) diff --git a/python/pyarrow/tests/test_compute.py b/python/pyarrow/tests/test_compute.py index 100adad23cb0..9d71e1243486 100644 --- a/python/pyarrow/tests/test_compute.py +++ b/python/pyarrow/tests/test_compute.py @@ -247,6 +247,7 @@ def test_list_functions(): assert "add" in pc.list_functions() +@pytest.mark.processes def test_call_tabular_function_rejects_invalid_registry(): code = """if 1: import pyarrow.compute as pc From ff7a7b22ec7a174bdc60dbb4be1bcd87848b795d Mon Sep 17 00:00:00 2001 From: 1fanwang <1fannnw@gmail.com> Date: Thu, 10 Sep 2026 04:53:56 -0700 Subject: [PATCH 3/4] GH-51228: Assert the type error with pytest.raises The subprocess wrapper guarded against the crash this change removes, so the in-process form reads better now. Signed-off-by: 1fanwang <1fannnw@gmail.com> --- python/pyarrow/tests/test_compute.py | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/python/pyarrow/tests/test_compute.py b/python/pyarrow/tests/test_compute.py index 9d71e1243486..1a4bf5c3a905 100644 --- a/python/pyarrow/tests/test_compute.py +++ b/python/pyarrow/tests/test_compute.py @@ -25,7 +25,6 @@ import os import pytest import random -import subprocess import sys import textwrap @@ -247,23 +246,10 @@ def test_list_functions(): assert "add" in pc.list_functions() -@pytest.mark.processes def test_call_tabular_function_rejects_invalid_registry(): - code = """if 1: - import pyarrow.compute as pc - - try: + with pytest.raises(TypeError, + match="func_registry must be a FunctionRegistry"): pc.call_tabular_function("", None, 1) - except TypeError as exc: - assert str(exc) == "func_registry must be a FunctionRegistry" - else: - raise AssertionError("expected TypeError") - """ - res = subprocess.run([sys.executable, "-c", code], - universal_newlines=True, stderr=subprocess.PIPE) - if res.returncode != 0: - print(res.stderr, file=sys.stderr) - res.check_returncode() def _check_get_function(name, expected_func_cls, expected_ker_cls, From 388ac7b61d2aa6c1dea9504975b858886a4f1657 Mon Sep 17 00:00:00 2001 From: 1fanwang <1fannnw@gmail.com> Date: Fri, 11 Sep 2026 21:47:33 -0700 Subject: [PATCH 4/4] GH-51228: [Python] Reject invalid UDF registration registries Signed-off-by: 1fanwang <1fannnw@gmail.com> --- python/pyarrow/_compute.pyx | 2 ++ python/pyarrow/tests/test_compute.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/python/pyarrow/_compute.pyx b/python/pyarrow/_compute.pyx index 323b2aa7da52..c17870f3225a 100644 --- a/python/pyarrow/_compute.pyx +++ b/python/pyarrow/_compute.pyx @@ -3482,6 +3482,8 @@ def _register_user_defined_function(register_func, func, function_name, function if func_registry is None: c_func_registry = NULL + elif not isinstance(func_registry, FunctionRegistry): + raise TypeError("func_registry must be a FunctionRegistry") else: c_func_registry = (func_registry).registry diff --git a/python/pyarrow/tests/test_compute.py b/python/pyarrow/tests/test_compute.py index 1a4bf5c3a905..00e73cee9ddb 100644 --- a/python/pyarrow/tests/test_compute.py +++ b/python/pyarrow/tests/test_compute.py @@ -252,6 +252,25 @@ def test_call_tabular_function_rejects_invalid_registry(): pc.call_tabular_function("", None, 1) +@pytest.mark.parametrize("register_function", [ + pc.register_scalar_function, + pc.register_vector_function, + pc.register_aggregate_function, + pc.register_tabular_function, +]) +def test_register_function_rejects_invalid_registry(register_function): + with pytest.raises(TypeError, + match="func_registry must be a FunctionRegistry"): + register_function( + func=lambda context: None, + function_name="invalid_registry", + function_doc={"summary": "", "description": ""}, + in_types={}, + out_type=pa.struct([]), + func_registry=1, + ) + + def _check_get_function(name, expected_func_cls, expected_ker_cls, min_num_kernels=1): func = pc.get_function(name)