diff --git a/tools/pythonpkg/src/common/exceptions.cpp b/tools/pythonpkg/src/common/exceptions.cpp index db62eee8236d..338a8cd6797f 100644 --- a/tools/pythonpkg/src/common/exceptions.cpp +++ b/tools/pythonpkg/src/common/exceptions.cpp @@ -373,6 +373,9 @@ void RegisterExceptions(const py::module &m) { } catch (const duckdb::Exception &ex) { duckdb::ErrorData error(ex); PyThrowException(error, HTTP_EXCEPTION.ptr()); + } catch (const py::builtin_exception &ex) { + // These represent Python exceptions, we don't want to catch these + throw; } catch (const std::exception &ex) { duckdb::ErrorData error(ex); if (error.Type() == ExceptionType::INVALID) { diff --git a/tools/pythonpkg/src/python_udf.cpp b/tools/pythonpkg/src/python_udf.cpp index fbd781604f22..26a2d5b90fc5 100644 --- a/tools/pythonpkg/src/python_udf.cpp +++ b/tools/pythonpkg/src/python_udf.cpp @@ -141,8 +141,12 @@ static scalar_function_t CreateVectorizedFunction(PyObject *function, PythonExce single_array[0] = python_object; single_name[0] = "c0"; - python_object = py::module_::import("pyarrow").attr("lib").attr("Table").attr("from_arrays")( - single_array, py::arg("names") = single_name); + try { + python_object = py::module_::import("pyarrow").attr("lib").attr("Table").attr("from_arrays")( + single_array, py::arg("names") = single_name); + } catch (py::error_already_set &ex) { + throw InvalidInputException("Could not convert the result into an Arrow Table"); + } } // Convert the pyarrow result back to a DuckDB datachunk ConvertPyArrowToDataChunk(python_object, result, state.GetContext(), count); diff --git a/tools/pythonpkg/tests/conftest.py b/tools/pythonpkg/tests/conftest.py index 998e1c561635..fb73295ca250 100644 --- a/tools/pythonpkg/tests/conftest.py +++ b/tools/pythonpkg/tests/conftest.py @@ -102,9 +102,9 @@ def pandas_2_or_higher(): def pandas_supports_arrow_backend(): try: - from pandas.compat import pa_version_under7p0 + from pandas.compat import pa_version_under11p0 - if pa_version_under7p0 == True: + if pa_version_under11p0 == True: return False except ImportError: return False diff --git a/tools/pythonpkg/tests/fast/arrow/test_filter_pushdown.py b/tools/pythonpkg/tests/fast/arrow/test_filter_pushdown.py index aa1732c712d1..cb61d4a4af16 100644 --- a/tools/pythonpkg/tests/fast/arrow/test_filter_pushdown.py +++ b/tools/pythonpkg/tests/fast/arrow/test_filter_pushdown.py @@ -745,7 +745,10 @@ def test_struct_filter_pushdown(self, duckdb_cursor, create_table): """ ).fetchall() - match = re.search(".*ARROW_SCAN.*Filters: s\\.a<2 AND s\\.a IS.*NOT NULL.*", query_res[0][1], flags=re.DOTALL) + input = query_res[0][1] + if 'PANDAS_SCAN' in input: + pytest.skip(reason="This version of pandas does not produce an Arrow object") + match = re.search(".*ARROW_SCAN.*Filters: s\\.a<2 AND s\\.a IS.*NOT NULL.*", input, flags=re.DOTALL) assert match # Check that the filter is applied correctly @@ -812,9 +815,10 @@ def test_nested_struct_filter_pushdown(self, duckdb_cursor, create_table): """ ).fetchall() - match = re.search( - ".*ARROW_SCAN.*Filters: s\\.a\\.b<2 AND s\\.a\\.b.*IS NOT NULL.*", query_res[0][1], flags=re.DOTALL - ) + input = query_res[0][1] + if 'PANDAS_SCAN' in input: + pytest.skip(reason="This version of pandas does not produce an Arrow object") + match = re.search(".*ARROW_SCAN.*Filters: s\\.a\\.b<2 AND s\\.a\\.b.*IS NOT NULL.*", input, flags=re.DOTALL) assert match # Check that the filter is applied correctly diff --git a/tools/pythonpkg/tests/fast/udf/test_scalar_arrow.py b/tools/pythonpkg/tests/fast/udf/test_scalar_arrow.py index e1be89515fd6..6eac008ab75c 100644 --- a/tools/pythonpkg/tests/fast/udf/test_scalar_arrow.py +++ b/tools/pythonpkg/tests/fast/udf/test_scalar_arrow.py @@ -101,7 +101,7 @@ def returns_none(col): con = duckdb.connect() con.create_function('will_crash', returns_none, [BIGINT], BIGINT, type='arrow') - with pytest.raises(duckdb.Error, match="""TypeError: 'NoneType' object is not iterable"""): + with pytest.raises(duckdb.Error, match="""Could not convert the result into an Arrow Table"""): res = con.sql("""select will_crash(5)""").fetchall() def test_empty_result(self):