From ae93499a37bdd9f7099b3c645ae36b7ed658f205 Mon Sep 17 00:00:00 2001 From: Alec Rosenbaum Date: Fri, 15 Jan 2021 13:01:43 -0500 Subject: [PATCH 1/7] add failing test for interface meta --- graphene/types/tests/test_interface.py | 5 +++++ 1 file changed, 5 insertions(+) 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(): From 86b7e6ac86a8a3095ef0bb4164c84cf727f4d9b8 Mon Sep 17 00:00:00 2001 From: Alec Rosenbaum Date: Fri, 15 Jan 2021 13:02:08 -0500 Subject: [PATCH 2/7] update InterfaceOptions to fix failing test --- graphene/types/interface.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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 From a17f63cf039939242167dbff46b6b9f24ac04802 Mon Sep 17 00:00:00 2001 From: Alec Rosenbaum Date: Fri, 15 Jan 2021 13:10:14 -0500 Subject: [PATCH 3/7] add failing type_map test, bar_graphql_type has no interfaces --- graphene/types/tests/test_type_map.py | 30 +++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/graphene/types/tests/test_type_map.py b/graphene/types/tests/test_type_map.py index 334eb241..cc1992ac 100644 --- a/graphene/types/tests/test_type_map.py +++ b/graphene/types/tests/test_type_map.py @@ -270,3 +270,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] From 7004515f06024227f3e6068150fc5d275b0c4392 Mon Sep 17 00:00:00 2001 From: Alec Rosenbaum Date: Fri, 15 Jan 2021 13:13:05 -0500 Subject: [PATCH 4/7] implement interface interfaces on TypeMap, fix failing test --- graphene/types/schema.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/graphene/types/schema.py b/graphene/types/schema.py index 4fd71769..8f27c9b9 100644 --- a/graphene/types/schema.py +++ b/graphene/types/schema.py @@ -236,11 +236,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, ) From beb957382d0ab9025d095756655edaa3356bf207 Mon Sep 17 00:00:00 2001 From: Rahul Jha Date: Thu, 13 Jan 2022 15:33:09 +0530 Subject: [PATCH 5/7] Highlight .get in backticks When I first read through the documentation twice, it took me two tries and looking very hard to find out the difference b/w the two. The background highlight using backticks would be helpful in this case. --- docs/types/enums.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 8f6a8f9c4ace5fc2184040e6a56e877a6d8acae6 Mon Sep 17 00:00:00 2001 From: Thomas Leonard Date: Fri, 24 Jun 2022 18:00:55 +0200 Subject: [PATCH 6/7] feat: add ability to provide a type name to enum when using from_enum --- graphene/types/enum.py | 7 +++-- graphene/types/tests/test_enum.py | 46 +++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) 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/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 From 8589aaeb98f0b15ea4c312ef8b6d3382c65c70b4 Mon Sep 17 00:00:00 2001 From: Tim Gates Date: Sat, 16 Jul 2022 14:40:00 +1000 Subject: [PATCH 7/7] docs: Fix a few typos There are small typos in: - UPGRADE-v1.0.md - UPGRADE-v2.0.md - docs/execution/fileuploading.rst Fixes: - Should read `standard` rather than `stantard`. - Should read `library` rather than `libary`. - Should read `explicitly` rather than `explicity`. Signed-off-by: Tim Gates --- UPGRADE-v1.0.md | 2 +- UPGRADE-v2.0.md | 2 +- docs/execution/fileuploading.rst | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) 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 `_.