From 3aafe58d4dcbbf5b26db5da6087a151b113b059d Mon Sep 17 00:00:00 2001 From: as Date: Wed, 28 Nov 2018 17:58:19 +0100 Subject: [PATCH 01/10] Special characters in README.rst break setup.py on Windows --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index d9f62bec..4b742900 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,5 @@ import ast +import codecs import re import sys @@ -61,7 +62,7 @@ setup( name="graphene", version=version, description="GraphQL Framework for Python", - long_description=open("README.rst").read(), + long_description=codecs.open("README.rst", "r", "utf-8").read(), url="https://github.com/graphql-python/graphene", author="Syrus Akbary", author_email="me@syrusakbary.com", From 2c43a2ae0a04fc61ecf68e9b6f7df8c6fbaae886 Mon Sep 17 00:00:00 2001 From: as Date: Wed, 28 Nov 2018 17:58:19 +0100 Subject: [PATCH 02/10] Now setup.py works on Windows other tools have trouble with PKG-INFO. Forcing long_description to ASCII. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 4b742900..f8c776c7 100644 --- a/setup.py +++ b/setup.py @@ -62,7 +62,7 @@ setup( name="graphene", version=version, description="GraphQL Framework for Python", - long_description=codecs.open("README.rst", "r", "utf-8").read(), + long_description=codecs.open("README.rst", "r", encoding="ascii", errors="replace").read(), url="https://github.com/graphql-python/graphene", author="Syrus Akbary", author_email="me@syrusakbary.com", From 2bc7699a98a0fcdb0cfe626d7007980c6804f5be Mon Sep 17 00:00:00 2001 From: as Date: Tue, 11 Dec 2018 14:58:20 +0100 Subject: [PATCH 03/10] black formatting and removed trailing whitespace --- docs/quickstart.rst | 2 +- setup.py | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 8c0055c6..fc333fae 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -30,7 +30,7 @@ server with an associated set of resolve methods that know how to fetch data. We are going to create a very simple schema, with a ``Query`` with only -one field: ``hello`` and an input name. And when we query it, it should return ``"Hello +one field: ``hello`` and an input name. And when we query it, it should return ``"Hello {argument}"``. .. code:: python diff --git a/setup.py b/setup.py index f8c776c7..48e5be35 100644 --- a/setup.py +++ b/setup.py @@ -62,7 +62,9 @@ setup( name="graphene", version=version, description="GraphQL Framework for Python", - long_description=codecs.open("README.rst", "r", encoding="ascii", errors="replace").read(), + long_description=codecs.open( + "README.rst", "r", encoding="ascii", errors="replace" + ).read(), url="https://github.com/graphql-python/graphene", author="Syrus Akbary", author_email="me@syrusakbary.com", From 349add570025e1e53d8a9d4b6ba0071e90e8093f Mon Sep 17 00:00:00 2001 From: Jonathan Kim Date: Wed, 26 Dec 2018 20:22:34 +0000 Subject: [PATCH 04/10] Update documentation on ObjectTypes Resolves #798 --- docs/types/objecttypes.rst | 155 +++++++++++++++++++++++++++++++++---- 1 file changed, 141 insertions(+), 14 deletions(-) diff --git a/docs/types/objecttypes.rst b/docs/types/objecttypes.rst index ca0d6b3f..b6eb3087 100644 --- a/docs/types/objecttypes.rst +++ b/docs/types/objecttypes.rst @@ -25,8 +25,8 @@ This example model defines a Person, with a first and a last name: last_name = graphene.String() full_name = graphene.String() - def resolve_full_name(self, info): - return '{} {}'.format(self.first_name, self.last_name) + def resolve_full_name(root, info): + return '{} {}'.format(root.first_name, root.last_name) **first\_name** and **last\_name** are fields of the ObjectType. Each field is specified as a class attribute, and each attribute maps to a @@ -56,23 +56,148 @@ NOTE: The resolvers on an ``ObjectType`` are always treated as ``staticmethod``\ so the first argument to the resolver method ``self`` (or ``root``) need not be an actual instance of the ``ObjectType``. +If an explicit resolver is not defined on the ``ObjectType`` then Graphene will +attempt to use a property with the same name on the object that is passed to the +``ObjectType``. -Quick example -~~~~~~~~~~~~~ +.. code:: python -This example model defines a ``Query`` type, which has a reverse field -that reverses the given ``word`` argument using the ``resolve_reverse`` -method in the class. + import graphene + + class Person(graphene.ObjectType): + first_name = graphene.String() + last_name = graphene.String() + + class Query(graphene.ObjectType): + me = graphene.Field(Person) + + def resolve_me(_, info): + # returns an object that represents a Person + return get_human(name='Luke Skywalker') + +If you are passing a dict instead of an object to your ``ObjectType`` you can +change the default resolver in the ``Meta`` class like this: + +.. code:: python + + import graphene + from graphene.types.resolver import dict_resolver + + class Person(graphene.ObjectType): + class Meta: + default_resolver = dict_resolver + + first_name = graphene.String() + last_name = graphene.String() + + class Query(graphene.ObjectType): + me = graphene.Field(Person) + + def resolve_me(_, info): + return { + "first_name": "Luke", + "last_name": "Skywalker", + } + +Or you can change the default resolver globally by calling ``set_default_resolver`` +before executing a query. + +.. code:: python + + import graphene + from graphene.types.resolver import dict_resolver, set_default_resolver + + set_default_resolver(dict_resolver) + + schema = graphene.Schema(query=Query) + result = schema.execute(''' + query { + me { + firstName + } + } + ''') + + +Resolvers with arguments +~~~~~~~~~~~~~~~~~~~~~~~~ + +Any arguments that a field defines gets passed to the resolver function as +kwargs. For example: .. code:: python import graphene class Query(graphene.ObjectType): - reverse = graphene.String(word=graphene.String()) + human_by_name = graphene.Field(Human, name=graphene.String(required=True)) + + def resolve_human_by_name(_, info, name): + return get_human(name=name) + +You can then execute the following query: + +.. code:: + + query { + humanByName(name: "Luke Skywalker") { + firstName + lastName + } + } + +NOTE: if you define an argument for a field that is not required (and in a query +execution it is not provided as an argument) it will not be passed to the +resolver function at all. This is so that the developer can differenciate +between a ``undefined`` value for an argument and an explicit ``null`` value. + +For example, given this schema: + +.. code:: python + + import graphene + + class Query(graphene.ObjectType): + hello = graphene.String(required=True, name=graphene.String()) + + def resolve_hello(_, info, name): + return name if name else 'World' + +And this query: + +.. code:: + + query { + hello + } + +An error will be thrown: + +.. code:: + + TypeError: resolve_hello() missing 1 required positional argument: 'name' + +You can fix this error in 2 ways. Either by combining all keyword arguments +into a dict: + +.. code:: python + + class Query(graphene.ObjectType): + hello = graphene.String(required=True, name=graphene.String()) + + def resolve_hello(_, info, **args): + return args.get('name', 'World') + +Or by setting a default value for the keyword argument: + +.. code:: python + + class Query(graphene.ObjectType): + hello = graphene.String(required=True, name=graphene.String()) + + def resolve_hello(_, info, name='World'): + return name - def resolve_reverse(self, info, word): - return word[::-1] Resolvers outside the class ~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -83,11 +208,13 @@ A field can use a custom resolver from outside the class: import graphene - def reverse(root, info, word): - return word[::-1] + def resolve_full_name(person, info): + return '{} {}'.format(person.first_name, person.last_name) - class Query(graphene.ObjectType): - reverse = graphene.String(word=graphene.String(), resolver=reverse) + class Person(graphene.ObjectType): + first_name = graphene.String() + last_name = graphene.String() + full_name = graphene.String(resolver=resolve_full_name) Instances as data containers From e1a2eb5a3541bbfdde80d84418010bac29b981d2 Mon Sep 17 00:00:00 2001 From: Jonathan Kim Date: Wed, 26 Dec 2018 20:32:22 +0000 Subject: [PATCH 05/10] Remove trailing whitespace --- docs/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 8c0055c6..fc333fae 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -30,7 +30,7 @@ server with an associated set of resolve methods that know how to fetch data. We are going to create a very simple schema, with a ``Query`` with only -one field: ``hello`` and an input name. And when we query it, it should return ``"Hello +one field: ``hello`` and an input name. And when we query it, it should return ``"Hello {argument}"``. .. code:: python From d1b1ad733f588961323e6172f9db2c32fff8dc60 Mon Sep 17 00:00:00 2001 From: Jonathan Kim Date: Thu, 27 Dec 2018 09:39:18 +0000 Subject: [PATCH 06/10] Ignore flake8 error --- graphene/utils/str_converters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphene/utils/str_converters.py b/graphene/utils/str_converters.py index d8804038..2d455d5d 100644 --- a/graphene/utils/str_converters.py +++ b/graphene/utils/str_converters.py @@ -18,4 +18,4 @@ def to_snake_case(name): def to_const(string): - return re.sub("[\W|^]+", "_", string).upper() + return re.sub("[\W|^]+", "_", string).upper() # noqa From ff4fb4f86affaa1bbac17eec9911fa959f63447a Mon Sep 17 00:00:00 2001 From: kiendang Date: Wed, 5 Dec 2018 01:56:25 +0800 Subject: [PATCH 07/10] Fix anchor link --- UPGRADE-v2.0.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/UPGRADE-v2.0.md b/UPGRADE-v2.0.md index 32b28d8b..c2048bd1 100644 --- a/UPGRADE-v2.0.md +++ b/UPGRADE-v2.0.md @@ -17,7 +17,7 @@ developer has to write to use them. **New Features!** * [`InputObjectType`](#inputobjecttype) -* [`Meta as Class arguments`](#meta-ass-class-arguments) (_only available for Python 3_) +* [`Meta as Class arguments`](#meta-as-class-arguments) (_only available for Python 3_) > The type metaclasses are now deleted as they are no longer necessary. If your code was depending From 806b99f59d7db8b1f5a0f502ef7b218a5dd2b129 Mon Sep 17 00:00:00 2001 From: Eran Kampf Date: Thu, 7 Mar 2019 18:29:40 -0800 Subject: [PATCH 08/10] Initial CODEOWNERS file as decided in our community meeting --- CODEOWNERS | 1 + 1 file changed, 1 insertion(+) create mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000..6d08e193 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1 @@ +/ @syrusakbary @ekampf @dan98765 From fd0bd0ccd795f1e1297102a3cbfb87ab56d3f904 Mon Sep 17 00:00:00 2001 From: Eran Kampf <205185+ekampf@users.noreply.github.com> Date: Fri, 8 Mar 2019 09:35:48 -0800 Subject: [PATCH 09/10] Adding Myn to CODEOWNERS (projectcheshire) --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 6d08e193..26bb1aff 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -/ @syrusakbary @ekampf @dan98765 +/ @syrusakbary @ekampf @dan98765 @projectcheshire From 9ae2359b873f648d306a1516f2fe31d650e832c9 Mon Sep 17 00:00:00 2001 From: Dan <3498629+dan98765@users.noreply.github.com> Date: Fri, 8 Mar 2019 11:09:45 -0800 Subject: [PATCH 10/10] Run pre-commit autoupdate to bump versions of precommit hooks, then run them on all files (#913) --- .pre-commit-config.yaml | 16 ++++++++++------ UPGRADE-v2.0.md | 4 ++-- graphene/pyutils/signature.py | 5 ++++- graphene/types/argument.py | 4 +--- graphene/types/base.py | 4 ++++ graphene/types/scalars.py | 3 +++ graphene/types/tests/test_definition.py | 2 -- graphene/types/tests/test_inputobjecttype.py | 1 - graphene/types/tests/test_json.py | 1 - graphene/types/tests/test_mountedtype.py | 1 - graphene/types/tests/test_resolver.py | 1 - graphene/types/tests/test_scalar.py | 1 - graphene/utils/str_converters.py | 2 +- 13 files changed, 25 insertions(+), 20 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a71f6fe0..93ab2e6d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,24 +1,28 @@ repos: - repo: git://github.com/pre-commit/pre-commit-hooks - rev: v1.3.0 + rev: v2.1.0 hooks: + - id: check-merge-conflict - id: check-json - id: check-yaml - id: debug-statements - id: end-of-file-fixer exclude: ^docs/.*$ - - id: trailing-whitespace - exclude: README.md - id: pretty-format-json args: - --autofix - - id: flake8 + - id: trailing-whitespace + exclude: README.md - repo: https://github.com/asottile/pyupgrade - rev: v1.4.0 + rev: v1.12.0 hooks: - id: pyupgrade - repo: https://github.com/ambv/black - rev: 18.6b4 + rev: 18.9b0 hooks: - id: black language_version: python3 +- repo: https://github.com/PyCQA/flake8 + rev: 3.7.7 + hooks: + - id: flake8 diff --git a/UPGRADE-v2.0.md b/UPGRADE-v2.0.md index c2048bd1..d9d48005 100644 --- a/UPGRADE-v2.0.md +++ b/UPGRADE-v2.0.md @@ -276,7 +276,7 @@ If you are using Middelwares, you need to some adjustments: Before: ```python -class MyGrapheneMiddleware(object): +class MyGrapheneMiddleware(object): def resolve(self, next_mw, root, args, context, info): ## Middleware code @@ -287,7 +287,7 @@ class MyGrapheneMiddleware(object): With 2.0: ```python -class MyGrapheneMiddleware(object): +class MyGrapheneMiddleware(object): def resolve(self, next_mw, root, info, **args): context = info.context diff --git a/graphene/pyutils/signature.py b/graphene/pyutils/signature.py index c66c2563..7757d9d0 100644 --- a/graphene/pyutils/signature.py +++ b/graphene/pyutils/signature.py @@ -707,7 +707,10 @@ class Signature(object): break elif param.name in kwargs: if param.kind == _POSITIONAL_ONLY: - msg = "{arg!r} parameter is positional only, " "but was passed as a keyword" + msg = ( + "{arg!r} parameter is positional only, " + "but was passed as a keyword" + ) msg = msg.format(arg=param.name) raise TypeError(msg) parameters_ex = (param,) diff --git a/graphene/types/argument.py b/graphene/types/argument.py index 9c75bcee..bf304608 100644 --- a/graphene/types/argument.py +++ b/graphene/types/argument.py @@ -75,9 +75,7 @@ def to_arguments(args, extra_args=None): arg_name = default_name or arg.name assert ( arg_name not in arguments - ), 'More than one Argument have same name "{}".'.format( - arg_name - ) + ), 'More than one Argument have same name "{}".'.format(arg_name) arguments[arg_name] = arg return arguments diff --git a/graphene/types/base.py b/graphene/types/base.py index aa97ed22..75685d98 100644 --- a/graphene/types/base.py +++ b/graphene/types/base.py @@ -1,5 +1,9 @@ from ..utils.subclass_with_meta import SubclassWithMeta from ..utils.trim_docstring import trim_docstring +import six + +if six.PY3: + from typing import Type class BaseOptions(object): diff --git a/graphene/types/scalars.py b/graphene/types/scalars.py index dfb63e52..c5f43787 100644 --- a/graphene/types/scalars.py +++ b/graphene/types/scalars.py @@ -4,6 +4,9 @@ from graphql.language.ast import BooleanValue, FloatValue, IntValue, StringValue from .base import BaseOptions, BaseType from .unmountedtype import UnmountedType +if six.PY3: + from typing import Any + class ScalarOptions(BaseOptions): pass diff --git a/graphene/types/tests/test_definition.py b/graphene/types/tests/test_definition.py index 347de9c9..549847d5 100644 --- a/graphene/types/tests/test_definition.py +++ b/graphene/types/tests/test_definition.py @@ -1,5 +1,3 @@ - - from ..argument import Argument from ..enum import Enum from ..field import Field diff --git a/graphene/types/tests/test_inputobjecttype.py b/graphene/types/tests/test_inputobjecttype.py index d565ff40..dc557b94 100644 --- a/graphene/types/tests/test_inputobjecttype.py +++ b/graphene/types/tests/test_inputobjecttype.py @@ -1,4 +1,3 @@ - from ..argument import Argument from ..field import Field from ..inputfield import InputField diff --git a/graphene/types/tests/test_json.py b/graphene/types/tests/test_json.py index 6b8189c4..b5537180 100644 --- a/graphene/types/tests/test_json.py +++ b/graphene/types/tests/test_json.py @@ -1,4 +1,3 @@ - from ..json import JSONString from ..objecttype import ObjectType from ..schema import Schema diff --git a/graphene/types/tests/test_mountedtype.py b/graphene/types/tests/test_mountedtype.py index 787bee56..b964233e 100644 --- a/graphene/types/tests/test_mountedtype.py +++ b/graphene/types/tests/test_mountedtype.py @@ -1,4 +1,3 @@ - from ..field import Field from ..scalars import String diff --git a/graphene/types/tests/test_resolver.py b/graphene/types/tests/test_resolver.py index 3be9a492..2a15028d 100644 --- a/graphene/types/tests/test_resolver.py +++ b/graphene/types/tests/test_resolver.py @@ -1,4 +1,3 @@ - from ..resolver import ( attr_resolver, dict_resolver, diff --git a/graphene/types/tests/test_scalar.py b/graphene/types/tests/test_scalar.py index 1ec986cd..559c0ce6 100644 --- a/graphene/types/tests/test_scalar.py +++ b/graphene/types/tests/test_scalar.py @@ -1,4 +1,3 @@ - from ..scalars import Scalar diff --git a/graphene/utils/str_converters.py b/graphene/utils/str_converters.py index 2d455d5d..216b0547 100644 --- a/graphene/utils/str_converters.py +++ b/graphene/utils/str_converters.py @@ -18,4 +18,4 @@ def to_snake_case(name): def to_const(string): - return re.sub("[\W|^]+", "_", string).upper() # noqa + return re.sub(r"[\W|^]+", "_", string).upper() # noqa