Skip to content

Commit

Permalink
dont inherit from GraphQLObject
Browse files Browse the repository at this point in the history
  • Loading branch information
Karol Gruszczyk authored and Karol Gruszczyk committed Feb 11, 2018
1 parent 484f542 commit 2ce4b92
Show file tree
Hide file tree
Showing 11 changed files with 85 additions and 57 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -3,3 +3,4 @@ venv
*.pyc
.cache
.idea
.pytest_cache/
2 changes: 1 addition & 1 deletion requirements.txt
Expand Up @@ -4,6 +4,6 @@ graphql-core==2.0
django==2.0

# development
pytest==3.3.2
pytest==3.4
pytest-cov==2.5.1
pytest-django==3.1.2
20 changes: 20 additions & 0 deletions slothql/base.py
@@ -0,0 +1,20 @@
import inspect
from typing import Union, Type, Callable

from graphql.type.definition import GraphQLType


class BaseType:
def __init__(self, type_: GraphQLType):
self._type = type_


LazyType = Union[Type[BaseType], BaseType, Callable]


def resolve_lazy_type(lazy_type: LazyType) -> BaseType:
assert inspect.isclass(lazy_type) and issubclass(lazy_type, BaseType) or isinstance(lazy_type, BaseType) \
or inspect.isfunction(lazy_type), f'"lazy_type" needs to inherit from BaseType or be a lazy reference'
of_type = lazy_type() if inspect.isfunction(lazy_type) else lazy_type
of_type = of_type() if inspect.isclass(of_type) else of_type
return of_type
9 changes: 5 additions & 4 deletions slothql/conftest.py
Expand Up @@ -8,6 +8,7 @@
from graphql.type.definition import GraphQLType

import slothql
from slothql.base import BaseType


def pytest_configure():
Expand All @@ -32,12 +33,12 @@ def pytest_runtest_setup(item):


@pytest.fixture()
def type():
return lambda: mock.Mock(spec=GraphQLType)
def type_mock():
return lambda: BaseType(mock.Mock(spec=GraphQLType))


@pytest.fixture()
def info():
def info_mock():
return lambda **kwargs: mock.Mock(spec=graphql.ResolveInfo, **kwargs)


Expand All @@ -47,7 +48,7 @@ def resolver_mock():


@pytest.fixture()
def field_mock(type):
def field_mock():
return mock.Mock(spec=slothql.Field)


Expand Down
8 changes: 4 additions & 4 deletions slothql/django/types/tests/resolution.py
Expand Up @@ -36,14 +36,14 @@ class Meta:
fields = '__all__'


def test_resolve__relation(info):
def test_resolve__relation(info_mock):
manager = mock.Mock(models.Manager)
manager.get_queryset.return_value = [1, 2, 3]
parent = mock.Mock(spec=Parent, children=manager)
assert [1, 2, 3] == Parent.children.resolver(parent, info(field_name='children'))
assert [1, 2, 3] == Parent.children.resolver(parent, info_mock(field_name='children'))


def test_resolve__default(info):
def test_resolve__default(info_mock):
with mock.patch.object(Child._meta.model._default_manager, 'get_queryset', return_value=[1, 2, 3]) as get_queryset:
assert [1, 2, 3] == Parent.children.resolver(None, info(field_name='children'))
assert [1, 2, 3] == Parent.children.resolver(None, info_mock(field_name='children'))
get_queryset.assert_called_with()
21 changes: 5 additions & 16 deletions slothql/fields/field.py
@@ -1,12 +1,9 @@
import inspect
import functools
from typing import Union, Type, Callable

import graphql
from graphql.type.definition import GraphQLType

from slothql.utils import LazyInitMixin

from slothql.base import LazyType, resolve_lazy_type, BaseType
from .list import ListMixin
from .resolver import Resolver

Expand All @@ -21,23 +18,15 @@ def get_default_resolver(cls, of_type):
return lambda obj, info: of_type.resolve(cls.resolve_field(obj, info), info)
return cls.resolve_field

def get_resolver(self, resolver, of_type):
def get_resolver(self, resolver, of_type: BaseType):
return Resolver(self, resolver).func or self.get_default_resolver(of_type)

@staticmethod
def resolve_lazy_type(of_type):
assert inspect.isclass(of_type) and issubclass(of_type, GraphQLType) or isinstance(of_type, GraphQLType) or inspect.isfunction(of_type), \
f'"of_type" needs to be a valid GraphQlType or a lazy reference'
of_type = of_type() if inspect.isfunction(of_type) else of_type
of_type = of_type() if inspect.isclass(of_type) else of_type
return of_type

def __init__(self, of_type: Union[Type[GraphQLType], Callable], resolver=None, **kwargs):
of_type = self.resolve_lazy_type(of_type)
def __init__(self, of_type: LazyType, resolver=None, **kwargs):
of_type = resolve_lazy_type(of_type)
resolver = self.get_resolver(resolver, of_type)
assert callable(resolver), f'resolver needs to be callable, not {resolver}'

super().__init__(type=of_type, resolver=functools.partial(self.resolve, resolver), **kwargs)
super().__init__(type=of_type._type, resolver=functools.partial(self.resolve, resolver), **kwargs)

@classmethod
def resolve(cls, resolver, obj, info: graphql.ResolveInfo):
Expand Down
13 changes: 7 additions & 6 deletions slothql/fields/scalars.py
@@ -1,5 +1,6 @@
import graphql

from slothql.base import BaseType
from .field import Field


Expand All @@ -15,29 +16,29 @@ def serialize(cls, value):

class Boolean(Scalar):
def __init__(self, **kwargs):
super().__init__(of_type=graphql.GraphQLBoolean, **kwargs)
super().__init__(of_type=BaseType(graphql.GraphQLBoolean), **kwargs)


class Integer(Scalar):
def __init__(self, **kwargs):
super().__init__(of_type=graphql.GraphQLInt, **kwargs)
super().__init__(of_type=BaseType(graphql.GraphQLInt), **kwargs)


class Float(Scalar):
def __init__(self, **kwargs):
super().__init__(of_type=graphql.GraphQLFloat, **kwargs)
super().__init__(of_type=BaseType(graphql.GraphQLFloat), **kwargs)


class String(Scalar):
def __init__(self, **kwargs):
super().__init__(of_type=graphql.GraphQLString, **kwargs)
super().__init__(of_type=BaseType(graphql.GraphQLString), **kwargs)


class JSONString(Scalar):
def __init__(self, **kwargs):
super().__init__(of_type=graphql.GraphQLString, **kwargs)
super().__init__(of_type=BaseType(graphql.GraphQLString), **kwargs)


class ID(Scalar):
def __init__(self, **kwargs):
super().__init__(of_type=graphql.GraphQLID, **kwargs)
super().__init__(of_type=BaseType(graphql.GraphQLID), **kwargs)
22 changes: 11 additions & 11 deletions slothql/fields/tests/field.py
Expand Up @@ -11,18 +11,18 @@

@pytest.mark.incremental
class TestField:
def test_can_init(self, type):
Field(of_type=type())
def test_can_init(self, type_mock):
Field(of_type=type_mock())

def test_has_resolver_callable(self, type):
assert callable(Field(of_type=type()).resolver)
def test_has_resolver_callable(self, type_mock):
assert callable(Field(of_type=type_mock()).resolver)

def test_no_resolver(self, type, info):
field = Field(of_type=type())
assert field.resolver(None, info()) is None
def test_no_resolver(self, type_mock, info_mock):
field = Field(of_type=type_mock())
assert field.resolver(None, info_mock()) is None

def test_default_resolver(self, partials_equal):
of_type= mock.Mock(spec=GraphQLType)
def test_default_resolver(self, type_mock, partials_equal):
of_type = type_mock()
field = Field(of_type=of_type)
assert partials_equal(partial(field.resolve, field.get_default_resolver(of_type)), field.resolver)

Expand All @@ -33,6 +33,6 @@ def test_default_resolver(self, partials_equal):
(None, {'a': None}),
({'a': {'nested': 'hell yeah'}}, {'a': {'nested': 'hell yeah'}}),
))
def test_resolve_field(obj, expected, info):
def test_resolve_field(obj, expected, info_mock):
for field_name, expected_value in expected.items():
assert expected_value == Field.resolve_field(obj, info(field_name=field_name))
assert expected_value == Field.resolve_field(obj, info_mock(field_name=field_name))
7 changes: 4 additions & 3 deletions slothql/schema.py
@@ -1,11 +1,12 @@
import graphql

from .base import LazyType, resolve_lazy_type


class Schema(graphql.GraphQLSchema):
def __init__(self, query, mutation=None, subscription=None, directives=None, types=None):
query = query() if callable(query) else query
def __init__(self, query: LazyType, mutation=None, subscription=None, directives=None, types=None):
super().__init__(
query=query,
query=resolve_lazy_type(query)._type,
mutation=mutation,
subscription=subscription,
directives=directives,
Expand Down
5 changes: 3 additions & 2 deletions slothql/types/object.py
Expand Up @@ -3,6 +3,7 @@

import graphql

from slothql.base import BaseType
from slothql.fields import Field
from slothql.utils import is_magic_name, get_attr_fields
from slothql.utils.singleton import Singleton
Expand Down Expand Up @@ -71,14 +72,14 @@ def merge_field(mcs, old, new):
return new


class Object(graphql.GraphQLObjectType, metaclass=ObjectMeta):
class Object(BaseType, metaclass=ObjectMeta):
@classmethod
def __new__(cls, *more):
assert not cls._meta.abstract, f'Abstract type {cls.__name__} can not be instantiated'
return super().__new__(*more)

def __init__(self, **kwargs):
super().__init__(name=self.__class__.__name__, fields=self._meta.fields, **kwargs)
super().__init__(graphql.GraphQLObjectType(name=self.__class__.__name__, fields=self._meta.fields, **kwargs))

class Meta:
abstract = True
Expand Down
34 changes: 24 additions & 10 deletions slothql/types/tests/object.py
Expand Up @@ -50,7 +50,7 @@ def test_can_create_not_abstract(self, field_mock):
class Inherit(Object):
field = field_mock

assert Inherit().name == 'Inherit'
assert Inherit()._type.name == 'Inherit'

def test_abstract_inherit(self):
class Inherit(Object):
Expand Down Expand Up @@ -93,7 +93,7 @@ def test_fields(self):
class Inherit(Object):
field = fields.Field(mock.Mock(spec=Object))

assert Inherit._meta.fields == Inherit().fields
assert Inherit._meta.fields == Inherit()._type.fields


def test_merge_object_options_dicts():
Expand Down Expand Up @@ -141,15 +141,15 @@ class Meta:
abstract = True


def test_resolver_lambda(info):
def test_resolver_lambda(info_mock):
class Test(Object):
field = slothql.String(resolver=lambda obj: obj)

expected = object()
assert expected == Test.field.resolver(expected, info(field_name='field'))
assert expected == Test.field.resolver(expected, info_mock(field_name='field'))


def test_resolver_staticmethod(info):
def test_resolver_staticmethod(info_mock):
class Test(Object):
@staticmethod
def get_field(obj):
Expand All @@ -158,10 +158,10 @@ def get_field(obj):
field = slothql.String(resolver=get_field)

expected = object()
assert expected == Test.field.resolver(expected, info(field_name='field'))
assert expected == Test.field.resolver(expected, info_mock(field_name='field'))


def test_resolver_classmethod(info):
def test_resolver_classmethod(info_mock):
class Test(Object):
@classmethod
def get_field(cls, obj):
Expand All @@ -170,15 +170,29 @@ def get_field(cls, obj):
field = slothql.String(resolver=get_field)

expected = object()
assert expected == Test.field.resolver(expected, info(field_name='field'))
assert expected == Test.field.resolver(expected, info_mock(field_name='field'))


def test_resolver_method(info):
def test_resolver_method(info_mock):
class Test(Object):
def get_field(self, obj):
return obj

field = slothql.String(resolver=get_field)

expected = object()
assert expected == Test.field.resolver(expected, info(field_name='field'))
assert expected == Test.field.resolver(expected, info_mock(field_name='field'))


def test_name_collision__fields():
class Query(slothql.Object):
fields = slothql.String(resolver=lambda: 'foo')

slothql.Schema(query=Query)


def test_name_collision__interfaces():
class Query(slothql.Object):
interfaces = slothql.String(resolver=lambda: 'foo')

slothql.Schema(query=Query)

0 comments on commit 2ce4b92

Please sign in to comment.