diff --git a/bigframes/functions/function_typing.py b/bigframes/functions/function_typing.py index 44ee071001..ecbc901317 100644 --- a/bigframes/functions/function_typing.py +++ b/bigframes/functions/function_typing.py @@ -60,8 +60,21 @@ class UnsupportedTypeError(ValueError): def __init__(self, type_, supported_types): self.type = type_ self.supported_types = supported_types + + types_to_format = supported_types + if isinstance(supported_types, dict): + types_to_format = supported_types.keys() + + supported_types_str = ", ".join( + sorted([getattr(t, "__name__", str(t)) for t in types_to_format]) + ) + if get_origin(type_) is not None: + type_str = str(type_) + else: + type_str = getattr(type_, "__name__", str(type_)) + super().__init__( - f"'{type_}' must be one of the supported types ({supported_types}) " + f"'{type_str}' must be one of the supported types ({supported_types_str}) " "or a list of one of those types." ) diff --git a/tests/unit/functions/test_function_typing.py b/tests/unit/functions/test_function_typing.py new file mode 100644 index 0000000000..53e4ca7d4d --- /dev/null +++ b/tests/unit/functions/test_function_typing.py @@ -0,0 +1,68 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import datetime +import decimal + +import pytest + +from bigframes.functions import function_typing + + +def test_unsupported_type_error_init_with_dict(): + # Arrange + unsupported_type = decimal.Decimal + supported_types = {int: "INT64", float: "FLOAT64"} + + # Act + err = function_typing.UnsupportedTypeError(unsupported_type, supported_types) + + # Assert + assert "Decimal" in str(err) + assert "float, int" in str(err) + + +def test_unsupported_type_error_init_with_set(): + # Arrange + unsupported_type = decimal.Decimal + supported_types = {int, float} + + # Act + err = function_typing.UnsupportedTypeError(unsupported_type, supported_types) + + # Assert + assert "Decimal" in str(err) + assert "float, int" in str(err) + + +def test_sdk_type_from_python_type_raises_unsupported_type_error(): + # Arrange + unsupported_type = datetime.datetime + + # Act & Assert + with pytest.raises(function_typing.UnsupportedTypeError) as excinfo: + function_typing.sdk_type_from_python_type(unsupported_type) + assert "datetime" in str(excinfo.value) + assert "bool, bytes, float, int, str" in str(excinfo.value) + + +def test_sdk_type_from_python_type_with_generic_type_raises_unsupported_type_error(): + # Arrange + unsupported_type = list[str] + + # Act & Assert + with pytest.raises(function_typing.UnsupportedTypeError) as excinfo: + function_typing.sdk_type_from_python_type(unsupported_type) + assert "list[str]" in str(excinfo.value) + assert "bool, bytes, float, int, str" in str(excinfo.value)