mirror of
				https://github.com/graphql-python/graphene.git
				synced 2025-11-04 01:47:45 +03:00 
			
		
		
		
	Manually merged in master.
This commit is contained in:
		
						commit
						3d6486a899
					
				| 
						 | 
				
			
			@ -22,7 +22,7 @@ before_install:
 | 
			
		|||
install:
 | 
			
		||||
- |
 | 
			
		||||
  if [ "$TEST_TYPE" = build ]; then
 | 
			
		||||
    pip install pytest pytest-cov pytest-benchmark coveralls six
 | 
			
		||||
    pip install pytest pytest-cov pytest-benchmark coveralls six pytz iso8601
 | 
			
		||||
    pip install -e .
 | 
			
		||||
    python setup.py develop
 | 
			
		||||
  elif [ "$TEST_TYPE" = lint ]; then
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,4 @@
 | 
			
		|||
You are in the `next` unreleased version of Graphene (`1.0.dev`).
 | 
			
		||||
Please read [UPGRADE-v1.0.md](/UPGRADE-v1.0.md) to learn how to upgrade.
 | 
			
		||||
Please read [UPGRADE-v1.0.md](/UPGRADE-v1.0.md) to learn how to upgrade to Graphene `1.0`.
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +31,7 @@ Graphene has multiple integrations with different frameworks:
 | 
			
		|||
For instaling graphene, just run this command in your shell
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
pip install "graphene>=1.0.dev"
 | 
			
		||||
pip install "graphene>=1.0"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## 1.0 Upgrade Guide
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,4 @@
 | 
			
		|||
You are in the ``next`` unreleased version of Graphene (``1.0.dev``).
 | 
			
		||||
Please read `UPGRADE-v1.0.md`_ to learn how to upgrade.
 | 
			
		||||
Please read `UPGRADE-v1.0.md`_ to learn how to upgrade to Graphene ``1.0``.
 | 
			
		||||
 | 
			
		||||
--------------
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +40,7 @@ For instaling graphene, just run this command in your shell
 | 
			
		|||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
    pip install "graphene>=1.0.dev"
 | 
			
		||||
    pip install "graphene>=1.0"
 | 
			
		||||
 | 
			
		||||
1.0 Upgrade Guide
 | 
			
		||||
-----------------
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										15
									
								
								docs/conf.py
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								docs/conf.py
									
									
									
									
									
								
							| 
						 | 
				
			
			@ -73,7 +73,7 @@ author = u'Syrus Akbary'
 | 
			
		|||
# The short X.Y version.
 | 
			
		||||
version = u'1.0'
 | 
			
		||||
# The full version, including alpha/beta/rc tags.
 | 
			
		||||
release = u'1.0.dev'
 | 
			
		||||
release = u'1.0'
 | 
			
		||||
 | 
			
		||||
# The language for content autogenerated by Sphinx. Refer to documentation
 | 
			
		||||
# for a list of supported languages.
 | 
			
		||||
| 
						 | 
				
			
			@ -133,9 +133,14 @@ todo_include_todos = True
 | 
			
		|||
# The theme to use for HTML and HTML Help pages.  See the documentation for
 | 
			
		||||
# a list of builtin themes.
 | 
			
		||||
#
 | 
			
		||||
html_theme = 'alabaster'
 | 
			
		||||
if on_rtd:
 | 
			
		||||
    html_theme = 'sphinx_rtd_theme'
 | 
			
		||||
# html_theme = 'alabaster'
 | 
			
		||||
# if on_rtd:
 | 
			
		||||
#     html_theme = 'sphinx_rtd_theme'
 | 
			
		||||
import sphinx_graphene_theme
 | 
			
		||||
 | 
			
		||||
html_theme = "sphinx_graphene_theme"
 | 
			
		||||
 | 
			
		||||
html_theme_path = [sphinx_graphene_theme.get_html_theme_path()]
 | 
			
		||||
 | 
			
		||||
# Theme options are theme-specific and customize the look and feel of a theme
 | 
			
		||||
# further.  For a list of options available for each theme, see the
 | 
			
		||||
| 
						 | 
				
			
			@ -149,7 +154,7 @@ if on_rtd:
 | 
			
		|||
# The name for this set of Sphinx documents.
 | 
			
		||||
# "<project> v<release> documentation" by default.
 | 
			
		||||
#
 | 
			
		||||
# html_title = u'Graphene v1.0.dev'
 | 
			
		||||
# html_title = u'Graphene v1.0'
 | 
			
		||||
 | 
			
		||||
# A shorter title for the navigation bar.  Default is the same as html_title.
 | 
			
		||||
#
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ Project setup
 | 
			
		|||
 | 
			
		||||
.. code:: bash
 | 
			
		||||
 | 
			
		||||
    pip install graphene>=1.0
 | 
			
		||||
    pip install "graphene>=1.0"
 | 
			
		||||
 | 
			
		||||
Creating a basic Schema
 | 
			
		||||
-----------------------
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										2
									
								
								docs/requirements.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								docs/requirements.txt
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,2 @@
 | 
			
		|||
# Docs template
 | 
			
		||||
https://github.com/graphql-python/graphene-python.org/archive/docs.zip
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +32,7 @@ plus the ones defined in ``UserFields``.
 | 
			
		|||
        pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. code:: graphql
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    type User {
 | 
			
		||||
      name: String
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,4 +10,5 @@ Types Reference
 | 
			
		|||
   interfaces
 | 
			
		||||
   abstracttypes
 | 
			
		||||
   objecttypes
 | 
			
		||||
   schema
 | 
			
		||||
   mutations
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,7 @@ An Interface contains the essential fields that will be implemented among
 | 
			
		|||
multiple ObjectTypes.
 | 
			
		||||
 | 
			
		||||
The basics:
 | 
			
		||||
 | 
			
		||||
- Each Interface is a Python class that inherits from ``graphene.Interface``.
 | 
			
		||||
- Each attribute of the Interface represents a GraphQL field.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -43,7 +44,7 @@ time.
 | 
			
		|||
 | 
			
		||||
The above types would have the following representation in a schema:
 | 
			
		||||
 | 
			
		||||
.. code:: graphql
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    interface Character {
 | 
			
		||||
      name: String
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,7 +53,7 @@ Executing the Mutation
 | 
			
		|||
 | 
			
		||||
Then, if we query (``schema.execute(query_str)``) the following:
 | 
			
		||||
 | 
			
		||||
.. code:: graphql
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    mutation myFirstMutation {
 | 
			
		||||
        createPerson(name:"Peter") {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,7 +8,7 @@ querying.
 | 
			
		|||
The basics:
 | 
			
		||||
 | 
			
		||||
- Each ObjectType is a Python class that inherits 
 | 
			
		||||
  ``graphene.ObjectType`` or inherits an implemented `Interface`_.
 | 
			
		||||
  ``graphene.ObjectType``.
 | 
			
		||||
- Each attribute of the ObjectType represents a ``Field``.
 | 
			
		||||
 | 
			
		||||
Quick example
 | 
			
		||||
| 
						 | 
				
			
			@ -36,7 +36,7 @@ Field.
 | 
			
		|||
The above ``Person`` ObjectType would have the following representation
 | 
			
		||||
in a schema:
 | 
			
		||||
 | 
			
		||||
.. code:: graphql
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    type Person {
 | 
			
		||||
      firstName: String
 | 
			
		||||
| 
						 | 
				
			
			@ -55,6 +55,11 @@ otherwise, the ``resolve_{field_name}`` within the ``ObjectType``.
 | 
			
		|||
By default a resolver will take the ``args``, ``context`` and ``info``
 | 
			
		||||
arguments.
 | 
			
		||||
 | 
			
		||||
NOTE: The class resolvers in a ``ObjectType`` are treated as ``staticmethod``s
 | 
			
		||||
always, so the first argument in the resolver: ``self`` (or ``root``) doesn't
 | 
			
		||||
need to be an actual instance of the ``ObjectType``.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Quick example
 | 
			
		||||
~~~~~~~~~~~~~
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ Scalars
 | 
			
		|||
=======
 | 
			
		||||
 | 
			
		||||
Graphene define the following base Scalar Types:
 | 
			
		||||
 | 
			
		||||
- ``graphene.String``
 | 
			
		||||
- ``graphene.Int``
 | 
			
		||||
- ``graphene.Float``
 | 
			
		||||
| 
						 | 
				
			
			@ -9,6 +10,7 @@ Graphene define the following base Scalar Types:
 | 
			
		|||
- ``graphene.ID``
 | 
			
		||||
 | 
			
		||||
Graphene also provides custom scalars for Dates and JSON:
 | 
			
		||||
 | 
			
		||||
- ``graphene.types.datetime.DateTime``
 | 
			
		||||
- ``graphene.types.json.JSONString``
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										81
									
								
								docs/types/schema.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								docs/types/schema.rst
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,81 @@
 | 
			
		|||
Schema
 | 
			
		||||
======
 | 
			
		||||
 | 
			
		||||
A Schema is created by supplying the root types of each type of operation, query and mutation (optional).
 | 
			
		||||
A schema definition is then supplied to the validator and executor.
 | 
			
		||||
 | 
			
		||||
.. code:: python
 | 
			
		||||
    my_schema = Schema(
 | 
			
		||||
        query=MyRootQuery,
 | 
			
		||||
        mutation=MyRootMutation,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
Types
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
There are some cases where the schema could not access all the types that we plan to have.
 | 
			
		||||
For example, when a field returns an ``Interface``, the schema doesn't know any of the
 | 
			
		||||
implementations.
 | 
			
		||||
 | 
			
		||||
In this case, we would need to use the ``types`` argument when creating the Schema.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. code:: python
 | 
			
		||||
 | 
			
		||||
    my_schema = Schema(
 | 
			
		||||
        query=MyRootQuery,
 | 
			
		||||
        types=[SomeExtraObjectType, ]
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Querying
 | 
			
		||||
--------
 | 
			
		||||
 | 
			
		||||
If you need to query a schema, you can directly call the ``execute`` method on it.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.. code:: python
 | 
			
		||||
    
 | 
			
		||||
    my_schema.execute('{ lastName }')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Auto CamelCase field names
 | 
			
		||||
--------------------------
 | 
			
		||||
 | 
			
		||||
By default all field and argument names (that are not 
 | 
			
		||||
explicitly set with the ``name`` arg) will be converted from
 | 
			
		||||
`snake_case` to `camelCase` (`as the API is usually being consumed by a js/mobile client`)
 | 
			
		||||
 | 
			
		||||
So, for example if we have the following ObjectType
 | 
			
		||||
 | 
			
		||||
.. code:: python
 | 
			
		||||
 | 
			
		||||
    class Person(graphene.ObjectType):
 | 
			
		||||
        last_name = graphene.String()
 | 
			
		||||
        other_name = graphene.String(name='_other_Name')
 | 
			
		||||
 | 
			
		||||
Then the ``last_name`` field name is converted to ``lastName``.
 | 
			
		||||
 | 
			
		||||
In the case we don't want to apply any transformation, we can specify
 | 
			
		||||
the field name with the ``name`` argument. So ``other_name`` field name
 | 
			
		||||
would be converted to ``_other_Name`` (without any other transformation).
 | 
			
		||||
 | 
			
		||||
So, you would need to query with:
 | 
			
		||||
 | 
			
		||||
.. code::
 | 
			
		||||
 | 
			
		||||
    {
 | 
			
		||||
        lastName
 | 
			
		||||
        _other_Name
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
If you want to disable this behavior, you set use the ``auto_camelcase`` argument
 | 
			
		||||
to ``False`` when you create the Schema.
 | 
			
		||||
 | 
			
		||||
.. code:: python
 | 
			
		||||
 | 
			
		||||
    my_schema = Schema(
 | 
			
		||||
        query=MyRootQuery,
 | 
			
		||||
        auto_camelcase=False,
 | 
			
		||||
    )
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ except NameError:
 | 
			
		|||
    __SETUP__ = False
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
VERSION = (1, 0, 0, 'alpha', 0)
 | 
			
		||||
VERSION = (1, 0, 2, 'final', 0)
 | 
			
		||||
 | 
			
		||||
__version__ = get_version(VERSION)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -95,13 +95,13 @@ class Connection(six.with_metaclass(ConnectionMeta, ObjectType)):
 | 
			
		|||
class IterableConnectionField(Field):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, type, *args, **kwargs):
 | 
			
		||||
        kwargs.setdefault('before', String())
 | 
			
		||||
        kwargs.setdefault('after', String())
 | 
			
		||||
        kwargs.setdefault('first', Int())
 | 
			
		||||
        kwargs.setdefault('last', Int())
 | 
			
		||||
        super(IterableConnectionField, self).__init__(
 | 
			
		||||
            type,
 | 
			
		||||
            *args,
 | 
			
		||||
            before=String(),
 | 
			
		||||
            after=String(),
 | 
			
		||||
            first=Int(),
 | 
			
		||||
            last=Int(),
 | 
			
		||||
            **kwargs
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -117,21 +117,25 @@ class IterableConnectionField(Field):
 | 
			
		|||
        ).format(str(self), connection_type)
 | 
			
		||||
        return connection_type
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def connection_resolver(resolver, connection, root, args, context, info):
 | 
			
		||||
        iterable = resolver(root, args, context, info)
 | 
			
		||||
        assert isinstance(iterable, Iterable), (
 | 
			
		||||
            'Resolved value from the connection field have to be iterable. '
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def connection_resolver(cls, resolver, connection, root, args, context, info):
 | 
			
		||||
        resolved = resolver(root, args, context, info)
 | 
			
		||||
 | 
			
		||||
        if isinstance(resolved, connection):
 | 
			
		||||
            return resolved
 | 
			
		||||
 | 
			
		||||
        assert isinstance(resolved, Iterable), (
 | 
			
		||||
            'Resolved value from the connection field have to be iterable or instance of {}. '
 | 
			
		||||
            'Received "{}"'
 | 
			
		||||
        ).format(iterable)
 | 
			
		||||
        ).format(connection, resolved)
 | 
			
		||||
        connection = connection_from_list(
 | 
			
		||||
            iterable,
 | 
			
		||||
            resolved,
 | 
			
		||||
            args,
 | 
			
		||||
            connection_type=connection,
 | 
			
		||||
            edge_type=connection.Edge,
 | 
			
		||||
            pageinfo_type=PageInfo
 | 
			
		||||
        )
 | 
			
		||||
        connection.iterable = iterable
 | 
			
		||||
        connection.iterable = resolved
 | 
			
		||||
        return connection
 | 
			
		||||
 | 
			
		||||
    def get_resolver(self, parent_resolver):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
 | 
			
		||||
from ...types import AbstractType, Field, List, NonNull, ObjectType, String
 | 
			
		||||
from ..connection import Connection, PageInfo
 | 
			
		||||
from ...types import AbstractType, Field, List, NonNull, ObjectType, String, Argument, Int
 | 
			
		||||
from ..connection import Connection, PageInfo, ConnectionField
 | 
			
		||||
from ..node import Node
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -109,3 +109,32 @@ def test_pageinfo():
 | 
			
		|||
    assert PageInfo._meta.name == 'PageInfo'
 | 
			
		||||
    fields = PageInfo._meta.fields
 | 
			
		||||
    assert list(fields.keys()) == ['has_next_page', 'has_previous_page', 'start_cursor', 'end_cursor']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_connectionfield():
 | 
			
		||||
    class MyObjectConnection(Connection):
 | 
			
		||||
        class Meta:
 | 
			
		||||
            node = MyObject
 | 
			
		||||
 | 
			
		||||
    field = ConnectionField(MyObjectConnection)
 | 
			
		||||
    assert field.args == {
 | 
			
		||||
        'before': Argument(String),
 | 
			
		||||
        'after': Argument(String),
 | 
			
		||||
        'first': Argument(Int),
 | 
			
		||||
        'last': Argument(Int),
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_connectionfield_custom_args():
 | 
			
		||||
    class MyObjectConnection(Connection):
 | 
			
		||||
        class Meta:
 | 
			
		||||
            node = MyObject
 | 
			
		||||
 | 
			
		||||
    field = ConnectionField(MyObjectConnection, before=String(required=True), extra=String())
 | 
			
		||||
    assert field.args == {
 | 
			
		||||
        'before': Argument(NonNull(String)),
 | 
			
		||||
        'after': Argument(String),
 | 
			
		||||
        'first': Argument(Int),
 | 
			
		||||
        'last': Argument(Int),
 | 
			
		||||
        'extra': Argument(String),
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@ from collections import OrderedDict
 | 
			
		|||
from graphql_relay.utils import base64
 | 
			
		||||
 | 
			
		||||
from ...types import ObjectType, Schema, String
 | 
			
		||||
from ..connection import ConnectionField
 | 
			
		||||
from ..connection import ConnectionField, PageInfo
 | 
			
		||||
from ..node import Node
 | 
			
		||||
 | 
			
		||||
letter_chars = ['A', 'B', 'C', 'D', 'E']
 | 
			
		||||
| 
						 | 
				
			
			@ -19,11 +19,26 @@ class Letter(ObjectType):
 | 
			
		|||
 | 
			
		||||
class Query(ObjectType):
 | 
			
		||||
    letters = ConnectionField(Letter)
 | 
			
		||||
    connection_letters = ConnectionField(Letter)
 | 
			
		||||
 | 
			
		||||
    node = Node.Field()
 | 
			
		||||
 | 
			
		||||
    def resolve_letters(self, args, context, info):
 | 
			
		||||
        return list(letters.values())
 | 
			
		||||
 | 
			
		||||
    node = Node.Field()
 | 
			
		||||
    def resolve_connection_letters(self, args, context, info):
 | 
			
		||||
        return Letter.Connection(
 | 
			
		||||
            page_info=PageInfo(
 | 
			
		||||
                has_next_page=True,
 | 
			
		||||
                has_previous_page=False
 | 
			
		||||
            ),
 | 
			
		||||
            edges=[
 | 
			
		||||
                Letter.Connection.Edge(
 | 
			
		||||
                    node=Letter(id=0, letter='A'),
 | 
			
		||||
                    cursor='a-cursor'
 | 
			
		||||
                ),
 | 
			
		||||
            ]
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
schema = Schema(Query)
 | 
			
		||||
| 
						 | 
				
			
			@ -176,3 +191,40 @@ def test_returns_all_elements_if_cursors_are_on_the_outside():
 | 
			
		|||
 | 
			
		||||
def test_returns_no_elements_if_cursors_cross():
 | 
			
		||||
    check('before: "{}" after: "{}"'.format(base64('arrayconnection:%s' % 2), base64('arrayconnection:%s' % 4)), '')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_connection_type_nodes():
 | 
			
		||||
    result = schema.execute('''
 | 
			
		||||
    {
 | 
			
		||||
        connectionLetters {
 | 
			
		||||
            edges {
 | 
			
		||||
                node {
 | 
			
		||||
                    id
 | 
			
		||||
                    letter
 | 
			
		||||
                }
 | 
			
		||||
                cursor
 | 
			
		||||
            }
 | 
			
		||||
            pageInfo {
 | 
			
		||||
                hasPreviousPage
 | 
			
		||||
                hasNextPage
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    ''')
 | 
			
		||||
 | 
			
		||||
    assert not result.errors
 | 
			
		||||
    assert result.data == {
 | 
			
		||||
        'connectionLetters': {
 | 
			
		||||
            'edges': [{
 | 
			
		||||
                'node': {
 | 
			
		||||
                    'id': 'TGV0dGVyOjA=',
 | 
			
		||||
                    'letter': 'A',
 | 
			
		||||
                },
 | 
			
		||||
                'cursor': 'a-cursor',
 | 
			
		||||
            }],
 | 
			
		||||
            'pageInfo': {
 | 
			
		||||
                'hasPreviousPage': False,
 | 
			
		||||
                'hasNextPage': True,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -84,6 +84,9 @@ def test_mutation():
 | 
			
		|||
    assert isinstance(field.args['input'], Argument)
 | 
			
		||||
    assert isinstance(field.args['input'].type, NonNull)
 | 
			
		||||
    assert field.args['input'].type.of_type == SaySomething.Input
 | 
			
		||||
    assert isinstance(fields['client_mutation_id'], Field)
 | 
			
		||||
    assert fields['client_mutation_id'].name == 'clientMutationId'
 | 
			
		||||
    assert fields['client_mutation_id'].type == String
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_mutation_input():
 | 
			
		||||
| 
						 | 
				
			
			@ -132,7 +135,7 @@ def test_node_query():
 | 
			
		|||
 | 
			
		||||
def test_edge_query():
 | 
			
		||||
    executed = schema.execute(
 | 
			
		||||
        'mutation a { other(input: {clientMutationId:"1"}) { myNodeEdge { cursor node { name }} } }'
 | 
			
		||||
        'mutation a { other(input: {clientMutationId:"1"}) { clientMutationId, myNodeEdge { cursor node { name }} } }'
 | 
			
		||||
    )
 | 
			
		||||
    assert not executed.errors
 | 
			
		||||
    assert dict(executed.data) == {'other': {'myNodeEdge': {'cursor': '1', 'node': {'name': 'name'}}}}
 | 
			
		||||
    assert dict(executed.data) == {'other': {'clientMutationId': '1', 'myNodeEdge': {'cursor': '1', 'node': {'name': 'name'}}}}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -18,6 +18,14 @@ class Argument(OrderedType):
 | 
			
		|||
        self.default_value = default_value
 | 
			
		||||
        self.description = description
 | 
			
		||||
 | 
			
		||||
    def __eq__(self, other):
 | 
			
		||||
        return isinstance(other, Argument) and (
 | 
			
		||||
            self.name == other.name,
 | 
			
		||||
            self.type == other.type,
 | 
			
		||||
            self.default_value == other.default_value,
 | 
			
		||||
            self.description == other.description
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def to_arguments(args, extra_args):
 | 
			
		||||
    from .unmountedtype import UnmountedType
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,4 +36,4 @@ class DateTime(Scalar):
 | 
			
		|||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def parse_value(value):
 | 
			
		||||
        return datetime.datetime.strptime(value, "%Y-%m-%dT%H:%M:%S.%f")
 | 
			
		||||
        return iso8601.parse_date(value)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,6 +8,9 @@ from .structures import NonNull
 | 
			
		|||
from .unmountedtype import UnmountedType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
base_type = type
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def source_resolver(source, root, args, context, info):
 | 
			
		||||
    resolved = getattr(root, source, None)
 | 
			
		||||
    if inspect.isfunction(resolved):
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +22,8 @@ class Field(OrderedType):
 | 
			
		|||
 | 
			
		||||
    def __init__(self, type, args=None, resolver=None, source=None,
 | 
			
		||||
                 deprecation_reason=None, name=None, description=None,
 | 
			
		||||
                 required=False, _creation_counter=None, **extra_args):
 | 
			
		||||
                 required=False, _creation_counter=None, default_value=None,
 | 
			
		||||
                 **extra_args):
 | 
			
		||||
        super(Field, self).__init__(_creation_counter=_creation_counter)
 | 
			
		||||
        assert not args or isinstance(args, Mapping), (
 | 
			
		||||
            'Arguments in a field have to be a mapping, received "{}".'
 | 
			
		||||
| 
						 | 
				
			
			@ -27,6 +31,9 @@ class Field(OrderedType):
 | 
			
		|||
        assert not (source and resolver), (
 | 
			
		||||
            'A Field cannot have a source and a resolver in at the same time.'
 | 
			
		||||
        )
 | 
			
		||||
        assert not callable(default_value), (
 | 
			
		||||
            'The default value can not be a function but received "{}".'
 | 
			
		||||
        ).format(base_type(default_value))
 | 
			
		||||
 | 
			
		||||
        if required:
 | 
			
		||||
            type = NonNull(type)
 | 
			
		||||
| 
						 | 
				
			
			@ -49,6 +56,7 @@ class Field(OrderedType):
 | 
			
		|||
        self.resolver = resolver
 | 
			
		||||
        self.deprecation_reason = deprecation_reason
 | 
			
		||||
        self.description = description
 | 
			
		||||
        self.default_value = default_value
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def type(self):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,7 +48,11 @@ class Interface(six.with_metaclass(InterfaceMeta)):
 | 
			
		|||
    when the field is resolved.
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
    resolve_type = None
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def resolve_type(cls, instance, context, info):
 | 
			
		||||
        from .objecttype import ObjectType
 | 
			
		||||
        if isinstance(instance, ObjectType):
 | 
			
		||||
            return type(instance)
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        raise Exception("An Interface cannot be intitialized")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ from functools import partial
 | 
			
		|||
import six
 | 
			
		||||
 | 
			
		||||
from ..utils.is_base_type import is_base_type
 | 
			
		||||
from ..utils.get_unbound_function import get_unbound_function
 | 
			
		||||
from ..utils.props import props
 | 
			
		||||
from .field import Field
 | 
			
		||||
from .objecttype import ObjectType, ObjectTypeMeta
 | 
			
		||||
| 
						 | 
				
			
			@ -22,6 +23,7 @@ class MutationMeta(ObjectTypeMeta):
 | 
			
		|||
        field_args = props(input_class) if input_class else {}
 | 
			
		||||
        resolver = getattr(cls, 'mutate', None)
 | 
			
		||||
        assert resolver, 'All mutations must define a mutate method in it'
 | 
			
		||||
        resolver = get_unbound_function(resolver)
 | 
			
		||||
        cls.Field = partial(Field, cls, args=field_args, resolver=resolver)
 | 
			
		||||
        return cls
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -63,10 +63,7 @@ class ObjectType(six.with_metaclass(ObjectTypeMeta)):
 | 
			
		|||
    have a name, but most importantly describe their fields.
 | 
			
		||||
    '''
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def is_type_of(cls, root, context, info):
 | 
			
		||||
        if isinstance(root, cls):
 | 
			
		||||
            return True
 | 
			
		||||
    is_type_of = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        # ObjectType acting as container
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,6 +27,13 @@ class List(Structure):
 | 
			
		|||
    def __str__(self):
 | 
			
		||||
        return '[{}]'.format(self.of_type)
 | 
			
		||||
 | 
			
		||||
    def __eq__(self, other):
 | 
			
		||||
        return isinstance(other, List) and (
 | 
			
		||||
            self.of_type == other.of_type and
 | 
			
		||||
            self.args == other.args and
 | 
			
		||||
            self.kwargs == other.kwargs
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class NonNull(Structure):
 | 
			
		||||
    '''
 | 
			
		||||
| 
						 | 
				
			
			@ -49,3 +56,10 @@ class NonNull(Structure):
 | 
			
		|||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return '{}!'.format(self.of_type)
 | 
			
		||||
 | 
			
		||||
    def __eq__(self, other):
 | 
			
		||||
        return isinstance(other, NonNull) and (
 | 
			
		||||
            self.of_type == other.of_type and
 | 
			
		||||
            self.args == other.args and
 | 
			
		||||
            self.kwargs == other.kwargs
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										26
									
								
								graphene/types/tests/test_argument.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								graphene/types/tests/test_argument.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,26 @@
 | 
			
		|||
import pytest
 | 
			
		||||
 | 
			
		||||
from ..argument import Argument
 | 
			
		||||
from ..structures import NonNull
 | 
			
		||||
from ..scalars import String
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_argument():
 | 
			
		||||
    arg = Argument(String, default_value='a', description='desc', name='b')
 | 
			
		||||
    assert arg.type == String
 | 
			
		||||
    assert arg.default_value == 'a'
 | 
			
		||||
    assert arg.description == 'desc'
 | 
			
		||||
    assert arg.name == 'b'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_argument_comparasion():
 | 
			
		||||
    arg1 = Argument(String, name='Hey', description='Desc', default_value='default')
 | 
			
		||||
    arg2 = Argument(String, name='Hey', description='Desc', default_value='default')
 | 
			
		||||
 | 
			
		||||
    assert arg1 == arg2
 | 
			
		||||
    assert arg1 != String()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_argument_required():
 | 
			
		||||
    arg = Argument(String, required=True)
 | 
			
		||||
    assert arg.type == NonNull(String)
 | 
			
		||||
							
								
								
									
										41
									
								
								graphene/types/tests/test_datetime.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								graphene/types/tests/test_datetime.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,41 @@
 | 
			
		|||
import datetime
 | 
			
		||||
import pytz
 | 
			
		||||
 | 
			
		||||
from ..datetime import DateTime
 | 
			
		||||
from ..objecttype import ObjectType
 | 
			
		||||
from ..schema import Schema
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Query(ObjectType):
 | 
			
		||||
    datetime = DateTime(_in=DateTime(name='in'))
 | 
			
		||||
 | 
			
		||||
    def resolve_datetime(self, args, context, info):
 | 
			
		||||
        _in = args.get('in')
 | 
			
		||||
        return _in
 | 
			
		||||
 | 
			
		||||
schema = Schema(query=Query)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_datetime_query():
 | 
			
		||||
    now = datetime.datetime.now().replace(tzinfo=pytz.utc)
 | 
			
		||||
    isoformat = now.isoformat()
 | 
			
		||||
 | 
			
		||||
    result = schema.execute('''{ datetime(in: "%s") }'''%isoformat)
 | 
			
		||||
    assert not result.errors
 | 
			
		||||
    assert result.data == {
 | 
			
		||||
        'datetime': isoformat
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_datetime_query_variable():
 | 
			
		||||
    now = datetime.datetime.now().replace(tzinfo=pytz.utc)
 | 
			
		||||
    isoformat = now.isoformat()
 | 
			
		||||
 | 
			
		||||
    result = schema.execute(
 | 
			
		||||
        '''query Test($date: DateTime){ datetime(in: $date) }''',
 | 
			
		||||
        variable_values={'date': isoformat}
 | 
			
		||||
    )
 | 
			
		||||
    assert not result.errors
 | 
			
		||||
    assert result.data == {
 | 
			
		||||
        'datetime': isoformat
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -16,19 +16,22 @@ def test_field_basic():
 | 
			
		|||
    resolver = lambda: None
 | 
			
		||||
    deprecation_reason = 'Deprecated now'
 | 
			
		||||
    description = 'My Field'
 | 
			
		||||
    my_default='something'
 | 
			
		||||
    field = Field(
 | 
			
		||||
        MyType,
 | 
			
		||||
        name='name',
 | 
			
		||||
        args=args,
 | 
			
		||||
        resolver=resolver,
 | 
			
		||||
        description=description,
 | 
			
		||||
        deprecation_reason=deprecation_reason
 | 
			
		||||
        deprecation_reason=deprecation_reason,
 | 
			
		||||
        default_value=my_default,
 | 
			
		||||
    )
 | 
			
		||||
    assert field.name == 'name'
 | 
			
		||||
    assert field.args == args
 | 
			
		||||
    assert field.resolver == resolver
 | 
			
		||||
    assert field.deprecation_reason == deprecation_reason
 | 
			
		||||
    assert field.description == description
 | 
			
		||||
    assert field.default_value == my_default
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_field_required():
 | 
			
		||||
| 
						 | 
				
			
			@ -38,6 +41,15 @@ def test_field_required():
 | 
			
		|||
    assert field.type.of_type == MyType
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_field_default_value_not_callable():
 | 
			
		||||
    MyType = object()
 | 
			
		||||
    try:
 | 
			
		||||
        Field(MyType, default_value=lambda: True)
 | 
			
		||||
    except AssertionError as e:
 | 
			
		||||
        # substring comparison for py 2/3 compatibility
 | 
			
		||||
        assert 'The default value can not be a function but received' in str(e)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_field_source():
 | 
			
		||||
    MyType = object()
 | 
			
		||||
    field = Field(MyType, source='value')
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										39
									
								
								graphene/types/tests/test_json.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								graphene/types/tests/test_json.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,39 @@
 | 
			
		|||
import json
 | 
			
		||||
 | 
			
		||||
from ..json import JSONString
 | 
			
		||||
from ..objecttype import ObjectType
 | 
			
		||||
from ..schema import Schema
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Query(ObjectType):
 | 
			
		||||
    json = JSONString(input=JSONString())
 | 
			
		||||
 | 
			
		||||
    def resolve_json(self, args, context, info):
 | 
			
		||||
        input = args.get('input')
 | 
			
		||||
        return input
 | 
			
		||||
 | 
			
		||||
schema = Schema(query=Query)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_jsonstring_query():
 | 
			
		||||
    json_value = '{"key": "value"}'
 | 
			
		||||
 | 
			
		||||
    json_value_quoted = json_value.replace('"', '\\"')
 | 
			
		||||
    result = schema.execute('''{ json(input: "%s") }'''%json_value_quoted)
 | 
			
		||||
    assert not result.errors
 | 
			
		||||
    assert result.data == {
 | 
			
		||||
        'json': json_value
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_jsonstring_query_variable():
 | 
			
		||||
    json_value = '{"key": "value"}'
 | 
			
		||||
 | 
			
		||||
    result = schema.execute(
 | 
			
		||||
        '''query Test($json: JSONString){ json(input: $json) }''',
 | 
			
		||||
        variable_values={'json': json_value}
 | 
			
		||||
    )
 | 
			
		||||
    assert not result.errors
 | 
			
		||||
    assert result.data == {
 | 
			
		||||
        'json': json_value
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -2,6 +2,8 @@ import pytest
 | 
			
		|||
 | 
			
		||||
from ..mutation import Mutation
 | 
			
		||||
from ..objecttype import ObjectType
 | 
			
		||||
from ..schema import Schema
 | 
			
		||||
from ..scalars import String
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_generate_mutation_no_args():
 | 
			
		||||
| 
						 | 
				
			
			@ -17,26 +19,6 @@ def test_generate_mutation_no_args():
 | 
			
		|||
    assert MyMutation.Field().resolver == MyMutation.mutate
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# def test_generate_mutation_with_args():
 | 
			
		||||
#     class MyMutation(Mutation):
 | 
			
		||||
#         '''Documentation'''
 | 
			
		||||
#         class Input:
 | 
			
		||||
#             s = String()
 | 
			
		||||
 | 
			
		||||
#         @classmethod
 | 
			
		||||
#         def mutate(cls, *args, **kwargs):
 | 
			
		||||
#             pass
 | 
			
		||||
 | 
			
		||||
#     graphql_type = MyMutation._meta.graphql_type
 | 
			
		||||
#     field = MyMutation.Field()
 | 
			
		||||
#     assert graphql_type.name == "MyMutation"
 | 
			
		||||
#     assert graphql_type.description == "Documentation"
 | 
			
		||||
#     assert isinstance(field, Field)
 | 
			
		||||
#     assert field.type == MyMutation._meta.graphql_type
 | 
			
		||||
#     assert 's' in field.args
 | 
			
		||||
#     assert field.args['s'].type == String
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_generate_mutation_with_meta():
 | 
			
		||||
    class MyMutation(Mutation):
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -59,3 +41,35 @@ def test_mutation_raises_exception_if_no_mutate():
 | 
			
		|||
            pass
 | 
			
		||||
 | 
			
		||||
    assert "All mutations must define a mutate method in it" == str(excinfo.value)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_mutation_execution():
 | 
			
		||||
    class CreateUser(Mutation):
 | 
			
		||||
        class Input:
 | 
			
		||||
            name = String()
 | 
			
		||||
 | 
			
		||||
        name = String()
 | 
			
		||||
 | 
			
		||||
        def mutate(self, args, context, info):
 | 
			
		||||
            name = args.get('name')
 | 
			
		||||
            return CreateUser(name=name)
 | 
			
		||||
 | 
			
		||||
    class Query(ObjectType):
 | 
			
		||||
        a = String()
 | 
			
		||||
 | 
			
		||||
    class MyMutation(ObjectType):
 | 
			
		||||
        create_user = CreateUser.Field()
 | 
			
		||||
 | 
			
		||||
    schema = Schema(query=Query, mutation=MyMutation)
 | 
			
		||||
    result = schema.execute(''' mutation mymutation {
 | 
			
		||||
        createUser(name:"Peter") {
 | 
			
		||||
            name
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    ''')
 | 
			
		||||
    assert not result.errors
 | 
			
		||||
    assert result.data == {
 | 
			
		||||
        'createUser': {
 | 
			
		||||
            'name': "Peter"
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,8 +1,9 @@
 | 
			
		|||
import json
 | 
			
		||||
from functools import partial
 | 
			
		||||
 | 
			
		||||
from graphql import Source, execute, parse
 | 
			
		||||
from graphql import Source, execute, parse, GraphQLError
 | 
			
		||||
 | 
			
		||||
from ..field import Field
 | 
			
		||||
from ..inputfield import InputField
 | 
			
		||||
from ..inputobjecttype import InputObjectType
 | 
			
		||||
from ..objecttype import ObjectType
 | 
			
		||||
| 
						 | 
				
			
			@ -22,6 +23,53 @@ def test_query():
 | 
			
		|||
    assert executed.data == {'hello': 'World'}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_query_default_value():
 | 
			
		||||
    class MyType(ObjectType):
 | 
			
		||||
        field = String()
 | 
			
		||||
 | 
			
		||||
    class Query(ObjectType):
 | 
			
		||||
        hello = Field(MyType, default_value=MyType(field='something else!'))
 | 
			
		||||
 | 
			
		||||
    hello_schema = Schema(Query)
 | 
			
		||||
 | 
			
		||||
    executed = hello_schema.execute('{ hello { field } }')
 | 
			
		||||
    assert not executed.errors
 | 
			
		||||
    assert executed.data == {'hello': {'field': 'something else!'}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_query_wrong_default_value():
 | 
			
		||||
    class MyType(ObjectType):
 | 
			
		||||
        field = String()
 | 
			
		||||
 | 
			
		||||
        @classmethod
 | 
			
		||||
        def is_type_of(cls, root, context, info):
 | 
			
		||||
            return isinstance(root, MyType)
 | 
			
		||||
 | 
			
		||||
    class Query(ObjectType):
 | 
			
		||||
        hello = Field(MyType, default_value='hello')
 | 
			
		||||
 | 
			
		||||
    hello_schema = Schema(Query)
 | 
			
		||||
 | 
			
		||||
    executed = hello_schema.execute('{ hello { field } }')
 | 
			
		||||
    assert len(executed.errors) == 1
 | 
			
		||||
    assert executed.errors[0].message == GraphQLError('Expected value of type "MyType" but got: str.').message
 | 
			
		||||
    assert executed.data == {'hello': None}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_query_default_value_ignored_by_resolver():
 | 
			
		||||
    class MyType(ObjectType):
 | 
			
		||||
        field = String()
 | 
			
		||||
 | 
			
		||||
    class Query(ObjectType):
 | 
			
		||||
        hello = Field(MyType, default_value='hello', resolver=lambda *_: MyType(field='no default.'))
 | 
			
		||||
 | 
			
		||||
    hello_schema = Schema(Query)
 | 
			
		||||
 | 
			
		||||
    executed = hello_schema.execute('{ hello { field } }')
 | 
			
		||||
    assert not executed.errors
 | 
			
		||||
    assert executed.data == {'hello': {'field': 'no default.'}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_query_resolve_function():
 | 
			
		||||
    class Query(ObjectType):
 | 
			
		||||
        hello = String()
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +157,30 @@ def test_query_middlewares():
 | 
			
		|||
    assert executed.data == {'hello': 'dlroW', 'other': 'rehto'}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_objecttype_on_instances():
 | 
			
		||||
    class Ship:
 | 
			
		||||
        def __init__(self, name):
 | 
			
		||||
            self.name = name
 | 
			
		||||
 | 
			
		||||
    class ShipType(ObjectType):
 | 
			
		||||
        name = String(description="Ship name", required=True)
 | 
			
		||||
 | 
			
		||||
        def resolve_name(self, context, args, info):
 | 
			
		||||
            # Here self will be the Ship instance returned in resolve_ship
 | 
			
		||||
            return self.name
 | 
			
		||||
 | 
			
		||||
    class Query(ObjectType):
 | 
			
		||||
        ship = Field(ShipType)
 | 
			
		||||
 | 
			
		||||
        def resolve_ship(self, context, args, info):
 | 
			
		||||
            return Ship(name='xwing')
 | 
			
		||||
 | 
			
		||||
    schema = Schema(query=Query)
 | 
			
		||||
    executed = schema.execute('{ ship { name } }')
 | 
			
		||||
    assert not executed.errors
 | 
			
		||||
    assert executed.data == {'ship': {'name': 'xwing'}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_big_list_query_benchmark(benchmark):
 | 
			
		||||
    big_list = range(10000)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										44
									
								
								graphene/types/tests/test_structures.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								graphene/types/tests/test_structures.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,44 @@
 | 
			
		|||
import pytest
 | 
			
		||||
 | 
			
		||||
from ..structures import List, NonNull
 | 
			
		||||
from ..scalars import String
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_list():
 | 
			
		||||
    _list = List(String)
 | 
			
		||||
    assert _list.of_type == String
 | 
			
		||||
    assert str(_list) == '[String]'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_nonnull():
 | 
			
		||||
    nonnull = NonNull(String)
 | 
			
		||||
    assert nonnull.of_type == String
 | 
			
		||||
    assert str(nonnull) == 'String!'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_list_comparasion():
 | 
			
		||||
    list1 = List(String)
 | 
			
		||||
    list2 = List(String)
 | 
			
		||||
    list3 = List(None)
 | 
			
		||||
 | 
			
		||||
    list1_argskwargs = List(String, None, b=True)
 | 
			
		||||
    list2_argskwargs = List(String, None, b=True)
 | 
			
		||||
 | 
			
		||||
    assert list1 == list2
 | 
			
		||||
    assert list1 != list3
 | 
			
		||||
    assert list1_argskwargs == list2_argskwargs
 | 
			
		||||
    assert list1 != list1_argskwargs
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_nonnull_comparasion():
 | 
			
		||||
    nonnull1 = NonNull(String)
 | 
			
		||||
    nonnull2 = NonNull(String)
 | 
			
		||||
    nonnull3 = NonNull(None)
 | 
			
		||||
 | 
			
		||||
    nonnull1_argskwargs = NonNull(String, None, b=True)
 | 
			
		||||
    nonnull2_argskwargs = NonNull(String, None, b=True)
 | 
			
		||||
 | 
			
		||||
    assert nonnull1 == nonnull2
 | 
			
		||||
    assert nonnull1 != nonnull3
 | 
			
		||||
    assert nonnull1_argskwargs == nonnull2_argskwargs
 | 
			
		||||
    assert nonnull1 != nonnull1_argskwargs
 | 
			
		||||
| 
						 | 
				
			
			@ -6,9 +6,11 @@ from graphql import (GraphQLArgument, GraphQLBoolean, GraphQLField,
 | 
			
		|||
                     GraphQLFloat, GraphQLID, GraphQLInputObjectField,
 | 
			
		||||
                     GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLString)
 | 
			
		||||
from graphql.type import GraphQLEnumValue
 | 
			
		||||
from graphql.execution.executor import get_default_resolve_type_fn
 | 
			
		||||
from graphql.type.typemap import GraphQLTypeMap
 | 
			
		||||
 | 
			
		||||
from ..utils.str_converters import to_camel_case
 | 
			
		||||
from ..utils.get_unbound_function import get_unbound_function
 | 
			
		||||
from .dynamic import Dynamic
 | 
			
		||||
from .enum import Enum
 | 
			
		||||
from .inputobjecttype import InputObjectType
 | 
			
		||||
| 
						 | 
				
			
			@ -26,11 +28,14 @@ def is_graphene_type(_type):
 | 
			
		|||
        return True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def resolve_type(resolve_type_func, map, root, args, info):
 | 
			
		||||
    _type = resolve_type_func(root, args, info)
 | 
			
		||||
def resolve_type(resolve_type_func, map, root, context, info):
 | 
			
		||||
    _type = resolve_type_func(root, context, info)
 | 
			
		||||
    # assert inspect.isclass(_type) and issubclass(_type, ObjectType), (
 | 
			
		||||
    #     'Received incompatible type "{}".'.format(_type)
 | 
			
		||||
    # )
 | 
			
		||||
    if not _type:
 | 
			
		||||
        return get_default_resolve_type_fn(root, context, info, info.return_type)
 | 
			
		||||
 | 
			
		||||
    if inspect.isclass(_type) and issubclass(_type, ObjectType):
 | 
			
		||||
        graphql_type = map.get(_type._meta.name)
 | 
			
		||||
        assert graphql_type and graphql_type.graphene_type == _type
 | 
			
		||||
| 
						 | 
				
			
			@ -190,8 +195,8 @@ class TypeMap(GraphQLTypeMap):
 | 
			
		|||
            return to_camel_case(name)
 | 
			
		||||
        return name
 | 
			
		||||
 | 
			
		||||
    def default_resolver(self, attname, root, *_):
 | 
			
		||||
        return getattr(root, attname, None)
 | 
			
		||||
    def default_resolver(self, attname, default_value, root, *_):
 | 
			
		||||
        return getattr(root, attname, default_value)
 | 
			
		||||
 | 
			
		||||
    def construct_fields_for_type(self, map, type, is_input_type=False):
 | 
			
		||||
        fields = OrderedDict()
 | 
			
		||||
| 
						 | 
				
			
			@ -224,7 +229,7 @@ class TypeMap(GraphQLTypeMap):
 | 
			
		|||
                _field = GraphQLField(
 | 
			
		||||
                    field_type,
 | 
			
		||||
                    args=args,
 | 
			
		||||
                    resolver=field.get_resolver(self.get_resolver_for_type(type, name)),
 | 
			
		||||
                    resolver=field.get_resolver(self.get_resolver_for_type(type, name, field.default_value)),
 | 
			
		||||
                    deprecation_reason=field.deprecation_reason,
 | 
			
		||||
                    description=field.description
 | 
			
		||||
                )
 | 
			
		||||
| 
						 | 
				
			
			@ -232,7 +237,7 @@ class TypeMap(GraphQLTypeMap):
 | 
			
		|||
            fields[field_name] = _field
 | 
			
		||||
        return fields
 | 
			
		||||
 | 
			
		||||
    def get_resolver_for_type(self, type, name):
 | 
			
		||||
    def get_resolver_for_type(self, type, name, default_value):
 | 
			
		||||
        if not issubclass(type, ObjectType):
 | 
			
		||||
            return
 | 
			
		||||
        resolver = getattr(type, 'resolve_{}'.format(name), None)
 | 
			
		||||
| 
						 | 
				
			
			@ -247,13 +252,12 @@ class TypeMap(GraphQLTypeMap):
 | 
			
		|||
                if interface_resolver:
 | 
			
		||||
                    break
 | 
			
		||||
            resolver = interface_resolver
 | 
			
		||||
 | 
			
		||||
        # Only if is not decorated with classmethod
 | 
			
		||||
        if resolver:
 | 
			
		||||
            if not getattr(resolver, '__self__', True):
 | 
			
		||||
                return resolver.__func__
 | 
			
		||||
            return resolver
 | 
			
		||||
            return get_unbound_function(resolver)
 | 
			
		||||
 | 
			
		||||
        return partial(self.default_resolver, name)
 | 
			
		||||
        return partial(self.default_resolver, name, default_value)
 | 
			
		||||
 | 
			
		||||
    def get_field_type(self, map, type):
 | 
			
		||||
        if isinstance(type, List):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										4
									
								
								graphene/utils/get_unbound_function.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								graphene/utils/get_unbound_function.py
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
def get_unbound_function(func):
 | 
			
		||||
    if not getattr(func, '__self__', True):
 | 
			
		||||
        return func.__func__
 | 
			
		||||
    return func
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user