diff --git a/graphene_sqlalchemy/converter.py b/graphene_sqlalchemy/converter.py index 4822f4d..de1bfde 100644 --- a/graphene_sqlalchemy/converter.py +++ b/graphene_sqlalchemy/converter.py @@ -247,8 +247,9 @@ def convert_sqlalchemy_type( return type_arg # No valid type found, warn and fall back to graphene.String - raise Exception( - "Don't know how to convert the SQLAlchemy field %s (%s, %s)" + raise TypeError( + "Don't know how to convert the SQLAlchemy field %s (%s, %s). " + "Please add a type converter or set the type manually using ORMField(type_=your_type)" % (column, column.__class__ or "no column provided", type_arg) ) @@ -562,6 +563,12 @@ def convert_sqlalchemy_hybrid_property_bare_str(type_arg: str, **kwargs): def convert_hybrid_property_return_type(hybrid_prop): # Grab the original method's return type annotations from inside the hybrid property - return_type_annotation = hybrid_prop.fget.__annotations__.get("return", str) + return_type_annotation = hybrid_prop.fget.__annotations__.get("return", None) + if not return_type_annotation: + raise TypeError( + "Cannot convert hybrid property type {} to a valid graphene type. " + "Please make sure to annotate the return type of the hybrid property or use the " + "type_ attribute of ORMField to set the type.".format(hybrid_prop) + ) return convert_sqlalchemy_type(return_type_annotation) diff --git a/graphene_sqlalchemy/tests/models.py b/graphene_sqlalchemy/tests/models.py index d02ee27..9531aaa 100644 --- a/graphene_sqlalchemy/tests/models.py +++ b/graphene_sqlalchemy/tests/models.py @@ -88,12 +88,12 @@ class Reporter(Base): favorite_article = relationship("Article", uselist=False, lazy="selectin") @hybrid_property - def hybrid_prop_with_doc(self): + def hybrid_prop_with_doc(self) -> str: """Docstring test""" return self.first_name @hybrid_property - def hybrid_prop(self): + def hybrid_prop(self) -> str: return self.first_name @hybrid_property @@ -253,13 +253,6 @@ def hybrid_prop_first_shopping_cart_item(self) -> ShoppingCartItem: def hybrid_prop_shopping_cart_item_list(self) -> List[ShoppingCartItem]: return [ShoppingCartItem(id=1), ShoppingCartItem(id=2)] - # Unsupported Type - # fixme move this somewhere else - # - # @hybrid_property - # def hybrid_prop_unsupported_type_tuple(self) -> Tuple[str, str]: - # return "this will actually", "be a string" - # Self-references @hybrid_property diff --git a/graphene_sqlalchemy/tests/test_converter.py b/graphene_sqlalchemy/tests/test_converter.py index 49a4d93..671c4f9 100644 --- a/graphene_sqlalchemy/tests/test_converter.py +++ b/graphene_sqlalchemy/tests/test_converter.py @@ -1,6 +1,6 @@ import enum import sys -from typing import Dict, Union +from typing import Dict, Tuple, Union import pytest import sqlalchemy_utils as sqa_utils @@ -79,6 +79,30 @@ def prop_method() -> int: assert get_hybrid_property_type(prop_method).type == graphene.Int +def test_hybrid_unknown_annotation(): + @hybrid_property + def hybrid_prop(self): + return "This should fail" + + with pytest.raises( + TypeError, + match=r"(.*)Please make sure to annotate the return type of the hybrid property or use the " + "type_ attribute of ORMField to set the type.(.*)", + ): + get_hybrid_property_type(hybrid_prop) + + +def test_hybrid_prop_no_type_annotation(): + @hybrid_property + def hybrid_prop(self) -> Tuple[str, str]: + return "This should Fail because", "we don't support Tuples in GQL" + + with pytest.raises( + TypeError, match=r"(.*)Don't know how to convert the SQLAlchemy field(.*)" + ): + get_hybrid_property_type(hybrid_prop) + + @pytest.mark.skipif( sys.version_info < (3, 10), reason="|-Style Unions are unsupported in python < 3.10" )