diff --git a/UPGRADE-v1.0.md b/UPGRADE-v1.0.md index 8ace8756..ecfa9da7 100644 --- a/UPGRADE-v1.0.md +++ b/UPGRADE-v1.0.md @@ -153,7 +153,7 @@ class Query(ObjectType): ``` Also, if you wanted to create an `ObjectType` that implements `Node`, you have to do it -explicity. +explicitly. ## Django diff --git a/UPGRADE-v2.0.md b/UPGRADE-v2.0.md index 444bc12a..04926e7a 100644 --- a/UPGRADE-v2.0.md +++ b/UPGRADE-v2.0.md @@ -123,7 +123,7 @@ def resolve_my_field(root, info, my_arg): return ... ``` -**PS.: Take care with receiving args like `my_arg` as above. This doesn't work for optional (non-required) arguments as stantard `Connection`'s arguments (first, last, after, before).** +**PS.: Take care with receiving args like `my_arg` as above. This doesn't work for optional (non-required) arguments as standard `Connection`'s arguments (first, last, after, before).** You may need something like this: ```python diff --git a/docs/execution/fileuploading.rst b/docs/execution/fileuploading.rst index d92174c0..66ce9bd3 100644 --- a/docs/execution/fileuploading.rst +++ b/docs/execution/fileuploading.rst @@ -4,5 +4,5 @@ File uploading File uploading is not part of the official GraphQL spec yet and is not natively implemented in Graphene. -If your server needs to support file uploading then you can use the libary: `graphene-file-upload `_ which enhances Graphene to add file +If your server needs to support file uploading then you can use the library: `graphene-file-upload `_ which enhances Graphene to add file uploads and conforms to the unoffical GraphQL `multipart request spec `_. diff --git a/docs/types/enums.rst b/docs/types/enums.rst index a3215cad..b9ac5333 100644 --- a/docs/types/enums.rst +++ b/docs/types/enums.rst @@ -86,7 +86,7 @@ In the Python ``Enum`` implementation you can access a member by initing the Enu assert Color(1) == Color.RED -However, in Graphene ``Enum`` you need to call get to have the same effect: +However, in Graphene ``Enum`` you need to call `.get` to have the same effect: .. code:: python diff --git a/graphene/types/enum.py b/graphene/types/enum.py index 70e8ee8e..e5cc50ed 100644 --- a/graphene/types/enum.py +++ b/graphene/types/enum.py @@ -52,7 +52,10 @@ class EnumMeta(SubclassWithMeta_Meta): return super(EnumMeta, cls).__call__(*args, **kwargs) # return cls._meta.enum(*args, **kwargs) - def from_enum(cls, enum, description=None, deprecation_reason=None): # noqa: N805 + def from_enum( + cls, enum, name=None, description=None, deprecation_reason=None + ): # noqa: N805 + name = name or enum.__name__ description = description or enum.__doc__ meta_dict = { "enum": enum, @@ -60,7 +63,7 @@ class EnumMeta(SubclassWithMeta_Meta): "deprecation_reason": deprecation_reason, } meta_class = type("Meta", (object,), meta_dict) - return type(meta_class.enum.__name__, (Enum,), {"Meta": meta_class}) + return type(name, (Enum,), {"Meta": meta_class}) class Enum(UnmountedType, BaseType, metaclass=EnumMeta): diff --git a/graphene/types/interface.py b/graphene/types/interface.py index 77086dab..6503b78b 100644 --- a/graphene/types/interface.py +++ b/graphene/types/interface.py @@ -5,11 +5,12 @@ from .utils import yank_fields_from_attrs # For static type checking with Mypy MYPY = False if MYPY: - from typing import Dict # NOQA + from typing import Dict, Iterable, Type # NOQA class InterfaceOptions(BaseOptions): fields = None # type: Dict[str, Field] + interfaces = () # type: Iterable[Type[Interface]] class Interface(BaseType): @@ -45,7 +46,7 @@ class Interface(BaseType): """ @classmethod - def __init_subclass_with_meta__(cls, _meta=None, **options): + def __init_subclass_with_meta__(cls, _meta=None, interfaces=(), **options): if not _meta: _meta = InterfaceOptions(cls) @@ -58,6 +59,9 @@ class Interface(BaseType): else: _meta.fields = fields + if not _meta.interfaces: + _meta.interfaces = interfaces + super(Interface, cls).__init_subclass_with_meta__(_meta=_meta, **options) @classmethod diff --git a/graphene/types/schema.py b/graphene/types/schema.py index bf76b36d..1a33a93d 100644 --- a/graphene/types/schema.py +++ b/graphene/types/schema.py @@ -233,11 +233,20 @@ class TypeMap(dict): else None ) + def interfaces(): + interfaces = [] + for graphene_interface in graphene_type._meta.interfaces: + interface = self.add_type(graphene_interface) + assert interface.graphene_type == graphene_interface + interfaces.append(interface) + return interfaces + return GrapheneInterfaceType( graphene_type=graphene_type, name=graphene_type._meta.name, description=graphene_type._meta.description, fields=partial(self.create_fields_for_type, graphene_type), + interfaces=interfaces, resolve_type=resolve_type, ) diff --git a/graphene/types/tests/test_enum.py b/graphene/types/tests/test_enum.py index 471727c0..679de16e 100644 --- a/graphene/types/tests/test_enum.py +++ b/graphene/types/tests/test_enum.py @@ -328,6 +328,52 @@ def test_enum_resolver_compat(): assert results.data["colorByName"] == Color.RED.name +def test_enum_with_name(): + from enum import Enum as PyEnum + + class Color(PyEnum): + RED = 1 + YELLOW = 2 + BLUE = 3 + + GColor = Enum.from_enum(Color, description="original colors") + UniqueGColor = Enum.from_enum( + Color, name="UniqueColor", description="unique colors" + ) + + class Query(ObjectType): + color = GColor(required=True) + unique_color = UniqueGColor(required=True) + + schema = Schema(query=Query) + + assert ( + str(schema).strip() + == dedent( + ''' + type Query { + color: Color! + uniqueColor: UniqueColor! + } + + """original colors""" + enum Color { + RED + YELLOW + BLUE + } + + """unique colors""" + enum UniqueColor { + RED + YELLOW + BLUE + } + ''' + ).strip() + ) + + def test_enum_resolver_invalid(): from enum import Enum as PyEnum diff --git a/graphene/types/tests/test_interface.py b/graphene/types/tests/test_interface.py index c30a8a3a..3dd4fc4f 100644 --- a/graphene/types/tests/test_interface.py +++ b/graphene/types/tests/test_interface.py @@ -25,13 +25,18 @@ def test_generate_interface(): def test_generate_interface_with_meta(): + class MyFirstInterface(Interface): + pass + class MyInterface(Interface): class Meta: name = "MyOtherInterface" description = "Documentation" + interfaces = [MyFirstInterface] assert MyInterface._meta.name == "MyOtherInterface" assert MyInterface._meta.description == "Documentation" + assert MyInterface._meta.interfaces == [MyFirstInterface] def test_generate_interface_with_fields(): diff --git a/graphene/types/tests/test_type_map.py b/graphene/types/tests/test_type_map.py index f0c78e08..adddd388 100644 --- a/graphene/types/tests/test_type_map.py +++ b/graphene/types/tests/test_type_map.py @@ -289,3 +289,33 @@ def test_objecttype_with_possible_types(): assert graphql_type.is_type_of assert graphql_type.is_type_of({}, None) is True assert graphql_type.is_type_of(MyObjectType(), None) is False + + +def test_interface_with_interfaces(): + class FooInterface(Interface): + foo = String() + + class BarInterface(Interface): + class Meta: + interfaces = [FooInterface] + + foo = String() + bar = String() + + type_map = create_type_map([FooInterface, BarInterface]) + assert "FooInterface" in type_map + foo_graphql_type = type_map["FooInterface"] + assert isinstance(foo_graphql_type, GraphQLInterfaceType) + assert foo_graphql_type.name == "FooInterface" + + assert "BarInterface" in type_map + bar_graphql_type = type_map["BarInterface"] + assert isinstance(bar_graphql_type, GraphQLInterfaceType) + assert bar_graphql_type.name == "BarInterface" + + fields = bar_graphql_type.fields + assert list(fields) == ["foo", "bar"] + assert isinstance(fields["foo"], GraphQLField) + assert isinstance(fields["bar"], GraphQLField) + + assert bar_graphql_type.interfaces == [foo_graphql_type]