Repository: graphql-python/graphene
Pull request #934 (2019-04-03 06:11:23)

Allow the Connection node to be wrapped in a `NonNull` type
When we declare the Connection `node` to be `NonNull` like below,
```python
class MyConnection(Connection):
    class Meta:
        node = NonNull(String)
```
we fail the assertion 
```python
assert issubclass(node, (Scalar, Enum, ObjectType, Interface, Union, NonNull))
```

However, this should be allowed according to the [Relay Connection specification](https://facebook.github.io/relay/graphql/connections.htm#sec-Node),
> An “Edge Type” must contain a field called node. This field must return either a Scalar, Enum, Object, Interface, Union, or a Non‐Null wrapper around one of those types.

Alternatively, we could define the `Edge` in the connection.
```python
class MyConnection(Connection):
    class Meta:
        node = String # We have another assertion that make this `Meta` field required!

    class Edge:
        node = NonNull(String)
```
However, from the assertion that `Meta`'s `node` field is required, `Edge`  isn't meant to be used to declare `node`.


Patch:
diff --git a/docs/execution/execute.rst b/docs/execution/execute.rst
index 1c28548..f4a1e75 100644
--- a/docs/execution/execute.rst
+++ b/docs/execution/execute.rst
@@ -48,12 +48,14 @@ You can pass variables to a query via ``variables``.
 
     schema = graphene.Schema(Query)
     result = schema.execute(
-        '''query getUser($id: ID) {
+        '''
+          query getUser($id: ID) {
             user(id: $id) {
-                id
-                firstName
-                lastName
+              id
+              firstName
+              lastName
             }
-        }''',
+          }
+        ''',
         variables={'id': 12},
     )
diff --git a/docs/execution/middleware.rst b/docs/execution/middleware.rst
index f7dac70..ad109e4 100644
--- a/docs/execution/middleware.rst
+++ b/docs/execution/middleware.rst
@@ -16,7 +16,7 @@ Resolve arguments
 
 Middlewares ``resolve`` is invoked with several arguments:
 
-- ``next`` represents the execution chain. Call ``next`` to continue evalution.
+- ``next`` represents the execution chain. Call ``next`` to continue evaluation.
 - ``root`` is the root value object passed throughout the query.
 - ``info`` is the resolver info.
 - ``args`` is the dict of arguments passed to the field.
diff --git a/docs/types/enums.rst b/docs/types/enums.rst
index 4e019ee..02cc267 100644
--- a/docs/types/enums.rst
+++ b/docs/types/enums.rst
@@ -59,7 +59,9 @@ you can add description etc. to your enum without changing the original:
 
 .. code:: python
 
-    graphene.Enum.from_enum(AlreadyExistingPyEnum, description=lambda value: return 'foo' if value == AlreadyExistingPyEnum.Foo else 'bar')
+    graphene.Enum.from_enum(
+        AlreadyExistingPyEnum,
+        description=lambda v: return 'foo' if v == AlreadyExistingPyEnum.Foo else 'bar')
 
 
 Notes
diff --git a/graphene/relay/connection.py b/graphene/relay/connection.py
index 927dfcc..047f2b4 100644
--- a/graphene/relay/connection.py
+++ b/graphene/relay/connection.py
@@ -53,7 +53,7 @@ class Connection(ObjectType):
     def __init_subclass_with_meta__(cls, node=None, name=None, **options):
         _meta = ConnectionOptions(cls)
         assert node, "You have to provide a node in {}.Meta".format(cls.__name__)
-        assert issubclass(
+        assert isinstance(node, NonNull) or issubclass(
             node, (Scalar, Enum, ObjectType, Interface, Union, NonNull)
         ), ('Received incompatible node "{}" for Connection {}.').format(
             node, cls.__name__
diff --git a/graphene/types/uuid.py b/graphene/types/uuid.py
index abb8f11..b9687e1 100644
--- a/graphene/types/uuid.py
+++ b/graphene/types/uuid.py
@@ -1,5 +1,5 @@
 from __future__ import absolute_import
-
+import six
 from uuid import UUID as _UUID
 
 from graphql.language import ast
@@ -12,8 +12,9 @@ class UUID(Scalar):
 
     @staticmethod
     def serialize(uuid):
-        if isinstance(uuid, str):
+        if isinstance(uuid, six.string_types):
             uuid = _UUID(uuid)
+
         assert isinstance(uuid, _UUID), "Expected UUID instance, received {}".format(
             uuid
         )


Test patch:
diff --git a/graphene/relay/tests/test_connection.py b/graphene/relay/tests/test_connection.py
index ca43f39..6686f96 100644
--- a/graphene/relay/tests/test_connection.py
+++ b/graphene/relay/tests/test_connection.py
@@ -108,6 +108,17 @@ def test_edge_with_bases():
     assert edge_fields["other"].type == String
 
 
+def test_edge_with_nonnull_node():
+    class MyObjectConnection(Connection):
+        class Meta:
+            node = NonNull(MyObject)
+
+    edge_fields = MyObjectConnection.Edge._meta.fields
+    assert isinstance(edge_fields["node"], Field)
+    assert isinstance(edge_fields["node"].type, NonNull)
+    assert edge_fields["node"].type.of_type == MyObject
+
+
 def test_pageinfo():
     assert PageInfo._meta.name == "PageInfo"
     fields = PageInfo._meta.fields