Skip to content

Commit cad1e49

Browse files
author
Connor Brinton
committed
Don't suppress SQLAlchemy errors when mapping classes
1 parent 6a96d37 commit cad1e49

File tree

3 files changed

+82
-5
lines changed

3 files changed

+82
-5
lines changed

Diff for: graphene_sqlalchemy/tests/test_types.py

+66
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
from collections import OrderedDict
22
from graphene import Field, Int, Interface, ObjectType
33
from graphene.relay import Node, is_node, Connection
4+
import mock
45
import six
6+
import sqlalchemy.exc
7+
import sqlalchemy.orm.exc
58
from promise import Promise
69

10+
from .. import utils
711
from ..registry import Registry
812
from ..types import SQLAlchemyObjectType, SQLAlchemyObjectTypeOptions
913
from .models import Article, Reporter
@@ -181,3 +185,65 @@ class Meta:
181185
resolver, TestConnection, ReporterWithCustomOptions, None, None
182186
)
183187
assert result is not None
188+
189+
190+
@mock.patch(utils.__name__ + '.class_mapper')
191+
def test_unique_errors_propagate(class_mapper_mock):
192+
# Define unique error to detect
193+
class UniqueError(Exception):
194+
pass
195+
196+
# Mock class_mapper effect
197+
class_mapper_mock.side_effect = UniqueError
198+
199+
# Make sure that errors are propagated from class_mapper when instantiating new classes
200+
error = None
201+
try:
202+
class ArticleOne(SQLAlchemyObjectType):
203+
class Meta(object):
204+
model = Article
205+
except UniqueError as e:
206+
error = e
207+
208+
# Check that an error occured, and that it was the unique error we gave
209+
assert error is not None
210+
assert isinstance(error, UniqueError)
211+
212+
213+
@mock.patch(utils.__name__ + '.class_mapper')
214+
def test_argument_errors_propagate(class_mapper_mock):
215+
# Mock class_mapper effect
216+
class_mapper_mock.side_effect = sqlalchemy.exc.ArgumentError
217+
218+
# Make sure that errors are propagated from class_mapper when instantiating new classes
219+
error = None
220+
try:
221+
class ArticleTwo(SQLAlchemyObjectType):
222+
class Meta(object):
223+
model = Article
224+
except sqlalchemy.exc.ArgumentError as e:
225+
error = e
226+
227+
# Check that an error occured, and that it was the unique error we gave
228+
assert error is not None
229+
assert isinstance(error, sqlalchemy.exc.ArgumentError)
230+
231+
232+
@mock.patch(utils.__name__ + '.class_mapper')
233+
def test_unmapped_errors_reformat(class_mapper_mock):
234+
# Mock class_mapper effect
235+
class_mapper_mock.side_effect = sqlalchemy.orm.exc.UnmappedClassError(object)
236+
237+
# Make sure that errors are propagated from class_mapper when instantiating new classes
238+
error = None
239+
try:
240+
class ArticleThree(SQLAlchemyObjectType):
241+
class Meta(object):
242+
model = Article
243+
except ValueError as e:
244+
error = e
245+
246+
# Check that an error occured, and that it was the unique error we gave
247+
assert error is not None
248+
assert isinstance(error, ValueError)
249+
assert "You need to pass a valid SQLAlchemy Model" in str(error)

Diff for: graphene_sqlalchemy/types.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,11 @@ def __init_subclass_with_meta__(
103103
_meta=None,
104104
**options
105105
):
106-
assert is_mapped_class(model), (
107-
"You need to pass a valid SQLAlchemy Model in " '{}.Meta, received "{}".'
108-
).format(cls.__name__, model)
106+
# Make sure model is a valid SQLAlchemy model
107+
if not is_mapped_class(model):
108+
raise ValueError(
109+
"You need to pass a valid SQLAlchemy Model in " '{}.Meta, received "{}".'.format(cls.__name__, model)
110+
)
109111

110112
if not registry:
111113
registry = get_global_registry()

Diff for: graphene_sqlalchemy/utils.py

+11-2
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,17 @@ def get_query(model, context):
2525
def is_mapped_class(cls):
2626
try:
2727
class_mapper(cls)
28-
except (ArgumentError, UnmappedClassError):
29-
return False
28+
except (ArgumentError, UnmappedClassError) as error:
29+
# Only handle ArgumentErrors for non-class objects
30+
if isinstance(error, ArgumentError) and "Class object expected" in str(error):
31+
return False
32+
33+
# Unmapped classes return false
34+
if isinstance(error, UnmappedClassError):
35+
return False
36+
37+
# We don't know how to handle this type of error, reraise
38+
raise error
3039
else:
3140
return True
3241

0 commit comments

Comments
 (0)