Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-graphene for openSUSE:Factory 
checked in at 2023-12-17 21:33:10
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-graphene (Old)
 and      /work/SRC/openSUSE:Factory/.python-graphene.new.25432 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-graphene"

Sun Dec 17 21:33:10 2023 rev:11 rq:1133682 version:3.3.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-graphene/python-graphene.changes  
2023-07-10 16:40:48.450867136 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-graphene.new.25432/python-graphene.changes   
    2023-12-17 21:35:20.284153316 +0100
@@ -1,0 +2,19 @@
+Sun Dec 17 01:58:11 UTC 2023 - Dirk Müller <dmuel...@suse.com>
+
+- update to 3.3.0:
+  * Default value for InputObjectTypes
+  * Default enum description to "An enumeration."
+  * Allow the user to change InputObjectType's default value on
+    non-specified inputs to a sentinel value
+  * 881: Corrected enum metaclass to fix pickle.dumps()
+  * chore: Use `typing.TYPE_CHECKING` instead of MYPY
+  * test: print schema with InputObjectType with DateTime field
+    with default_value (#1293)
+  * docs: add get_human function
+  * CI: drop python 3.6
+  * types: add option for strict connection types
+- update to 3.2.2:
+  * This release provides some internal refactoring to the relay
+    types to improve support for adding custom fields to them.
+
+-------------------------------------------------------------------

Old:
----
  graphene-3.2.1.tar.gz

New:
----
  graphene-3.3.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ python-graphene.spec ++++++
--- /var/tmp/diff_new_pack.05ARxi/_old  2023-12-17 21:35:20.708168723 +0100
+++ /var/tmp/diff_new_pack.05ARxi/_new  2023-12-17 21:35:20.708168723 +0100
@@ -18,7 +18,7 @@
 
 %{?sle15_python_module_pythons}
 Name:           python-graphene
-Version:        3.2.1
+Version:        3.3.0
 Release:        0
 Summary:        GraphQL Framework for Python
 License:        MIT

++++++ graphene-3.2.1.tar.gz -> graphene-3.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/.github/workflows/tests.yml 
new/graphene-3.3.0/.github/workflows/tests.yml
--- old/graphene-3.2.1/.github/workflows/tests.yml      2022-12-11 
21:05:25.000000000 +0100
+++ new/graphene-3.3.0/.github/workflows/tests.yml      2023-07-26 
08:26:30.000000000 +0200
@@ -25,12 +25,11 @@
       fail-fast: false
       matrix:
         include:
-          - {name: '3.11', python: '3.11-dev', os: ubuntu-latest, tox: py311}
+          - {name: '3.11', python: '3.11', os: ubuntu-latest, tox: py311}
           - {name: '3.10', python: '3.10', os: ubuntu-latest, tox: py310}
           - {name: '3.9', python: '3.9', os: ubuntu-latest, tox: py39}
           - {name: '3.8', python: '3.8', os: ubuntu-latest, tox: py38}
           - {name: '3.7', python: '3.7', os: ubuntu-latest, tox: py37}
-          - {name: '3.6', python: '3.6', os: ubuntu-20.04, tox: py36}
     steps:
       - uses: actions/checkout@v3
       - uses: actions/setup-python@v4
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/README.md new/graphene-3.3.0/README.md
--- old/graphene-3.2.1/README.md        2022-12-11 21:05:25.000000000 +0100
+++ new/graphene-3.3.0/README.md        2023-07-26 08:26:30.000000000 +0200
@@ -1,6 +1,6 @@
-# ![Graphene Logo](http://graphene-python.org/favicon.png) 
[Graphene](http://graphene-python.org) [![Build 
Status](https://travis-ci.org/graphql-python/graphene.svg?branch=master)](https://travis-ci.org/graphql-python/graphene)
 [![PyPI 
version](https://badge.fury.io/py/graphene.svg)](https://badge.fury.io/py/graphene)
 [![Coverage 
Status](https://coveralls.io/repos/graphql-python/graphene/badge.svg?branch=master&service=github)](https://coveralls.io/github/graphql-python/graphene?branch=master)
+# ![Graphene Logo](http://graphene-python.org/favicon.png) 
[Graphene](http://graphene-python.org)  [![PyPI 
version](https://badge.fury.io/py/graphene.svg)](https://badge.fury.io/py/graphene)
 [![Coverage 
Status](https://coveralls.io/repos/graphql-python/graphene/badge.svg?branch=master&service=github)](https://coveralls.io/github/graphql-python/graphene?branch=master)
 
[![](https://dcbadge.vercel.app/api/server/T6Gp6NFYHe?style=flat)](https://discord.gg/T6Gp6NFYHe)
 
-[💬 Join the community on 
Slack](https://join.slack.com/t/graphenetools/shared_invite/enQtOTE2MDQ1NTg4MDM1LTA4Nzk0MGU0NGEwNzUxZGNjNDQ4ZjAwNDJjMjY0OGE1ZDgxZTg4YjM2ZTc4MjE2ZTAzZjE2ZThhZTQzZTkyMmM)
+[💬 Join the community on Discord](https://discord.gg/T6Gp6NFYHe)
 
 **We are looking for contributors**! Please check the current issues to see 
how you can help ❤️
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/README.rst 
new/graphene-3.3.0/README.rst
--- old/graphene-3.2.1/README.rst       2022-12-11 21:05:25.000000000 +0100
+++ new/graphene-3.3.0/README.rst       2023-07-26 08:26:30.000000000 +0200
@@ -36,9 +36,6 @@
 | SQLAlchemy        | `graphene-sqlalchemy <https://git               |
 |                   | hub.com/graphql-python/graphene-sqlalchemy/>`__ |
 +-------------------+-------------------------------------------------+
-| Google App Engine | `graphene-gae <http                             |
-|                   | s://github.com/graphql-python/graphene-gae/>`__ |
-+-------------------+-------------------------------------------------+
 
 Also, Graphene is fully compatible with the GraphQL spec, working
 seamlessly with all GraphQL clients, such as
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/docs/api/index.rst 
new/graphene-3.3.0/docs/api/index.rst
--- old/graphene-3.2.1/docs/api/index.rst       2022-12-11 21:05:25.000000000 
+0100
+++ new/graphene-3.3.0/docs/api/index.rst       2023-07-26 08:26:30.000000000 
+0200
@@ -92,7 +92,7 @@
 
 .. autoclass:: graphene.Context
 
-.. autoclass:: graphql.execution.base.ExecutionResult
+.. autoclass:: graphql.ExecutionResult
 
 .. Relay
 .. -----
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/docs/conf.py 
new/graphene-3.3.0/docs/conf.py
--- old/graphene-3.2.1/docs/conf.py     2022-12-11 21:05:25.000000000 +0100
+++ new/graphene-3.3.0/docs/conf.py     2023-07-26 08:26:30.000000000 +0200
@@ -82,7 +82,7 @@
 #
 # This is also used if you do content translation via gettext catalogs.
 # Usually you set "language" from the command line for these cases.
-language = None
+# language = None
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
@@ -456,5 +456,4 @@
         "http://docs.graphene-python.org/projects/sqlalchemy/en/latest/";,
         None,
     ),
-    "graphene_gae": 
("http://docs.graphene-python.org/projects/gae/en/latest/";, None),
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/docs/execution/dataloader.rst 
new/graphene-3.3.0/docs/execution/dataloader.rst
--- old/graphene-3.2.1/docs/execution/dataloader.rst    2022-12-11 
21:05:25.000000000 +0100
+++ new/graphene-3.3.0/docs/execution/dataloader.rst    2023-07-26 
08:26:30.000000000 +0200
@@ -36,10 +36,10 @@
     user_loader = UserLoader()
 
     user1 = await user_loader.load(1)
-    user1_best_friend = await user_loader.load(user1.best_friend_id))
+    user1_best_friend = await user_loader.load(user1.best_friend_id)
 
     user2 = await user_loader.load(2)
-    user2_best_friend = await user_loader.load(user2.best_friend_id))
+    user2_best_friend = await user_loader.load(user2.best_friend_id)
 
 
 A naive application may have issued *four* round-trips to a backend for the
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/docs/execution/queryvalidation.rst 
new/graphene-3.3.0/docs/execution/queryvalidation.rst
--- old/graphene-3.2.1/docs/execution/queryvalidation.rst       2022-12-11 
21:05:25.000000000 +0100
+++ new/graphene-3.3.0/docs/execution/queryvalidation.rst       2023-07-26 
08:26:30.000000000 +0200
@@ -1,5 +1,5 @@
 Query Validation
-==========
+================
 GraphQL uses query validators to check if Query AST is valid and can be 
executed. Every GraphQL server implements
 standard query validators. For example, there is an validator that tests if 
queried field exists on queried type, that
 makes query fail with "Cannot query field on type" error if it doesn't.
@@ -8,7 +8,7 @@
 
 
 Depth limit Validator
------------------
+---------------------
 The depth limit validator helps to prevent execution of malicious
 queries. It takes in the following arguments.
 
@@ -17,7 +17,7 @@
 - ``callback`` Called each time validation runs. Receives an Object which is a 
map of the depths for each operation.
 
 Usage
--------
+-----
 
 Here is how you would implement depth-limiting on your schema.
 
@@ -54,7 +54,7 @@
 This is a useful security measure in production environments.
 
 Usage
--------
+-----
 
 Here is how you would disable introspection for your schema.
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/docs/index.rst 
new/graphene-3.3.0/docs/index.rst
--- old/graphene-3.2.1/docs/index.rst   2022-12-11 21:05:25.000000000 +0100
+++ new/graphene-3.3.0/docs/index.rst   2023-07-26 08:26:30.000000000 +0200
@@ -1,12 +1,6 @@
 Graphene
 ========
 
-------------
-
-The documentation below is for the ``dev`` (prerelease) version of Graphene. 
To view the documentation for the latest stable Graphene version go to the `v2 
docs <https://docs.graphene-python.org/en/stable/>`_.
-
-------------
-
 Contents:
 
 .. toctree::
@@ -27,7 +21,6 @@
 * `Graphene-Django 
<http://docs.graphene-python.org/projects/django/en/latest/>`_ (`source 
<https://github.com/graphql-python/graphene-django/>`_)
 * Flask-Graphql (`source <https://github.com/graphql-python/flask-graphql>`_)
 * `Graphene-SQLAlchemy 
<http://docs.graphene-python.org/projects/sqlalchemy/en/latest/>`_ (`source 
<https://github.com/graphql-python/graphene-sqlalchemy/>`_)
-* `Graphene-GAE <http://docs.graphene-python.org/projects/gae/en/latest/>`_ 
(`source <https://github.com/graphql-python/graphene-gae/>`_)
 * `Graphene-Mongo <http://graphene-mongo.readthedocs.io/en/latest/>`_ (`source 
<https://github.com/graphql-python/graphene-mongo>`_)
 * `Starlette <https://www.starlette.io/graphql/>`_ (`source 
<https://github.com/encode/starlette>`_)
 * `FastAPI <https://fastapi.tiangolo.com/advanced/graphql/>`_ (`source 
<https://github.com/tiangolo/fastapi>`_)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/docs/requirements.txt 
new/graphene-3.3.0/docs/requirements.txt
--- old/graphene-3.2.1/docs/requirements.txt    2022-12-11 21:05:25.000000000 
+0100
+++ new/graphene-3.3.0/docs/requirements.txt    2023-07-26 08:26:30.000000000 
+0200
@@ -1,5 +1,5 @@
 # Required library
-Sphinx==1.5.3
-sphinx-autobuild==0.7.1
+Sphinx==6.1.3
+sphinx-autobuild==2021.3.14
 # Docs template
 http://graphene-python.org/sphinx_graphene_theme.zip
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/docs/types/objecttypes.rst 
new/graphene-3.3.0/docs/types/objecttypes.rst
--- old/graphene-3.2.1/docs/types/objecttypes.rst       2022-12-11 
21:05:25.000000000 +0100
+++ new/graphene-3.3.0/docs/types/objecttypes.rst       2023-07-26 
08:26:30.000000000 +0200
@@ -80,6 +80,10 @@
 
     from graphene import ObjectType, String, Field
 
+    def get_human(name):
+        first_name, last_name = name.split()
+        return Person(first_name, last_name)
+
     class Person(ObjectType):
         full_name = String()
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/docs/types/scalars.rst 
new/graphene-3.3.0/docs/types/scalars.rst
--- old/graphene-3.2.1/docs/types/scalars.rst   2022-12-11 21:05:25.000000000 
+0100
+++ new/graphene-3.3.0/docs/types/scalars.rst   2023-07-26 08:26:30.000000000 
+0200
@@ -271,7 +271,7 @@
 
         @staticmethod
         def parse_literal(node, _variables=None):
-            if isinstance(node, ast.StringValue):
+            if isinstance(node, ast.StringValueNode):
                 return datetime.datetime.strptime(
                     node.value, "%Y-%m-%dT%H:%M:%S.%f")
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/graphene/__init__.py 
new/graphene-3.3.0/graphene/__init__.py
--- old/graphene-3.2.1/graphene/__init__.py     2022-12-11 21:05:25.000000000 
+0100
+++ new/graphene-3.3.0/graphene/__init__.py     2023-07-26 08:26:30.000000000 
+0200
@@ -46,7 +46,7 @@
 from .utils.module_loading import lazy_import
 from .utils.resolve_only_args import resolve_only_args
 
-VERSION = (3, 2, 1, "final", 0)
+VERSION = (3, 3, 0, "final", 0)
 
 
 __version__ = get_version(VERSION)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/graphene/relay/connection.py 
new/graphene-3.3.0/graphene/relay/connection.py
--- old/graphene-3.2.1/graphene/relay/connection.py     2022-12-11 
21:05:25.000000000 +0100
+++ new/graphene-3.3.0/graphene/relay/connection.py     2023-07-26 
08:26:30.000000000 +0200
@@ -1,6 +1,7 @@
 import re
 from collections.abc import Iterable
 from functools import partial
+from typing import Type
 
 from graphql_relay import connection_from_array
 
@@ -8,7 +9,34 @@
 from ..types.field import Field
 from ..types.objecttype import ObjectType, ObjectTypeOptions
 from ..utils.thenables import maybe_thenable
-from .node import is_node
+from .node import is_node, AbstractNode
+
+
+def get_edge_class(
+    connection_class: Type["Connection"],
+    _node: Type[AbstractNode],
+    base_name: str,
+    strict_types: bool = False,
+):
+    edge_class = getattr(connection_class, "Edge", None)
+
+    class EdgeBase:
+        node = Field(
+            NonNull(_node) if strict_types else _node,
+            description="The item at the end of the edge",
+        )
+        cursor = String(required=True, description="A cursor for use in 
pagination")
+
+    class EdgeMeta:
+        description = f"A Relay edge containing a `{base_name}` and its 
cursor."
+
+    edge_name = f"{base_name}Edge"
+
+    edge_bases = [edge_class, EdgeBase] if edge_class else [EdgeBase]
+    if not isinstance(edge_class, ObjectType):
+        edge_bases = [*edge_bases, ObjectType]
+
+    return type(edge_name, tuple(edge_bases), {"Meta": EdgeMeta})
 
 
 class PageInfo(ObjectType):
@@ -61,8 +89,11 @@
         abstract = True
 
     @classmethod
-    def __init_subclass_with_meta__(cls, node=None, name=None, **options):
-        _meta = ConnectionOptions(cls)
+    def __init_subclass_with_meta__(
+        cls, node=None, name=None, strict_types=False, _meta=None, **options
+    ):
+        if not _meta:
+            _meta = ConnectionOptions(cls)
         assert node, f"You have to provide a node in {cls.__name__}.Meta"
         assert isinstance(node, NonNull) or issubclass(
             node, (Scalar, Enum, ObjectType, Interface, Union, NonNull)
@@ -72,39 +103,29 @@
         if not name:
             name = f"{base_name}Connection"
 
-        edge_class = getattr(cls, "Edge", None)
-        _node = node
+        options["name"] = name
 
-        class EdgeBase:
-            node = Field(_node, description="The item at the end of the edge")
-            cursor = String(required=True, description="A cursor for use in 
pagination")
-
-        class EdgeMeta:
-            description = f"A Relay edge containing a `{base_name}` and its 
cursor."
-
-        edge_name = f"{base_name}Edge"
-        if edge_class:
-            edge_bases = (edge_class, EdgeBase, ObjectType)
-        else:
-            edge_bases = (EdgeBase, ObjectType)
+        _meta.node = node
 
-        edge = type(edge_name, edge_bases, {"Meta": EdgeMeta})
-        cls.Edge = edge
+        if not _meta.fields:
+            _meta.fields = {}
 
-        options["name"] = name
-        _meta.node = node
-        _meta.fields = {
-            "page_info": Field(
+        if "page_info" not in _meta.fields:
+            _meta.fields["page_info"] = Field(
                 PageInfo,
                 name="pageInfo",
                 required=True,
                 description="Pagination data for this connection.",
-            ),
-            "edges": Field(
-                NonNull(List(edge)),
+            )
+
+        if "edges" not in _meta.fields:
+            edge_class = get_edge_class(cls, node, base_name, strict_types)  # 
type: ignore
+            cls.Edge = edge_class
+            _meta.fields["edges"] = Field(
+                NonNull(List(NonNull(edge_class) if strict_types else 
edge_class)),
                 description="Contains the nodes in this connection.",
-            ),
-        }
+            )
+
         return super(Connection, cls).__init_subclass_with_meta__(
             _meta=_meta, **options
         )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/graphene-3.2.1/graphene/relay/tests/test_connection.py 
new/graphene-3.3.0/graphene/relay/tests/test_connection.py
--- old/graphene-3.2.1/graphene/relay/tests/test_connection.py  2022-12-11 
21:05:25.000000000 +0100
+++ new/graphene-3.3.0/graphene/relay/tests/test_connection.py  2023-07-26 
08:26:30.000000000 +0200
@@ -1,7 +1,15 @@
+import re
+
 from pytest import raises
 
 from ...types import Argument, Field, Int, List, NonNull, ObjectType, Schema, 
String
-from ..connection import Connection, ConnectionField, PageInfo
+from ..connection import (
+    Connection,
+    ConnectionField,
+    PageInfo,
+    ConnectionOptions,
+    get_edge_class,
+)
 from ..node import Node
 
 
@@ -51,6 +59,111 @@
     assert list(fields) == ["page_info", "edges", "extra"]
 
 
+def test_connection_extra_abstract_fields():
+    class ConnectionWithNodes(Connection):
+        class Meta:
+            abstract = True
+
+        @classmethod
+        def __init_subclass_with_meta__(cls, node=None, name=None, **options):
+            _meta = ConnectionOptions(cls)
+
+            _meta.fields = {
+                "nodes": Field(
+                    NonNull(List(node)),
+                    description="Contains all the nodes in this connection.",
+                ),
+            }
+
+            return super(ConnectionWithNodes, cls).__init_subclass_with_meta__(
+                node=node, name=name, _meta=_meta, **options
+            )
+
+    class MyObjectConnection(ConnectionWithNodes):
+        class Meta:
+            node = MyObject
+
+        class Edge:
+            other = String()
+
+    assert MyObjectConnection._meta.name == "MyObjectConnection"
+    fields = MyObjectConnection._meta.fields
+    assert list(fields) == ["nodes", "page_info", "edges"]
+    edge_field = fields["edges"]
+    pageinfo_field = fields["page_info"]
+    nodes_field = fields["nodes"]
+
+    assert isinstance(edge_field, Field)
+    assert isinstance(edge_field.type, NonNull)
+    assert isinstance(edge_field.type.of_type, List)
+    assert edge_field.type.of_type.of_type == MyObjectConnection.Edge
+
+    assert isinstance(pageinfo_field, Field)
+    assert isinstance(pageinfo_field.type, NonNull)
+    assert pageinfo_field.type.of_type == PageInfo
+
+    assert isinstance(nodes_field, Field)
+    assert isinstance(nodes_field.type, NonNull)
+    assert isinstance(nodes_field.type.of_type, List)
+    assert nodes_field.type.of_type.of_type == MyObject
+
+
+def test_connection_override_fields():
+    class ConnectionWithNodes(Connection):
+        class Meta:
+            abstract = True
+
+        @classmethod
+        def __init_subclass_with_meta__(cls, node=None, name=None, **options):
+            _meta = ConnectionOptions(cls)
+            base_name = (
+                re.sub("Connection$", "", name or cls.__name__) or 
node._meta.name
+            )
+
+            edge_class = get_edge_class(cls, node, base_name)
+
+            _meta.fields = {
+                "page_info": Field(
+                    NonNull(
+                        PageInfo,
+                        name="pageInfo",
+                        required=True,
+                        description="Pagination data for this connection.",
+                    )
+                ),
+                "edges": Field(
+                    NonNull(List(NonNull(edge_class))),
+                    description="Contains the nodes in this connection.",
+                ),
+            }
+
+            return super(ConnectionWithNodes, cls).__init_subclass_with_meta__(
+                node=node, name=name, _meta=_meta, **options
+            )
+
+    class MyObjectConnection(ConnectionWithNodes):
+        class Meta:
+            node = MyObject
+
+    assert MyObjectConnection._meta.name == "MyObjectConnection"
+    fields = MyObjectConnection._meta.fields
+    assert list(fields) == ["page_info", "edges"]
+    edge_field = fields["edges"]
+    pageinfo_field = fields["page_info"]
+
+    assert isinstance(edge_field, Field)
+    assert isinstance(edge_field.type, NonNull)
+    assert isinstance(edge_field.type.of_type, List)
+    assert isinstance(edge_field.type.of_type.of_type, NonNull)
+
+    assert edge_field.type.of_type.of_type.of_type.__name__ == "MyObjectEdge"
+
+    # This page info is NonNull
+    assert isinstance(pageinfo_field, Field)
+    assert isinstance(edge_field.type, NonNull)
+    assert pageinfo_field.type.of_type == PageInfo
+
+
 def test_connection_name():
     custom_name = "MyObjectCustomNameConnection"
 
@@ -186,3 +299,20 @@
     executed = schema.execute("{ testConnection { edges { cursor } } }")
     assert not executed.errors
     assert executed.data == {"testConnection": {"edges": []}}
+
+
+def test_connectionfield_strict_types():
+    class MyObjectConnection(Connection):
+        class Meta:
+            node = MyObject
+            strict_types = True
+
+    connection_field = ConnectionField(MyObjectConnection)
+    edges_field_type = connection_field.type._meta.fields["edges"].type
+    assert isinstance(edges_field_type, NonNull)
+
+    edges_list_element_type = edges_field_type.of_type.of_type
+    assert isinstance(edges_list_element_type, NonNull)
+
+    node_field = edges_list_element_type.of_type._meta.fields["node"]
+    assert isinstance(node_field.type, NonNull)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/graphene/tests/issues/test_1293.py 
new/graphene-3.3.0/graphene/tests/issues/test_1293.py
--- old/graphene-3.2.1/graphene/tests/issues/test_1293.py       1970-01-01 
01:00:00.000000000 +0100
+++ new/graphene-3.3.0/graphene/tests/issues/test_1293.py       2023-07-26 
08:26:30.000000000 +0200
@@ -0,0 +1,41 @@
+# https://github.com/graphql-python/graphene/issues/1293
+
+import datetime
+
+import graphene
+from graphql.utilities import print_schema
+
+
+class Filters(graphene.InputObjectType):
+    datetime_after = graphene.DateTime(
+        required=False,
+        default_value=datetime.datetime.utcfromtimestamp(1434549820776 / 1000),
+    )
+    datetime_before = graphene.DateTime(
+        required=False,
+        default_value=datetime.datetime.utcfromtimestamp(1444549820776 / 1000),
+    )
+
+
+class SetDatetime(graphene.Mutation):
+    class Arguments:
+        filters = Filters(required=True)
+
+    ok = graphene.Boolean()
+
+    def mutate(root, info, filters):
+        return SetDatetime(ok=True)
+
+
+class Query(graphene.ObjectType):
+    goodbye = graphene.String()
+
+
+class Mutations(graphene.ObjectType):
+    set_datetime = SetDatetime.Field()
+
+
+def test_schema_printable_with_default_datetime_value():
+    schema = graphene.Schema(query=Query, mutation=Mutations)
+    schema_str = print_schema(schema.graphql_schema)
+    assert schema_str, "empty schema printed"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/graphene/tests/issues/test_881.py 
new/graphene-3.3.0/graphene/tests/issues/test_881.py
--- old/graphene-3.2.1/graphene/tests/issues/test_881.py        1970-01-01 
01:00:00.000000000 +0100
+++ new/graphene-3.3.0/graphene/tests/issues/test_881.py        2023-07-26 
08:26:30.000000000 +0200
@@ -0,0 +1,27 @@
+import pickle
+
+from ...types.enum import Enum
+
+
+class PickleEnum(Enum):
+    # is defined outside of test because pickle unable to dump class inside ot 
pytest function
+    A = "a"
+    B = 1
+
+
+def test_enums_pickling():
+    a = PickleEnum.A
+    pickled = pickle.dumps(a)
+    restored = pickle.loads(pickled)
+    assert type(a) is type(restored)
+    assert a == restored
+    assert a.value == restored.value
+    assert a.name == restored.name
+
+    b = PickleEnum.B
+    pickled = pickle.dumps(b)
+    restored = pickle.loads(pickled)
+    assert type(a) is type(restored)
+    assert b == restored
+    assert b.value == restored.value
+    assert b.name == restored.name
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/graphene/types/enum.py 
new/graphene-3.3.0/graphene/types/enum.py
--- old/graphene-3.2.1/graphene/types/enum.py   2022-12-11 21:05:25.000000000 
+0100
+++ new/graphene-3.3.0/graphene/types/enum.py   2023-07-26 08:26:30.000000000 
+0200
@@ -31,9 +31,11 @@
         # with the enum values.
         enum_members.pop("Meta", None)
         enum = PyEnum(cls.__name__, enum_members)
-        return SubclassWithMeta_Meta.__new__(
+        obj = SubclassWithMeta_Meta.__new__(
             cls, name_, bases, dict(classdict, __enum__=enum), **options
         )
+        globals()[name_] = obj.__enum__
+        return obj
 
     def get(cls, value):
         return cls._meta.enum(value)
@@ -63,7 +65,7 @@
         cls, enum, name=None, description=None, deprecation_reason=None
     ):  # noqa: N805
         name = name or enum.__name__
-        description = description or enum.__doc__
+        description = description or enum.__doc__ or "An enumeration."
         meta_dict = {
             "enum": enum,
             "description": description,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/graphene/types/inputobjecttype.py 
new/graphene-3.3.0/graphene/types/inputobjecttype.py
--- old/graphene-3.2.1/graphene/types/inputobjecttype.py        2022-12-11 
21:05:25.000000000 +0100
+++ new/graphene-3.3.0/graphene/types/inputobjecttype.py        2023-07-26 
08:26:30.000000000 +0200
@@ -1,11 +1,12 @@
+from typing import TYPE_CHECKING
+
 from .base import BaseOptions, BaseType
 from .inputfield import InputField
 from .unmountedtype import UnmountedType
 from .utils import yank_fields_from_attrs
 
-# For static type checking with Mypy
-MYPY = False
-if MYPY:
+# For static type checking with type checker
+if TYPE_CHECKING:
     from typing import Dict, Callable  # NOQA
 
 
@@ -14,6 +15,31 @@
     container = None  # type: InputObjectTypeContainer
 
 
+# Currently in Graphene, we get a `None` whenever we access an (optional) 
field that was not set in an InputObjectType
+# using the InputObjectType.<attribute> dot access syntax. This is ambiguous, 
because in this current (Graphene
+# historical) arrangement, we cannot distinguish between a field not being set 
and a field being set to None.
+# At the same time, we shouldn't break existing code that expects a `None` 
when accessing a field that was not set.
+_INPUT_OBJECT_TYPE_DEFAULT_VALUE = None
+
+# To mitigate this, we provide the function 
`set_input_object_type_default_value` to allow users to change the default
+# value returned in non-specified fields in InputObjectType to another 
meaningful sentinel value (e.g. Undefined)
+# if they want to. This way, we can keep code that expects a `None` working 
while we figure out a better solution (or
+# a well-documented breaking change) for this issue.
+
+
+def set_input_object_type_default_value(default_value):
+    """
+    Change the sentinel value returned by non-specified fields in an 
InputObjectType
+    Useful to differentiate between a field not being set and a field being 
set to None by using a sentinel value
+    (e.g. Undefined is a good sentinel value for this purpose)
+
+    This function should be called at the beginning of the app or in some 
other place where it is guaranteed to
+    be called before any InputObjectType is defined.
+    """
+    global _INPUT_OBJECT_TYPE_DEFAULT_VALUE
+    _INPUT_OBJECT_TYPE_DEFAULT_VALUE = default_value
+
+
 class InputObjectTypeContainer(dict, BaseType):  # type: ignore
     class Meta:
         abstract = True
@@ -21,7 +47,7 @@
     def __init__(self, *args, **kwargs):
         dict.__init__(self, *args, **kwargs)
         for key in self._meta.fields:
-            setattr(self, key, self.get(key, None))
+            setattr(self, key, self.get(key, _INPUT_OBJECT_TYPE_DEFAULT_VALUE))
 
     def __init_subclass__(cls, *args, **kwargs):
         pass
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/graphene/types/interface.py 
new/graphene-3.3.0/graphene/types/interface.py
--- old/graphene-3.2.1/graphene/types/interface.py      2022-12-11 
21:05:25.000000000 +0100
+++ new/graphene-3.3.0/graphene/types/interface.py      2023-07-26 
08:26:30.000000000 +0200
@@ -1,10 +1,11 @@
+from typing import TYPE_CHECKING
+
 from .base import BaseOptions, BaseType
 from .field import Field
 from .utils import yank_fields_from_attrs
 
-# For static type checking with Mypy
-MYPY = False
-if MYPY:
+# For static type checking with type checker
+if TYPE_CHECKING:
     from typing import Dict, Iterable, Type  # NOQA
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/graphene/types/mutation.py 
new/graphene-3.3.0/graphene/types/mutation.py
--- old/graphene-3.2.1/graphene/types/mutation.py       2022-12-11 
21:05:25.000000000 +0100
+++ new/graphene-3.3.0/graphene/types/mutation.py       2023-07-26 
08:26:30.000000000 +0200
@@ -1,3 +1,5 @@
+from typing import TYPE_CHECKING
+
 from ..utils.deprecated import warn_deprecation
 from ..utils.get_unbound_function import get_unbound_function
 from ..utils.props import props
@@ -6,9 +8,8 @@
 from .utils import yank_fields_from_attrs
 from .interface import Interface
 
-# For static type checking with Mypy
-MYPY = False
-if MYPY:
+# For static type checking with type checker
+if TYPE_CHECKING:
     from .argument import Argument  # NOQA
     from typing import Dict, Type, Callable, Iterable  # NOQA
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/graphene/types/objecttype.py 
new/graphene-3.3.0/graphene/types/objecttype.py
--- old/graphene-3.2.1/graphene/types/objecttype.py     2022-12-11 
21:05:25.000000000 +0100
+++ new/graphene-3.3.0/graphene/types/objecttype.py     2023-07-26 
08:26:30.000000000 +0200
@@ -1,3 +1,5 @@
+from typing import TYPE_CHECKING
+
 from .base import BaseOptions, BaseType, BaseTypeMeta
 from .field import Field
 from .interface import Interface
@@ -7,9 +9,8 @@
     from dataclasses import make_dataclass, field
 except ImportError:
     from ..pyutils.dataclasses import make_dataclass, field  # type: ignore
-# For static type checking with Mypy
-MYPY = False
-if MYPY:
+# For static type checking with type checker
+if TYPE_CHECKING:
     from typing import Dict, Iterable, Type  # NOQA
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/graphene/types/tests/conftest.py 
new/graphene-3.3.0/graphene/types/tests/conftest.py
--- old/graphene-3.2.1/graphene/types/tests/conftest.py 1970-01-01 
01:00:00.000000000 +0100
+++ new/graphene-3.3.0/graphene/types/tests/conftest.py 2023-07-26 
08:26:30.000000000 +0200
@@ -0,0 +1,12 @@
+import pytest
+from graphql import Undefined
+
+from graphene.types.inputobjecttype import set_input_object_type_default_value
+
+
+@pytest.fixture()
+def set_default_input_object_type_to_undefined():
+    """This fixture is used to change the default value of optional inputs in 
InputObjectTypes for specific tests"""
+    set_input_object_type_default_value(Undefined)
+    yield
+    set_input_object_type_default_value(None)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/graphene/types/tests/test_enum.py 
new/graphene-3.3.0/graphene/types/tests/test_enum.py
--- old/graphene-3.2.1/graphene/types/tests/test_enum.py        2022-12-11 
21:05:25.000000000 +0100
+++ new/graphene-3.3.0/graphene/types/tests/test_enum.py        2023-07-26 
08:26:30.000000000 +0200
@@ -65,6 +65,21 @@
     assert RGB.BLUE
 
 
+def test_enum_custom_description_in_constructor():
+    description = "An enumeration, but with a custom description"
+    RGB = Enum(
+        "RGB",
+        "RED,GREEN,BLUE",
+        description=description,
+    )
+    assert RGB._meta.description == description
+
+
+def test_enum_from_python3_enum_uses_default_builtin_doc():
+    RGB = Enum("RGB", "RED,GREEN,BLUE")
+    assert RGB._meta.description == "An enumeration."
+
+
 def test_enum_from_builtin_enum_accepts_lambda_description():
     def custom_description(value):
         if not value:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/graphene-3.2.1/graphene/types/tests/test_inputobjecttype.py 
new/graphene-3.3.0/graphene/types/tests/test_inputobjecttype.py
--- old/graphene-3.2.1/graphene/types/tests/test_inputobjecttype.py     
2022-12-11 21:05:25.000000000 +0100
+++ new/graphene-3.3.0/graphene/types/tests/test_inputobjecttype.py     
2023-07-26 08:26:30.000000000 +0200
@@ -1,3 +1,5 @@
+from graphql import Undefined
+
 from ..argument import Argument
 from ..field import Field
 from ..inputfield import InputField
@@ -6,6 +8,7 @@
 from ..scalars import Boolean, String
 from ..schema import Schema
 from ..unmountedtype import UnmountedType
+from ... import NonNull
 
 
 class MyType:
@@ -136,3 +139,31 @@
 
     assert not result.errors
     assert result.data == {"isChild": True}
+
+
+def test_inputobjecttype_default_input_as_undefined(
+    set_default_input_object_type_to_undefined,
+):
+    class TestUndefinedInput(InputObjectType):
+        required_field = String(required=True)
+        optional_field = String()
+
+    class Query(ObjectType):
+        undefined_optionals_work = Field(NonNull(Boolean), 
input=TestUndefinedInput())
+
+        def resolve_undefined_optionals_work(self, info, input: 
TestUndefinedInput):
+            # Confirm that optional_field comes as Undefined
+            return (
+                input.required_field == "required" and input.optional_field is 
Undefined
+            )
+
+    schema = Schema(query=Query)
+    result = schema.execute(
+        """query basequery {
+        undefinedOptionalsWork(input: {requiredField: "required"})
+    }
+    """
+    )
+
+    assert not result.errors
+    assert result.data == {"undefinedOptionalsWork": True}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/graphene/types/tests/test_type_map.py 
new/graphene-3.3.0/graphene/types/tests/test_type_map.py
--- old/graphene-3.2.1/graphene/types/tests/test_type_map.py    2022-12-11 
21:05:25.000000000 +0100
+++ new/graphene-3.3.0/graphene/types/tests/test_type_map.py    2023-07-26 
08:26:30.000000000 +0200
@@ -20,8 +20,8 @@
 from ..interface import Interface
 from ..objecttype import ObjectType
 from ..scalars import Int, String
-from ..structures import List, NonNull
 from ..schema import Schema
+from ..structures import List, NonNull
 
 
 def create_type_map(types, auto_camelcase=True):
@@ -227,6 +227,18 @@
     assert foo_field.description == "Field description"
 
 
+def test_inputobject_undefined(set_default_input_object_type_to_undefined):
+    class OtherObjectType(InputObjectType):
+        optional_field = String()
+
+    type_map = create_type_map([OtherObjectType])
+    assert "OtherObjectType" in type_map
+    graphql_type = type_map["OtherObjectType"]
+
+    container = graphql_type.out_type({})
+    assert container.optional_field is Undefined
+
+
 def test_objecttype_camelcase():
     class MyObjectType(ObjectType):
         """Description"""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/graphene/types/union.py 
new/graphene-3.3.0/graphene/types/union.py
--- old/graphene-3.2.1/graphene/types/union.py  2022-12-11 21:05:25.000000000 
+0100
+++ new/graphene-3.3.0/graphene/types/union.py  2023-07-26 08:26:30.000000000 
+0200
@@ -1,9 +1,10 @@
+from typing import TYPE_CHECKING
+
 from .base import BaseOptions, BaseType
 from .unmountedtype import UnmountedType
 
-# For static type checking with Mypy
-MYPY = False
-if MYPY:
+# For static type checking with type checker
+if TYPE_CHECKING:
     from .objecttype import ObjectType  # NOQA
     from typing import Iterable, Type  # NOQA
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/graphene/validation/depth_limit.py 
new/graphene-3.3.0/graphene/validation/depth_limit.py
--- old/graphene-3.2.1/graphene/validation/depth_limit.py       2022-12-11 
21:05:25.000000000 +0100
+++ new/graphene-3.3.0/graphene/validation/depth_limit.py       2023-07-26 
08:26:30.000000000 +0200
@@ -30,7 +30,7 @@
 except ImportError:
     # backwards compatibility for v3.6
     from typing import Pattern
-from typing import Callable, Dict, List, Optional, Union
+from typing import Callable, Dict, List, Optional, Union, Tuple
 
 from graphql import GraphQLError
 from graphql.validation import ValidationContext, ValidationRule
@@ -82,7 +82,7 @@
 
 
 def get_fragments(
-    definitions: List[DefinitionNode],
+    definitions: Tuple[DefinitionNode, ...],
 ) -> Dict[str, FragmentDefinitionNode]:
     fragments = {}
     for definition in definitions:
@@ -94,7 +94,7 @@
 # This will actually get both queries and mutations.
 # We can basically treat those the same
 def get_queries_and_mutations(
-    definitions: List[DefinitionNode],
+    definitions: Tuple[DefinitionNode, ...],
 ) -> Dict[str, OperationDefinitionNode]:
     operations = {}
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/graphene-3.2.1/tox.ini new/graphene-3.3.0/tox.ini
--- old/graphene-3.2.1/tox.ini  2022-12-11 21:05:25.000000000 +0100
+++ new/graphene-3.3.0/tox.ini  2023-07-26 08:26:30.000000000 +0200
@@ -1,5 +1,5 @@
 [tox]
-envlist = py3{6,7,8,9,10}, mypy, pre-commit
+envlist = py3{7,8,9,10,11}, mypy, pre-commit
 skipsdist = true
 
 [testenv]
@@ -8,7 +8,7 @@
 setenv =
      PYTHONPATH = .:{envdir}
 commands =
-    py{36,37,38,39,310}: pytest --cov=graphene graphene --cov-report=term 
--cov-report=xml examples {posargs}
+    py{37,38,39,310,311}: pytest --cov=graphene graphene --cov-report=term 
--cov-report=xml examples {posargs}
 
 [testenv:pre-commit]
 basepython = python3.10

Reply via email to